aboutsummaryrefslogtreecommitdiffstats
path: root/src/features/appearance/index.ts
diff options
context:
space:
mode:
authorLibravatar Markus Hatvan <markus_hatvan@aon.at>2021-09-18 11:15:25 +0200
committerLibravatar GitHub <noreply@github.com>2021-09-18 11:15:25 +0200
commitd4101a48b3eee8b1fb177831aa02a4b4fbec2588 (patch)
treec92f2fbe91197fde8589207463d0d6526b4ff76b /src/features/appearance/index.ts
parent5.6.3-nightly.6 [skip ci] (diff)
downloadferdium-app-d4101a48b3eee8b1fb177831aa02a4b4fbec2588.tar.gz
ferdium-app-d4101a48b3eee8b1fb177831aa02a4b4fbec2588.tar.zst
ferdium-app-d4101a48b3eee8b1fb177831aa02a4b4fbec2588.zip
chore: convert various files from JS to TS (#1959)
Diffstat (limited to 'src/features/appearance/index.ts')
-rw-r--r--src/features/appearance/index.ts250
1 files changed, 250 insertions, 0 deletions
diff --git a/src/features/appearance/index.ts b/src/features/appearance/index.ts
new file mode 100644
index 000000000..b00b9df3d
--- /dev/null
+++ b/src/features/appearance/index.ts
@@ -0,0 +1,250 @@
1import color from 'color';
2import { reaction } from 'mobx';
3import { iconSizeBias } from '../../config';
4import { DEFAULT_APP_SETTINGS } from '../../environment';
5
6const STYLE_ELEMENT_ID = 'custom-appearance-style';
7
8function createStyleElement() {
9 const styles = document.createElement('style');
10 styles.id = STYLE_ELEMENT_ID;
11
12 document.querySelector('head')?.appendChild(styles);
13}
14
15function setAppearance(style) {
16 const styleElement = document.querySelector(`#${STYLE_ELEMENT_ID}`);
17
18 if (styleElement) {
19 styleElement.innerHTML = style;
20 }
21}
22
23// See https://github.com/Qix-/color/issues/53#issuecomment-656590710
24function darkenAbsolute(originalColor, absoluteChange) {
25 const originalLightness = originalColor.lightness();
26 return originalColor.lightness(originalLightness - absoluteChange);
27}
28
29function generateAccentStyle(accentColorStr) {
30 let style = '';
31
32 let accentColor = color(DEFAULT_APP_SETTINGS.accentColor);
33 try {
34 accentColor = color(accentColorStr);
35 } catch {
36 // Ignore invalid accent color.
37 }
38 const darkerColorStr = darkenAbsolute(accentColor, 5).hex();
39 style += `
40 a.button:hover, button.button:hover {
41 background: ${darkenAbsolute(accentColor, 10).hex()};
42 }
43
44 .franz-form__button:hover,
45 .franz-form__button.franz-form__button--inverted:hover,
46 .settings .settings__close:hover,
47 .theme__dark .franz-form__button:hover,
48 .theme__dark .franz-form__button.franz-form__button--inverted:hover,
49 .theme__dark .settings .settings__close:hover {
50 background: ${darkerColorStr};
51 }
52
53 .franz-form__button:active,
54 .theme__dark .franz-form__button:active {
55 background: ${darkerColorStr};
56 }
57
58 .theme__dark .franz-form__button.franz-form__button--inverted,
59 .franz-form__button.franz-form__button--inverted {
60 background: none;
61 border-color: ${accentColorStr};
62 }
63
64 .tab-item.is-active {
65 background: ${accentColor.lighten(0.35).hex()};
66 }
67 `;
68
69 return style;
70}
71
72function generateServiceRibbonWidthStyle(widthStr, iconSizeStr, vertical) {
73 const width = Number(widthStr);
74 const iconSize = Number(iconSizeStr) - iconSizeBias;
75
76 return vertical
77 ? `
78 .tab-item {
79 width: ${width - 2}px !important;
80 height: ${width - 5 + iconSize}px !important;
81 min-height: unset;
82 }
83 .tab-item .tab-item__icon {
84 width: ${width / 2 + iconSize}px !important;
85 }
86 .sidebar__button {
87 font-size: ${width / 3}px !important;
88 }
89 `
90 : `
91 .sidebar {
92 width: ${width}px !important;
93 }
94 .tab-item {
95 width: ${width}px !important;
96 height: ${width - 5 + iconSize}px !important;
97 }
98 .tab-item .tab-item__icon {
99 width: ${width / 2 + iconSize}px !important;
100 }
101 .sidebar__button {
102 font-size: ${width / 3}px !important;
103 }
104 .todos__todos-panel--expanded {
105 width: calc(100% - ${300 + width}px) !important;
106 }
107 `;
108}
109
110function generateShowDragAreaStyle(accentColor) {
111 return `
112 .sidebar {
113 padding-top: 0px !important;
114 }
115 .window-draggable {
116 position: initial;
117 background-color: ${accentColor};
118 }
119 #root {
120 /** Remove 22px from app height, otherwise the page will be too high */
121 height: calc(100% - 22px);
122 }
123 `;
124}
125
126function generateVerticalStyle(widthStr, alwaysShowWorkspaces) {
127 if (!document.querySelector('#vertical-style')) {
128 const link = document.createElement('link');
129 link.id = 'vertical-style';
130 link.rel = 'stylesheet';
131 link.type = 'text/css';
132 link.href = './styles/vertical.css';
133
134 document.head.append(link);
135 }
136 const width = Number(widthStr);
137 const sidebarWidth = width - 4;
138 const verticalStyleOffset = 23;
139
140 return `
141 .sidebar {
142 height: ${sidebarWidth + verticalStyleOffset + 1}px !important;
143 ${
144 alwaysShowWorkspaces
145 ? `
146 width: calc(100% - 300px) !important;
147 `
148 : ''
149 }
150 }
151
152 .sidebar .sidebar__button {
153 width: ${width}px;
154 }
155
156 .app .app__content {
157 padding-top: ${sidebarWidth + verticalStyleOffset + 1}px !important;
158 }
159
160 .workspaces-drawer {
161 margin-top: -${sidebarWidth - verticalStyleOffset - 1}px !important;
162 }
163
164 .todos__todos-panel--expanded {
165 width: calc(100% - 300px) !important;
166 }
167 `;
168}
169
170function generateOpenWorkspaceStyle() {
171 return `
172 .app .app__content {
173 width: 100%;
174 transform: translateX(0px);
175 }
176 .sidebar__button--workspaces {
177 display: none;
178 }
179 `;
180}
181
182function generateStyle(settings) {
183 let style = '';
184
185 const {
186 accentColor,
187 serviceRibbonWidth,
188 iconSize,
189 showDragArea,
190 useVerticalStyle,
191 alwaysShowWorkspaces,
192 } = settings;
193
194 if (
195 accentColor.toLowerCase() !== DEFAULT_APP_SETTINGS.accentColor.toLowerCase()
196 ) {
197 style += generateAccentStyle(accentColor);
198 }
199 if (
200 serviceRibbonWidth !== DEFAULT_APP_SETTINGS.serviceRibbonWidth ||
201 iconSize !== DEFAULT_APP_SETTINGS.iconSize
202 ) {
203 style += generateServiceRibbonWidthStyle(
204 serviceRibbonWidth,
205 iconSize,
206 useVerticalStyle,
207 );
208 }
209 if (showDragArea) {
210 style += generateShowDragAreaStyle(accentColor);
211 }
212 if (useVerticalStyle) {
213 style += generateVerticalStyle(serviceRibbonWidth, alwaysShowWorkspaces);
214 } else if (document.querySelector('#vertical-style')) {
215 const link = document.querySelector('#vertical-style');
216 if (link) {
217 link.remove();
218 }
219 }
220 if (alwaysShowWorkspaces) {
221 style += generateOpenWorkspaceStyle();
222 }
223
224 return style;
225}
226function updateStyle(settings) {
227 const style = generateStyle(settings);
228 setAppearance(style);
229}
230
231export default function initAppearance(stores) {
232 const { settings } = stores;
233 createStyleElement();
234
235 // Update style when settings change
236 reaction(
237 () => [
238 settings.all.app.accentColor,
239 settings.all.app.serviceRibbonWidth,
240 settings.all.app.iconSize,
241 settings.all.app.showDragArea,
242 settings.all.app.useVerticalStyle,
243 settings.all.app.alwaysShowWorkspaces,
244 ],
245 () => {
246 updateStyle(settings.all.app);
247 },
248 { fireImmediately: true },
249 );
250}