@code-coaching/vuetiful 0.23.2 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/types/components/atoms/VLightSwitch.vue.d.ts +5 -1
- package/dist/types/services/dark-mode.service.d.ts +13 -13
- package/dist/types/services/index.d.ts +2 -2
- package/dist/types/utils/colors/colors.service.d.ts +69 -0
- package/dist/types/utils/index.d.ts +5 -1
- package/dist/types/utils/theme/theme.service.d.ts +12 -23
- package/dist/types/utils/theme/themes.d.ts +39 -0
- package/dist/vuetiful.es.mjs +452 -146
- package/dist/vuetiful.umd.js +71 -16
- package/package.json +1 -1
- package/src/components/atoms/VLightSwitch.test.ts +61 -12
- package/src/components/atoms/VLightSwitch.vue +13 -19
- package/src/components/molecules/VTabs/VTab.test.ts +21 -0
- package/src/directives/clipboard.test.ts +2 -2
- package/src/services/dark-mode.service.test.ts +58 -210
- package/src/services/dark-mode.service.ts +32 -51
- package/src/services/drawer.service.test.ts +4 -4
- package/src/services/highlight.service.test.ts +3 -3
- package/src/services/index.ts +2 -2
- package/src/services/rail.service.test.ts +2 -2
- package/src/utils/colors/colors.service.ts +293 -0
- package/src/utils/index.ts +5 -1
- package/src/utils/platform/platform.service.test.ts +3 -3
- package/src/utils/theme/callback.test.ts +9 -5
- package/src/utils/theme/remove.test.ts +7 -5
- package/src/utils/theme/theme-switcher.vue +34 -37
- package/src/utils/theme/theme.service.test.ts +160 -58
- package/src/utils/theme/theme.service.ts +140 -78
- package/src/utils/theme/themes.ts +127 -0
- package/dist/types/components/index.test.d.ts +0 -1
- package/dist/types/index.test.d.ts +0 -1
- package/dist/types/utils/index.test.d.ts +0 -1
- package/src/components/index.test.ts +0 -10
- package/src/index.test.ts +0 -26
- package/src/utils/index.test.ts +0 -11
- /package/src/themes/{theme-vuetiful-0.0.1.css → theme-vuetiful.css} +0 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect,
|
|
2
|
-
import { Theme } from './theme.service';
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
3
2
|
|
|
4
3
|
const localStorageMock = {
|
|
5
4
|
getItem: vi.fn(),
|
|
@@ -12,7 +11,7 @@ describe('useTheme', () => {
|
|
|
12
11
|
});
|
|
13
12
|
describe('initializetheme', () => {
|
|
14
13
|
describe('given not in browser', () => {
|
|
15
|
-
|
|
14
|
+
test('should set the theme to the default theme if no theme is stored', async () => {
|
|
16
15
|
const platform = await import('../platform/platform.service');
|
|
17
16
|
vi.spyOn(platform, 'usePlatform').mockReturnValueOnce({
|
|
18
17
|
isBrowser: false,
|
|
@@ -33,8 +32,9 @@ describe('useTheme', () => {
|
|
|
33
32
|
beforeEach(() => {
|
|
34
33
|
window.localStorage = localStorageMock as any;
|
|
35
34
|
});
|
|
35
|
+
|
|
36
36
|
describe('given no theme is stored', () => {
|
|
37
|
-
|
|
37
|
+
test('should set the theme to the default theme', async () => {
|
|
38
38
|
const platform = await import('../platform/platform.service');
|
|
39
39
|
vi.spyOn(platform, 'usePlatform').mockReturnValueOnce({
|
|
40
40
|
isBrowser: true,
|
|
@@ -43,125 +43,227 @@ describe('useTheme', () => {
|
|
|
43
43
|
const localStorageSpy = vi.spyOn(window.localStorage, 'getItem');
|
|
44
44
|
|
|
45
45
|
const { useTheme } = await import('./theme.service');
|
|
46
|
-
const { initializeTheme,
|
|
46
|
+
const { initializeTheme, themes, chosenTheme } = useTheme();
|
|
47
47
|
|
|
48
48
|
initializeTheme();
|
|
49
49
|
|
|
50
|
-
expect(localStorageSpy).
|
|
51
|
-
expect(chosenTheme.value).toBe(
|
|
50
|
+
expect(localStorageSpy).not.toHaveBeenCalled();
|
|
51
|
+
expect(chosenTheme.value.name).toBe(themes[0].name);
|
|
52
52
|
});
|
|
53
53
|
});
|
|
54
|
+
|
|
54
55
|
describe('given a theme is stored', () => {
|
|
55
|
-
|
|
56
|
+
test('should set the theme to the stored theme', async () => {
|
|
56
57
|
const platform = await import('../platform/platform.service');
|
|
57
58
|
vi.spyOn(platform, 'usePlatform').mockReturnValueOnce({
|
|
58
59
|
isBrowser: true,
|
|
59
60
|
});
|
|
60
61
|
|
|
61
62
|
const { useTheme } = await import('./theme.service');
|
|
62
|
-
const { initializeTheme,
|
|
63
|
+
const { initializeTheme, themes, chosenTheme } = useTheme();
|
|
64
|
+
|
|
65
|
+
const customTheme = JSON.parse(JSON.stringify(themes[1]));
|
|
66
|
+
customTheme.name = 'rocket';
|
|
63
67
|
|
|
64
68
|
const localStorageSpy = vi.spyOn(window.localStorage, 'getItem');
|
|
65
|
-
localStorageSpy.mockReturnValueOnce(THEMES.ROCKET);
|
|
66
69
|
|
|
70
|
+
document.cookie = 'vuetiful-theme=rocket';
|
|
67
71
|
initializeTheme();
|
|
68
72
|
|
|
69
|
-
expect(localStorageSpy).
|
|
70
|
-
expect(chosenTheme.value).
|
|
73
|
+
expect(localStorageSpy).not.toHaveBeenCalled();
|
|
74
|
+
expect(chosenTheme.value).toEqual(customTheme);
|
|
71
75
|
});
|
|
72
76
|
});
|
|
73
77
|
|
|
74
|
-
describe('given the theme is not
|
|
75
|
-
|
|
78
|
+
describe('given the theme cookie is not a known theme', () => {
|
|
79
|
+
test('should set the theme to the default theme', async () => {
|
|
76
80
|
const platform = await import('../platform/platform.service');
|
|
77
81
|
vi.spyOn(platform, 'usePlatform').mockReturnValueOnce({
|
|
78
82
|
isBrowser: true,
|
|
79
83
|
});
|
|
80
84
|
|
|
81
85
|
const { useTheme } = await import('./theme.service');
|
|
82
|
-
const { initializeTheme,
|
|
86
|
+
const { initializeTheme, themes, chosenTheme } = useTheme();
|
|
83
87
|
|
|
84
88
|
const localStorageSpy = vi.spyOn(window.localStorage, 'getItem');
|
|
85
|
-
localStorageSpy.mockReturnValueOnce('invalid-theme');
|
|
86
89
|
|
|
90
|
+
document.cookie = 'vuetiful-theme=not-a-theme';
|
|
87
91
|
initializeTheme();
|
|
88
92
|
|
|
89
|
-
expect(localStorageSpy).
|
|
90
|
-
expect(chosenTheme.value).
|
|
93
|
+
expect(localStorageSpy).not.toHaveBeenCalled();
|
|
94
|
+
expect(chosenTheme.value).toEqual(themes[0]);
|
|
91
95
|
});
|
|
92
96
|
});
|
|
97
|
+
|
|
98
|
+
describe('given the theme is set to custom', () => {
|
|
99
|
+
test('should set the custom theme from local storage', async () => {
|
|
100
|
+
const platform = await import('../platform/platform.service');
|
|
101
|
+
vi.spyOn(platform, 'usePlatform').mockReturnValueOnce({
|
|
102
|
+
isBrowser: true,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const { useTheme } = await import('./theme.service');
|
|
106
|
+
const { initializeTheme, themes, chosenTheme } = useTheme();
|
|
107
|
+
|
|
108
|
+
const localStorageSpy = vi.spyOn(window.localStorage, 'getItem');
|
|
109
|
+
const customTheme = JSON.parse(JSON.stringify(themes[0]));
|
|
110
|
+
customTheme.name = 'custom';
|
|
111
|
+
localStorageSpy.mockReturnValueOnce(JSON.stringify(customTheme));
|
|
112
|
+
|
|
113
|
+
document.cookie = 'vuetiful-theme=custom';
|
|
114
|
+
initializeTheme();
|
|
115
|
+
|
|
116
|
+
expect(localStorageSpy).toHaveBeenCalledWith('vuetiful-custom-theme');
|
|
117
|
+
expect(chosenTheme.value).toEqual(customTheme);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('should set the default theme if an invalid theme is stored', async () => {
|
|
121
|
+
const platform = await import('../platform/platform.service');
|
|
122
|
+
vi.spyOn(platform, 'usePlatform').mockReturnValueOnce({
|
|
123
|
+
isBrowser: true,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const { useTheme } = await import('./theme.service');
|
|
127
|
+
const { initializeTheme, themes, chosenTheme } = useTheme();
|
|
128
|
+
|
|
129
|
+
const localStorageSpy = vi.spyOn(window.localStorage, 'getItem');
|
|
130
|
+
localStorageSpy.mockReturnValueOnce('not-a-theme');
|
|
131
|
+
|
|
132
|
+
document.cookie = 'vuetiful-theme=custom';
|
|
133
|
+
initializeTheme();
|
|
134
|
+
|
|
135
|
+
expect(localStorageSpy).toHaveBeenCalledWith('vuetiful-custom-theme');
|
|
136
|
+
expect(chosenTheme.value).toEqual(themes[0]);
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test('should set the default theme if no theme is stored', async () => {
|
|
140
|
+
const platform = await import('../platform/platform.service');
|
|
141
|
+
vi.spyOn(platform, 'usePlatform').mockReturnValueOnce({
|
|
142
|
+
isBrowser: true,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const { useTheme } = await import('./theme.service');
|
|
146
|
+
const { initializeTheme, themes, chosenTheme } = useTheme();
|
|
147
|
+
|
|
148
|
+
const localStorageSpy = vi.spyOn(window.localStorage, 'getItem');
|
|
149
|
+
localStorageSpy.mockReturnValueOnce(null);
|
|
150
|
+
|
|
151
|
+
document.cookie = 'vuetiful-theme=custom';
|
|
152
|
+
initializeTheme();
|
|
153
|
+
|
|
154
|
+
expect(localStorageSpy).toHaveBeenCalledWith('vuetiful-custom-theme');
|
|
155
|
+
expect(chosenTheme.value).toEqual(themes[0]);
|
|
156
|
+
})
|
|
157
|
+
});
|
|
158
|
+
|
|
93
159
|
});
|
|
94
160
|
});
|
|
95
161
|
|
|
96
|
-
describe('
|
|
97
|
-
|
|
98
|
-
|
|
162
|
+
describe('registerTheme', () => {
|
|
163
|
+
test('should register a theme', async () => {
|
|
164
|
+
const { useTheme } = await import('./theme.service');
|
|
165
|
+
const { registerTheme, themes } = useTheme();
|
|
166
|
+
|
|
167
|
+
const newTheme = JSON.parse(JSON.stringify(themes[0]));
|
|
168
|
+
newTheme.name = 'new-theme';
|
|
169
|
+
|
|
170
|
+
registerTheme(newTheme);
|
|
171
|
+
expect(themes).toContain(newTheme);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe('given the theme is already registered', () => {
|
|
175
|
+
test('should update the theme', async () => {
|
|
99
176
|
const { useTheme } = await import('./theme.service');
|
|
100
|
-
const {
|
|
177
|
+
const { registerTheme, themes } = useTheme();
|
|
101
178
|
|
|
102
|
-
|
|
179
|
+
const newTheme = JSON.parse(JSON.stringify(themes[0]));
|
|
180
|
+
newTheme.customCss = 'new-custom-css';
|
|
103
181
|
|
|
104
|
-
|
|
182
|
+
registerTheme(newTheme);
|
|
183
|
+
|
|
184
|
+
expect(themes.find((theme) => theme.name === newTheme.name)).toEqual(newTheme);
|
|
105
185
|
});
|
|
106
186
|
});
|
|
107
|
-
|
|
108
|
-
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe('getThemeFromCookie', () => {
|
|
190
|
+
describe('given there is no cookie', () => {
|
|
191
|
+
test('should return default theme', async () => {
|
|
109
192
|
const { useTheme } = await import('./theme.service');
|
|
110
|
-
const {
|
|
193
|
+
const { getThemeFromCookie, themes } = useTheme();
|
|
111
194
|
|
|
112
|
-
|
|
195
|
+
const cookie = '';
|
|
196
|
+
const theme = getThemeFromCookie(cookie);
|
|
113
197
|
|
|
114
|
-
expect(
|
|
198
|
+
expect(theme).toEqual(themes[0]);
|
|
115
199
|
});
|
|
116
200
|
});
|
|
117
|
-
|
|
118
|
-
|
|
201
|
+
|
|
202
|
+
describe('given there is a cookie', () => {
|
|
203
|
+
test('should return the theme', async () => {
|
|
119
204
|
const { useTheme } = await import('./theme.service');
|
|
120
|
-
const {
|
|
205
|
+
const { getThemeFromCookie, themes } = useTheme();
|
|
121
206
|
|
|
122
|
-
const
|
|
123
|
-
overwriteThemes([themes.value[1], theme]);
|
|
207
|
+
const cookie = 'vuetiful-theme=rocket';
|
|
124
208
|
|
|
125
|
-
|
|
209
|
+
const theme = getThemeFromCookie(cookie);
|
|
126
210
|
|
|
127
|
-
|
|
211
|
+
const rocketTheme = themes.find((theme) => theme.name === 'rocket');
|
|
212
|
+
expect(theme).toEqual(rocketTheme);
|
|
128
213
|
});
|
|
129
214
|
});
|
|
130
215
|
});
|
|
131
216
|
|
|
132
|
-
describe('
|
|
133
|
-
|
|
217
|
+
describe('applyThemeSSR', () => {
|
|
218
|
+
test('should apply the theme', async () => {
|
|
134
219
|
const { useTheme } = await import('./theme.service');
|
|
135
|
-
const {
|
|
220
|
+
const { applyThemeSSR, themes } = useTheme();
|
|
136
221
|
|
|
137
|
-
|
|
222
|
+
const theme = themes[0];
|
|
138
223
|
|
|
139
|
-
|
|
224
|
+
const preHtml = '<html><head></head><body></body></html>';
|
|
225
|
+
const html = applyThemeSSR(preHtml, theme);
|
|
226
|
+
|
|
227
|
+
const style = html.includes('id="vuetiful-theme"');
|
|
228
|
+
expect(style).toBe(true);
|
|
229
|
+
const body = html.includes('data-theme="vuetiful"');
|
|
230
|
+
expect(body).toBe(true);
|
|
140
231
|
});
|
|
141
|
-
});
|
|
142
232
|
|
|
143
|
-
|
|
144
|
-
it('should register a theme', async () => {
|
|
233
|
+
test('no customBase, no customHeadings, no gradients, no custom css', async () => {
|
|
145
234
|
const { useTheme } = await import('./theme.service');
|
|
146
|
-
const {
|
|
147
|
-
|
|
148
|
-
const theme
|
|
149
|
-
|
|
235
|
+
const { applyThemeSSR, themes } = useTheme();
|
|
236
|
+
|
|
237
|
+
const theme = JSON.parse(JSON.stringify(themes[0]));
|
|
238
|
+
theme.fonts.customBase = '';
|
|
239
|
+
theme.fonts.customHeadings = '';
|
|
240
|
+
theme.gradients.light = '';
|
|
241
|
+
theme.gradients.dark = '';
|
|
242
|
+
theme.customCss = '';
|
|
243
|
+
|
|
244
|
+
const preHtml = '<html><head></head><body></body></html>';
|
|
245
|
+
const html = applyThemeSSR(preHtml, theme);
|
|
246
|
+
|
|
247
|
+
const style = html.includes('id="vuetiful-theme"');
|
|
248
|
+
expect(style).toBe(true);
|
|
249
|
+
const body = html.includes('data-theme="vuetiful"');
|
|
250
|
+
expect(body).toBe(true);
|
|
150
251
|
});
|
|
151
|
-
});
|
|
152
252
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const { useTheme } = await import('./theme.service');
|
|
157
|
-
const { saveThemeToStorage } = useTheme();
|
|
253
|
+
test('invalid hex colors', async () => {
|
|
254
|
+
const { useTheme } = await import('./theme.service');
|
|
255
|
+
const { applyThemeSSR, themes } = useTheme();
|
|
158
256
|
|
|
159
|
-
|
|
257
|
+
const theme = JSON.parse(JSON.stringify(themes[0]));
|
|
258
|
+
theme.colors.primary = 'invalid';
|
|
160
259
|
|
|
161
|
-
|
|
260
|
+
const preHtml = '<html><head></head><body></body></html>';
|
|
261
|
+
const html = applyThemeSSR(preHtml, theme);
|
|
162
262
|
|
|
163
|
-
|
|
164
|
-
|
|
263
|
+
const style = html.includes('id="vuetiful-theme"');
|
|
264
|
+
expect(style).toBe(true);
|
|
265
|
+
const body = html.includes('data-theme="vuetiful"');
|
|
266
|
+
expect(body).toBe(true);
|
|
165
267
|
});
|
|
166
268
|
});
|
|
167
269
|
});
|
|
@@ -1,109 +1,171 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Ref, ref } from 'vue';
|
|
2
2
|
import { usePlatform } from '../platform/platform.service';
|
|
3
|
+
import { ColorSettings, Palette, useColors } from '../colors/colors.service';
|
|
4
|
+
import { Theme, themes, THEME } from './themes';
|
|
3
5
|
|
|
4
6
|
const { isBrowser } = usePlatform();
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const builtInUrl = (name: string): string => {
|
|
22
|
-
return `https://code-coaching.dev/vuetiful-themes/theme-${name}.css`;
|
|
7
|
+
const chosenTheme: Ref<Theme> = ref(themes[0]);
|
|
8
|
+
const { generatePalette, hexValuesAreValid } = useColors();
|
|
9
|
+
|
|
10
|
+
const generateColorCSS = (theme: Theme): string => {
|
|
11
|
+
let newCSS = '';
|
|
12
|
+
const newPalette: Record<string, Palette> = {};
|
|
13
|
+
Object.values(theme.colors).forEach((color: ColorSettings) => {
|
|
14
|
+
const colorKey = color.key;
|
|
15
|
+
newPalette[color.key] = generatePalette(color.hex);
|
|
16
|
+
newCSS += '\n\t';
|
|
17
|
+
newCSS += `/* ${colorKey} | ${newPalette[colorKey][500].hex} */\n\t`;
|
|
18
|
+
for (let [k, v] of Object.entries(newPalette[colorKey])) {
|
|
19
|
+
newCSS += `--color-${colorKey}-${k}: ${v.rgb}; /* ⬅ ${v.hex} */\n\t`;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
return newCSS;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
const builtInThemes: Array<Theme> = [
|
|
26
|
-
{ name: THEMES.VUETIFUL, url: builtInUrl(`${THEMES.VUETIFUL}-0.0.1`) },
|
|
27
|
-
{ name: THEMES.ROCKET, url: builtInUrl(`${THEMES.ROCKET}`) },
|
|
28
|
-
{ name: THEMES.SAHARA, url: builtInUrl(`${THEMES.SAHARA}`) },
|
|
29
|
-
{ name: THEMES.SEAFOAM, url: builtInUrl(`${THEMES.SEAFOAM}`) },
|
|
30
|
-
{ name: THEMES.SEASONAL, url: builtInUrl(`${THEMES.SEASONAL}`) },
|
|
31
|
-
{ name: THEMES.SKELETON, url: builtInUrl(`${THEMES.SKELETON}`) },
|
|
32
|
-
{ name: THEMES.VINTAGE, url: builtInUrl(`${THEMES.VINTAGE}`) },
|
|
33
|
-
];
|
|
34
|
-
|
|
35
|
-
const themes: Ref<Array<Theme>> = ref([...builtInThemes]);
|
|
36
|
-
|
|
37
|
-
const defaultTheme = THEMES.VUETIFUL;
|
|
38
|
-
const chosenTheme = ref(defaultTheme);
|
|
39
|
-
|
|
40
25
|
const useTheme = () => {
|
|
41
|
-
const
|
|
42
|
-
const theme = themes.value.find((t) => t.name === name);
|
|
43
|
-
if (!theme) return;
|
|
26
|
+
const changeDataTheme = (name: string) => document.body.setAttribute('data-theme', name);
|
|
44
27
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
28
|
+
const getThemeFromCookie = (cookies: string): Theme => {
|
|
29
|
+
const themeName = getThemeNameFromCookie(cookies);
|
|
30
|
+
const theme = themes.find((t) => t.name === themeName);
|
|
31
|
+
if (theme) return theme;
|
|
32
|
+
return themes[0];
|
|
49
33
|
};
|
|
50
34
|
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
else loadTheme(defaultTheme, callback);
|
|
56
|
-
}
|
|
35
|
+
const getThemeNameFromCookie = (cookies: string): string => {
|
|
36
|
+
const cookie = cookies.split(';').find((c) => c.trim().startsWith(`vuetiful-theme=`));
|
|
37
|
+
const value = cookie?.split('=')[1];
|
|
38
|
+
return value || '';
|
|
57
39
|
};
|
|
58
40
|
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
41
|
+
const applyThemeSSR = (html: string, theme: Theme): string => {
|
|
42
|
+
chosenTheme.value = theme;
|
|
43
|
+
const css = generateCss(theme);
|
|
44
|
+
html = html.replace('</head>', `<style type="text/css" id="vuetiful-theme">${css}</style></head>`);
|
|
45
|
+
html = html.replace('<body', `<body data-theme="${theme.name}"`);
|
|
46
|
+
return html;
|
|
47
|
+
};
|
|
62
48
|
|
|
63
|
-
|
|
64
|
-
|
|
49
|
+
const generateCss = (theme: Theme): string => {
|
|
50
|
+
if (hexValuesAreValid(Object.values(theme.colors))) {
|
|
51
|
+
return `${theme.fonts.baseImports}
|
|
52
|
+
${theme.fonts.headingImports}
|
|
53
|
+
:root {
|
|
54
|
+
/* =~= Theme Properties =~= */
|
|
55
|
+
--theme-font-family-base: ${theme.fonts.customBase ? `"${theme.fonts.customBase}", ` : ''}${theme.fonts.base};
|
|
56
|
+
--theme-font-family-heading: ${theme.fonts.customHeadings ? `"${theme.fonts.customHeadings}", ` : ''}${
|
|
57
|
+
theme.fonts.headings
|
|
58
|
+
};
|
|
59
|
+
--theme-font-color-base: ${theme.textColorLight};
|
|
60
|
+
--theme-font-color-dark: ${theme.textColorDark};
|
|
61
|
+
--theme-rounded-base: ${theme.roundedBase};
|
|
62
|
+
--theme-rounded-container: ${theme.roundedContainer};
|
|
63
|
+
--theme-border-base: ${theme.borderBase};
|
|
64
|
+
|
|
65
|
+
/* =~= Theme On-X Colors =~= */
|
|
66
|
+
--on-primary: ${theme.colors.primary.on};
|
|
67
|
+
--on-secondary: ${theme.colors.secondary.on};
|
|
68
|
+
--on-tertiary: ${theme.colors.tertiary.on};
|
|
69
|
+
--on-success: ${theme.colors.success.on};
|
|
70
|
+
--on-warning: ${theme.colors.warning.on};
|
|
71
|
+
--on-error: ${theme.colors.error.on};
|
|
72
|
+
--on-surface: ${theme.colors.surface.on};
|
|
73
|
+
|
|
74
|
+
/* =~= Theme Colors =~= */
|
|
75
|
+
${generateColorCSS(theme)}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
${
|
|
79
|
+
theme.gradients.light.length
|
|
80
|
+
? `[data-theme="${theme.name}"] {
|
|
81
|
+
background-image:
|
|
82
|
+
${theme.gradients.light};
|
|
83
|
+
}`
|
|
84
|
+
: ''
|
|
85
|
+
}
|
|
86
|
+
${
|
|
87
|
+
theme.gradients.dark.length
|
|
88
|
+
? `.dark [data-theme="${theme.name}"] {
|
|
89
|
+
background-image:
|
|
90
|
+
${theme.gradients.dark};
|
|
91
|
+
}`
|
|
92
|
+
: ''
|
|
93
|
+
}
|
|
94
|
+
${theme.customCss}
|
|
95
|
+
`;
|
|
96
|
+
}
|
|
97
|
+
return '';
|
|
98
|
+
};
|
|
65
99
|
|
|
66
|
-
|
|
67
|
-
|
|
100
|
+
const applyTheme = (theme: Theme, callback?: Function) => {
|
|
101
|
+
const existingStyle = document.getElementById('vuetiful-theme');
|
|
102
|
+
const themeCss = generateCss(theme);
|
|
68
103
|
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
link.rel = 'stylesheet';
|
|
74
|
-
link.onload = () => {
|
|
104
|
+
const style = document.createElement('style');
|
|
105
|
+
style.innerHTML = themeCss;
|
|
106
|
+
style.id = 'vuetiful-theme';
|
|
107
|
+
style.onload = () => {
|
|
75
108
|
if (existingStyle) existingStyle.remove();
|
|
76
|
-
saveThemeToStorage(theme.name);
|
|
77
109
|
if (callback) callback();
|
|
78
110
|
};
|
|
79
111
|
|
|
80
112
|
const head = document.querySelector('head');
|
|
81
|
-
if (head) head.appendChild(
|
|
82
|
-
};
|
|
113
|
+
if (head) head.appendChild(style);
|
|
83
114
|
|
|
84
|
-
|
|
85
|
-
|
|
115
|
+
chosenTheme.value = theme;
|
|
116
|
+
if (isBrowser) {
|
|
117
|
+
document.cookie = `vuetiful-theme=${theme.name};path=/;max-age=31536000;SameSite=Lax`;
|
|
118
|
+
changeDataTheme(theme.name);
|
|
119
|
+
}
|
|
86
120
|
};
|
|
87
121
|
|
|
88
|
-
const
|
|
89
|
-
|
|
122
|
+
const initializeTheme = (callback?: Function): void => {
|
|
123
|
+
if (isBrowser) {
|
|
124
|
+
const themeName = getThemeNameFromCookie(document.cookie);
|
|
125
|
+
|
|
126
|
+
if (themeName === 'custom') {
|
|
127
|
+
const storedThemeJson = localStorage.getItem('vuetiful-custom-theme');
|
|
128
|
+
let storedTheme: Theme | null = null;
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
storedTheme = storedThemeJson ? JSON.parse(storedThemeJson) : null;
|
|
132
|
+
if (storedTheme) {
|
|
133
|
+
applyTheme(storedTheme, callback);
|
|
134
|
+
registerTheme(storedTheme);
|
|
135
|
+
}
|
|
136
|
+
} catch (e) {
|
|
137
|
+
applyTheme(themes[0], callback);
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
const theme = themes.find((t) => t.name === themeName);
|
|
141
|
+
if (theme) {
|
|
142
|
+
applyTheme(theme, callback);
|
|
143
|
+
} else {
|
|
144
|
+
applyTheme(themes[0], callback);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
90
148
|
};
|
|
91
149
|
|
|
92
|
-
const
|
|
93
|
-
themes.
|
|
150
|
+
const registerTheme = (theme: Theme): void => {
|
|
151
|
+
const existingTheme = themes.find((t) => t.name === theme.name);
|
|
152
|
+
if (existingTheme) {
|
|
153
|
+
const index = themes.indexOf(existingTheme);
|
|
154
|
+
themes[index] = theme;
|
|
155
|
+
} else {
|
|
156
|
+
themes.push(theme);
|
|
157
|
+
}
|
|
94
158
|
};
|
|
95
159
|
|
|
96
160
|
return {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
161
|
+
chosenTheme,
|
|
162
|
+
themes,
|
|
163
|
+
THEME,
|
|
164
|
+
applyThemeSSR,
|
|
165
|
+
applyTheme,
|
|
166
|
+
getThemeFromCookie,
|
|
100
167
|
initializeTheme,
|
|
101
|
-
|
|
102
|
-
saveThemeToStorage,
|
|
103
|
-
|
|
104
|
-
THEMES,
|
|
105
|
-
overwriteThemes,
|
|
106
|
-
registerAllBuiltInThemes,
|
|
168
|
+
changeDataTheme,
|
|
107
169
|
registerTheme,
|
|
108
170
|
};
|
|
109
171
|
};
|