@vanduo-oss/framework 1.4.3 → 1.4.4
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/README.md +15 -3
- package/css/components/theme-switcher.css +129 -0
- package/css/vanduo.css +1 -0
- package/dist/build-info.json +3 -3
- package/dist/vanduo.cjs.js +244 -15
- package/dist/vanduo.cjs.js.map +2 -2
- package/dist/vanduo.cjs.min.js +6 -6
- package/dist/vanduo.cjs.min.js.map +3 -3
- package/dist/vanduo.css +117 -1
- package/dist/vanduo.css.map +1 -1
- package/dist/vanduo.esm.js +244 -15
- package/dist/vanduo.esm.js.map +2 -2
- package/dist/vanduo.esm.min.js +6 -6
- package/dist/vanduo.esm.min.js.map +3 -3
- package/dist/vanduo.js +244 -15
- package/dist/vanduo.js.map +2 -2
- package/dist/vanduo.min.css +2 -2
- package/dist/vanduo.min.css.map +1 -1
- package/dist/vanduo.min.js +6 -6
- package/dist/vanduo.min.js.map +3 -3
- package/js/components/code-snippet.js +14 -8
- package/js/components/theme-switcher.js +290 -29
- package/package.json +5 -5
|
@@ -69,9 +69,10 @@
|
|
|
69
69
|
const snippets = this.queryWithin(root, '.vd-code-snippet');
|
|
70
70
|
|
|
71
71
|
snippets.forEach(snippet => {
|
|
72
|
-
if (
|
|
73
|
-
|
|
72
|
+
if (snippet.dataset.initialized === 'true') {
|
|
73
|
+
return;
|
|
74
74
|
}
|
|
75
|
+
this.initSnippet(snippet);
|
|
75
76
|
});
|
|
76
77
|
},
|
|
77
78
|
|
|
@@ -80,7 +81,9 @@
|
|
|
80
81
|
* @param {HTMLElement} snippet - Code snippet container element
|
|
81
82
|
*/
|
|
82
83
|
initSnippet: function (snippet) {
|
|
83
|
-
snippet.dataset.initialized
|
|
84
|
+
if (snippet.dataset.initialized === 'true') {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
84
87
|
snippet._codeSnippetCleanup = [];
|
|
85
88
|
|
|
86
89
|
// Handle collapsible toggle
|
|
@@ -121,6 +124,8 @@
|
|
|
121
124
|
lineNumberPanes.forEach(pane => {
|
|
122
125
|
this.addLineNumbers(pane);
|
|
123
126
|
});
|
|
127
|
+
|
|
128
|
+
snippet.dataset.initialized = 'true';
|
|
124
129
|
},
|
|
125
130
|
|
|
126
131
|
/**
|
|
@@ -132,14 +137,15 @@
|
|
|
132
137
|
initCollapsible: function (snippet, toggle, content) {
|
|
133
138
|
// Set initial state
|
|
134
139
|
const isExpanded = snippet.dataset.expanded === 'true';
|
|
135
|
-
toggle.setAttribute('aria-expanded', isExpanded);
|
|
136
|
-
content.dataset.visible = isExpanded;
|
|
140
|
+
toggle.setAttribute('aria-expanded', isExpanded ? 'true' : 'false');
|
|
141
|
+
content.dataset.visible = isExpanded ? 'true' : 'false';
|
|
137
142
|
|
|
138
143
|
this.addListener(snippet, toggle, 'click', () => {
|
|
139
144
|
const expanded = snippet.dataset.expanded === 'true';
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
145
|
+
const nextExpanded = !expanded;
|
|
146
|
+
snippet.dataset.expanded = nextExpanded ? 'true' : 'false';
|
|
147
|
+
toggle.setAttribute('aria-expanded', nextExpanded ? 'true' : 'false');
|
|
148
|
+
content.dataset.visible = nextExpanded ? 'true' : 'false';
|
|
143
149
|
|
|
144
150
|
// Extract HTML on first expand if needed
|
|
145
151
|
if (!expanded) {
|
|
@@ -6,23 +6,56 @@
|
|
|
6
6
|
(function () {
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
|
+
const THEME_MODES = ['system', 'light', 'dark'];
|
|
10
|
+
|
|
11
|
+
const THEME_ICON_CLASSES = {
|
|
12
|
+
system: 'ph ph-desktop',
|
|
13
|
+
light: 'ph ph-sun',
|
|
14
|
+
dark: 'ph ph-moon'
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const THEME_LABELS = {
|
|
18
|
+
system: 'Theme: System',
|
|
19
|
+
light: 'Theme: Light',
|
|
20
|
+
dark: 'Theme: Dark'
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const THEME_OPTION_TOOLTIPS = {
|
|
24
|
+
system: 'Use system preference',
|
|
25
|
+
light: 'Light theme',
|
|
26
|
+
dark: 'Dark theme'
|
|
27
|
+
};
|
|
28
|
+
|
|
9
29
|
const ThemeSwitcher = {
|
|
10
30
|
isInitialized: false,
|
|
11
31
|
_mediaQuery: null,
|
|
12
32
|
_onMediaChange: null,
|
|
33
|
+
menuInstances: new Map(),
|
|
13
34
|
|
|
14
35
|
getToggles: function (root) {
|
|
36
|
+
const scope = root || document;
|
|
37
|
+
const toggles = window.Vanduo && typeof window.Vanduo.queryAll === 'function'
|
|
38
|
+
? window.Vanduo.queryAll(scope, '[data-toggle="theme"]')
|
|
39
|
+
: Array.from(scope.querySelectorAll('[data-toggle="theme"]'));
|
|
40
|
+
|
|
41
|
+
return toggles.filter(function (toggle) {
|
|
42
|
+
return !toggle.closest('.vd-theme-switcher[data-theme-ui="menu"]');
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
getMenuSwitchers: function (root) {
|
|
47
|
+
const scope = root || document;
|
|
15
48
|
if (window.Vanduo && typeof window.Vanduo.queryAll === 'function') {
|
|
16
|
-
return window.Vanduo.queryAll(
|
|
49
|
+
return window.Vanduo.queryAll(scope, '.vd-theme-switcher[data-theme-ui="menu"]');
|
|
17
50
|
}
|
|
18
51
|
|
|
19
|
-
return Array.from(
|
|
52
|
+
return Array.from(scope.querySelectorAll('.vd-theme-switcher[data-theme-ui="menu"]'));
|
|
20
53
|
},
|
|
21
54
|
|
|
22
55
|
init: function (root) {
|
|
23
56
|
this.STORAGE_KEY = 'vanduo-theme-preference';
|
|
24
57
|
this.state = {
|
|
25
|
-
preference: this.getPreference()
|
|
58
|
+
preference: this.getPreference()
|
|
26
59
|
};
|
|
27
60
|
|
|
28
61
|
if (this.isInitialized) {
|
|
@@ -44,16 +77,18 @@
|
|
|
44
77
|
},
|
|
45
78
|
|
|
46
79
|
setPreference: function (pref) {
|
|
80
|
+
if (!THEME_MODES.includes(pref)) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
47
84
|
this.state.preference = pref;
|
|
48
85
|
this.setStorageValue(this.STORAGE_KEY, pref);
|
|
49
86
|
this.applyTheme();
|
|
50
|
-
|
|
51
|
-
// Coordinate with ThemeCustomizer for primary color swap if available
|
|
52
|
-
// Check _isApplying flag to prevent circular updates
|
|
87
|
+
|
|
53
88
|
if (window.ThemeCustomizer && window.ThemeCustomizer.applyTheme && !window.ThemeCustomizer._isApplying) {
|
|
54
89
|
window.ThemeCustomizer.applyTheme(pref);
|
|
55
90
|
}
|
|
56
|
-
|
|
91
|
+
|
|
57
92
|
this.updateUI();
|
|
58
93
|
},
|
|
59
94
|
|
|
@@ -85,14 +120,6 @@
|
|
|
85
120
|
const pref = this.state.preference;
|
|
86
121
|
|
|
87
122
|
if (pref === 'system') {
|
|
88
|
-
// When in system mode, we remove the data attribute to let the media query take over
|
|
89
|
-
// or we can explicitly set it. Explicitly setting it ensures consistency if we rely on [data-theme]
|
|
90
|
-
// But if we rely on @media in CSS, we might want to remove attributes.
|
|
91
|
-
// However, our CSS strategy uses :root:not([data-theme="light"]) inside media query for system dark fallback
|
|
92
|
-
// which is a bit complex.
|
|
93
|
-
|
|
94
|
-
// Simpler approach:
|
|
95
|
-
// If preference is system, REMOVE data-theme attribute. Let CSS media queries handle it.
|
|
96
123
|
document.documentElement.removeAttribute('data-theme');
|
|
97
124
|
} else {
|
|
98
125
|
document.documentElement.setAttribute('data-theme', pref);
|
|
@@ -107,9 +134,7 @@
|
|
|
107
134
|
this._mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
108
135
|
this._onMediaChange = _e => {
|
|
109
136
|
if (this.state.preference === 'system') {
|
|
110
|
-
// Re-apply (effectively just to ensure consistency, though removing attribute usually suffices)
|
|
111
137
|
this.applyTheme();
|
|
112
|
-
// Keep default primary (black/amber) aligned when OS scheme changes while in system mode
|
|
113
138
|
if (window.ThemeCustomizer && typeof window.ThemeCustomizer.applyTheme === 'function' && !window.ThemeCustomizer._isApplying) {
|
|
114
139
|
window.ThemeCustomizer.applyTheme('system');
|
|
115
140
|
}
|
|
@@ -118,9 +143,9 @@
|
|
|
118
143
|
this._mediaQuery.addEventListener('change', this._onMediaChange);
|
|
119
144
|
},
|
|
120
145
|
|
|
121
|
-
// Helper to facilitate UI creation if needed, though often UI is in HTML
|
|
122
146
|
renderUI: function (root) {
|
|
123
|
-
|
|
147
|
+
this.renderMenuSwitchers(root);
|
|
148
|
+
|
|
124
149
|
const toggles = this.getToggles(root);
|
|
125
150
|
toggles.forEach(toggle => {
|
|
126
151
|
if (toggle.getAttribute('data-theme-initialized') === 'true') {
|
|
@@ -130,7 +155,6 @@
|
|
|
130
155
|
return;
|
|
131
156
|
}
|
|
132
157
|
|
|
133
|
-
// Simplified UI Binding - assumes a select or a button cycle
|
|
134
158
|
if (toggle.tagName === 'SELECT') {
|
|
135
159
|
toggle.value = this.state.preference;
|
|
136
160
|
const onChange = (e) => {
|
|
@@ -139,18 +163,242 @@
|
|
|
139
163
|
toggle.addEventListener('change', onChange);
|
|
140
164
|
toggle._themeToggleHandler = onChange;
|
|
141
165
|
} else {
|
|
142
|
-
// Button implementation (cycle)
|
|
143
166
|
const onClick = () => {
|
|
144
|
-
const modes =
|
|
167
|
+
const modes = THEME_MODES;
|
|
145
168
|
const nextIndex = (modes.indexOf(this.state.preference) + 1) % modes.length;
|
|
146
169
|
this.setPreference(modes[nextIndex]);
|
|
147
170
|
};
|
|
148
171
|
toggle.addEventListener('click', onClick);
|
|
149
172
|
toggle._themeToggleHandler = onClick;
|
|
150
173
|
}
|
|
151
|
-
|
|
174
|
+
|
|
152
175
|
toggle.setAttribute('data-theme-initialized', 'true');
|
|
153
176
|
});
|
|
177
|
+
|
|
178
|
+
this.updateUI(root);
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
renderMenuSwitchers: function (root) {
|
|
182
|
+
const switchers = this.getMenuSwitchers(root);
|
|
183
|
+
|
|
184
|
+
switchers.forEach(switcher => {
|
|
185
|
+
if (switcher.getAttribute('data-theme-menu-initialized') === 'true') {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const toggle = switcher.querySelector('.vd-theme-switcher-toggle');
|
|
190
|
+
const menu = switcher.querySelector('.vd-theme-switcher-menu');
|
|
191
|
+
|
|
192
|
+
if (!toggle || !menu) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const options = menu.querySelectorAll('[data-theme-value]');
|
|
197
|
+
const cleanupFunctions = [];
|
|
198
|
+
|
|
199
|
+
toggle.setAttribute('aria-haspopup', 'true');
|
|
200
|
+
toggle.setAttribute('aria-expanded', 'false');
|
|
201
|
+
menu.setAttribute('aria-hidden', 'true');
|
|
202
|
+
|
|
203
|
+
const toggleClickHandler = (e) => {
|
|
204
|
+
e.preventDefault();
|
|
205
|
+
e.stopPropagation();
|
|
206
|
+
this.toggleMenu(switcher, toggle, menu);
|
|
207
|
+
};
|
|
208
|
+
toggle.addEventListener('click', toggleClickHandler);
|
|
209
|
+
cleanupFunctions.push(() => toggle.removeEventListener('click', toggleClickHandler));
|
|
210
|
+
|
|
211
|
+
options.forEach(option => {
|
|
212
|
+
const optionClickHandler = (e) => {
|
|
213
|
+
e.preventDefault();
|
|
214
|
+
e.stopPropagation();
|
|
215
|
+
const value = option.getAttribute('data-theme-value');
|
|
216
|
+
if (value) {
|
|
217
|
+
this.setPreference(value);
|
|
218
|
+
}
|
|
219
|
+
this.closeMenu(switcher, toggle, menu);
|
|
220
|
+
};
|
|
221
|
+
option.addEventListener('click', optionClickHandler);
|
|
222
|
+
cleanupFunctions.push(() => option.removeEventListener('click', optionClickHandler));
|
|
223
|
+
|
|
224
|
+
const optionKeydownHandler = (e) => {
|
|
225
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
226
|
+
e.preventDefault();
|
|
227
|
+
optionClickHandler(e);
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
option.addEventListener('keydown', optionKeydownHandler);
|
|
231
|
+
cleanupFunctions.push(() => option.removeEventListener('keydown', optionKeydownHandler));
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const toggleKeydownHandler = (e) => {
|
|
235
|
+
if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {
|
|
236
|
+
e.preventDefault();
|
|
237
|
+
if (!menu.classList.contains('is-open')) {
|
|
238
|
+
this.openMenu(switcher, toggle, menu);
|
|
239
|
+
}
|
|
240
|
+
} else if (e.key === 'Escape' && menu.classList.contains('is-open')) {
|
|
241
|
+
e.preventDefault();
|
|
242
|
+
this.closeMenu(switcher, toggle, menu);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
toggle.addEventListener('keydown', toggleKeydownHandler);
|
|
246
|
+
cleanupFunctions.push(() => toggle.removeEventListener('keydown', toggleKeydownHandler));
|
|
247
|
+
|
|
248
|
+
const menuKeydownHandler = (e) => {
|
|
249
|
+
this.handleMenuKeydown(e, switcher, toggle, menu, options);
|
|
250
|
+
};
|
|
251
|
+
menu.addEventListener('keydown', menuKeydownHandler);
|
|
252
|
+
cleanupFunctions.push(() => menu.removeEventListener('keydown', menuKeydownHandler));
|
|
253
|
+
|
|
254
|
+
const documentClickHandler = (e) => {
|
|
255
|
+
if (!switcher.contains(e.target) && menu.classList.contains('is-open')) {
|
|
256
|
+
this.closeMenu(switcher, toggle, menu);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
document.addEventListener('click', documentClickHandler);
|
|
260
|
+
cleanupFunctions.push(() => document.removeEventListener('click', documentClickHandler));
|
|
261
|
+
|
|
262
|
+
this.menuInstances.set(switcher, { toggle, menu, cleanup: cleanupFunctions });
|
|
263
|
+
switcher.setAttribute('data-theme-menu-initialized', 'true');
|
|
264
|
+
|
|
265
|
+
this.initMenuTooltips(switcher);
|
|
266
|
+
});
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
initMenuTooltips: function (switcher) {
|
|
270
|
+
const tooltips = window.Vanduo && typeof window.Vanduo.getComponent === 'function'
|
|
271
|
+
? window.Vanduo.getComponent('tooltips')
|
|
272
|
+
: null;
|
|
273
|
+
|
|
274
|
+
if (tooltips && typeof tooltips.init === 'function') {
|
|
275
|
+
tooltips.init(switcher);
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
|
|
279
|
+
closeOtherMenus: function (exceptMenu) {
|
|
280
|
+
this.menuInstances.forEach((instance, switcher) => {
|
|
281
|
+
if (instance.menu !== exceptMenu && instance.menu.classList.contains('is-open')) {
|
|
282
|
+
this.closeMenu(switcher, instance.toggle, instance.menu);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
toggleMenu: function (switcher, toggle, menu) {
|
|
288
|
+
if (menu.classList.contains('is-open')) {
|
|
289
|
+
this.closeMenu(switcher, toggle, menu);
|
|
290
|
+
} else {
|
|
291
|
+
this.openMenu(switcher, toggle, menu);
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
openMenu: function (switcher, toggle, menu) {
|
|
296
|
+
this.closeOtherMenus(menu);
|
|
297
|
+
switcher.classList.add('is-open');
|
|
298
|
+
menu.classList.add('is-open');
|
|
299
|
+
toggle.setAttribute('aria-expanded', 'true');
|
|
300
|
+
menu.setAttribute('aria-hidden', 'false');
|
|
301
|
+
|
|
302
|
+
const activeOption = menu.querySelector('[data-theme-value].is-active')
|
|
303
|
+
|| menu.querySelector('[data-theme-value]');
|
|
304
|
+
if (activeOption) {
|
|
305
|
+
setTimeout(() => activeOption.focus(), 0);
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
|
|
309
|
+
closeMenu: function (switcher, toggle, menu) {
|
|
310
|
+
switcher.classList.remove('is-open');
|
|
311
|
+
menu.classList.remove('is-open');
|
|
312
|
+
toggle.setAttribute('aria-expanded', 'false');
|
|
313
|
+
menu.setAttribute('aria-hidden', 'true');
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
handleMenuKeydown: function (e, switcher, toggle, menu, options) {
|
|
317
|
+
const items = Array.from(options);
|
|
318
|
+
const currentIndex = items.indexOf(document.activeElement);
|
|
319
|
+
|
|
320
|
+
if (e.key === 'Escape') {
|
|
321
|
+
e.preventDefault();
|
|
322
|
+
this.closeMenu(switcher, toggle, menu);
|
|
323
|
+
toggle.focus();
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (e.key === 'ArrowDown') {
|
|
328
|
+
e.preventDefault();
|
|
329
|
+
const nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;
|
|
330
|
+
items[nextIndex].focus();
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (e.key === 'ArrowUp') {
|
|
335
|
+
e.preventDefault();
|
|
336
|
+
const prevIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;
|
|
337
|
+
items[prevIndex].focus();
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
updateMenuSwitcher: function (switcher) {
|
|
342
|
+
const toggle = switcher.querySelector('.vd-theme-switcher-toggle');
|
|
343
|
+
const menu = switcher.querySelector('.vd-theme-switcher-menu');
|
|
344
|
+
if (!toggle || !menu) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const pref = this.state.preference;
|
|
349
|
+
const icon = toggle.querySelector('[data-theme-icon]');
|
|
350
|
+
const label = THEME_LABELS[pref] || THEME_LABELS.system;
|
|
351
|
+
|
|
352
|
+
if (icon) {
|
|
353
|
+
icon.className = THEME_ICON_CLASSES[pref] || THEME_ICON_CLASSES.system;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
toggle.setAttribute('aria-label', label);
|
|
357
|
+
if (toggle.hasAttribute('data-tooltip')) {
|
|
358
|
+
toggle.setAttribute('data-tooltip', label);
|
|
359
|
+
this.refreshTooltipContent(toggle, label);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
menu.querySelectorAll('[data-theme-value]').forEach(option => {
|
|
363
|
+
const value = option.getAttribute('data-theme-value');
|
|
364
|
+
const isActive = value === pref;
|
|
365
|
+
option.classList.toggle('is-active', isActive);
|
|
366
|
+
option.setAttribute('aria-checked', isActive ? 'true' : 'false');
|
|
367
|
+
|
|
368
|
+
const tooltipText = THEME_OPTION_TOOLTIPS[value];
|
|
369
|
+
if (tooltipText && option.hasAttribute('data-tooltip')) {
|
|
370
|
+
option.setAttribute('data-tooltip', tooltipText);
|
|
371
|
+
this.refreshTooltipContent(option, tooltipText);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
},
|
|
375
|
+
|
|
376
|
+
refreshTooltipContent: function (element, text) {
|
|
377
|
+
const tooltips = window.Vanduo && typeof window.Vanduo.getComponent === 'function'
|
|
378
|
+
? window.Vanduo.getComponent('tooltips')
|
|
379
|
+
: null;
|
|
380
|
+
|
|
381
|
+
if (!tooltips || typeof tooltips.update !== 'function') {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
tooltips.update(element, text);
|
|
386
|
+
},
|
|
387
|
+
|
|
388
|
+
updateCycleToggle: function (toggle) {
|
|
389
|
+
const pref = this.state.preference;
|
|
390
|
+
const icon = toggle.querySelector('[data-theme-icon]');
|
|
391
|
+
const label = THEME_LABELS[pref] || THEME_LABELS.system;
|
|
392
|
+
|
|
393
|
+
if (icon) {
|
|
394
|
+
icon.className = THEME_ICON_CLASSES[pref] || THEME_ICON_CLASSES.system;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
toggle.setAttribute('aria-label', label);
|
|
398
|
+
if (toggle.hasAttribute('data-tooltip')) {
|
|
399
|
+
toggle.setAttribute('data-tooltip', label);
|
|
400
|
+
this.refreshTooltipContent(toggle, label);
|
|
401
|
+
}
|
|
154
402
|
},
|
|
155
403
|
|
|
156
404
|
updateUI: function (root) {
|
|
@@ -159,21 +407,35 @@
|
|
|
159
407
|
if (toggle.tagName === 'SELECT') {
|
|
160
408
|
toggle.value = this.state.preference;
|
|
161
409
|
} else {
|
|
162
|
-
// Update button text/icon if needed
|
|
163
|
-
// e.g. toggle.textContent = this.state.preference;
|
|
164
|
-
// For now, assume the user handles visual state or generic text
|
|
165
|
-
|
|
166
|
-
// If there is an icon or text span inside, update it
|
|
167
410
|
const span = toggle.querySelector('.theme-current-label');
|
|
168
411
|
if (span) {
|
|
169
412
|
span.textContent = this.state.preference.charAt(0).toUpperCase() + this.state.preference.slice(1);
|
|
170
413
|
}
|
|
414
|
+
|
|
415
|
+
if (toggle.querySelector('[data-theme-icon]')) {
|
|
416
|
+
this.updateCycleToggle(toggle);
|
|
417
|
+
}
|
|
171
418
|
}
|
|
172
419
|
});
|
|
420
|
+
|
|
421
|
+
this.getMenuSwitchers(root).forEach(switcher => {
|
|
422
|
+
this.updateMenuSwitcher(switcher);
|
|
423
|
+
});
|
|
173
424
|
},
|
|
174
425
|
|
|
175
426
|
destroyAll: function (root) {
|
|
176
427
|
const scope = root || document;
|
|
428
|
+
|
|
429
|
+
this.getMenuSwitchers(scope).forEach(switcher => {
|
|
430
|
+
const instance = this.menuInstances.get(switcher);
|
|
431
|
+
if (instance) {
|
|
432
|
+
instance.cleanup.forEach(fn => fn());
|
|
433
|
+
this.closeMenu(switcher, instance.toggle, instance.menu);
|
|
434
|
+
this.menuInstances.delete(switcher);
|
|
435
|
+
}
|
|
436
|
+
switcher.removeAttribute('data-theme-menu-initialized');
|
|
437
|
+
});
|
|
438
|
+
|
|
177
439
|
const toggles = this.getToggles(scope).filter(function (toggle) {
|
|
178
440
|
return toggle.getAttribute('data-theme-initialized') === 'true';
|
|
179
441
|
});
|
|
@@ -198,7 +460,6 @@
|
|
|
198
460
|
}
|
|
199
461
|
};
|
|
200
462
|
|
|
201
|
-
// Register component
|
|
202
463
|
if (window.Vanduo) {
|
|
203
464
|
window.Vanduo.register('themeSwitcher', ThemeSwitcher);
|
|
204
465
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vanduo-oss/framework",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.4",
|
|
4
4
|
"description": "Zero-dependency CSS/JS framework built on Fibonacci/Golden Ratio design system with Open Color integration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"css",
|
|
@@ -40,12 +40,12 @@
|
|
|
40
40
|
],
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@eslint/js": "^10.0.1",
|
|
43
|
-
"@playwright/test": "^1.
|
|
44
|
-
"esbuild": "^0.
|
|
45
|
-
"eslint": "^10.
|
|
43
|
+
"@playwright/test": "^1.60.0",
|
|
44
|
+
"esbuild": "^0.28.0",
|
|
45
|
+
"eslint": "^10.4.1",
|
|
46
46
|
"husky": "^9.1.7",
|
|
47
47
|
"lightningcss": "^1.32.0",
|
|
48
|
-
"stylelint": "^17.
|
|
48
|
+
"stylelint": "^17.12.0",
|
|
49
49
|
"stylelint-config-standard": "^40.0.0"
|
|
50
50
|
},
|
|
51
51
|
"engines": {
|