@vanduo-oss/framework 1.4.3 → 1.4.5
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 +21 -4
- package/css/components/draggable.css +4 -0
- package/css/components/forms.css +1 -0
- 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 +122 -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
package/dist/vanduo.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Vanduo v1.4.
|
|
1
|
+
/*! Vanduo v1.4.5 | Built: 2026-06-11T18:11:31.153Z | git:70ed923 | development */
|
|
2
2
|
|
|
3
3
|
// js/utils/lifecycle.js
|
|
4
4
|
(function() {
|
|
@@ -176,7 +176,7 @@
|
|
|
176
176
|
// js/vanduo.js
|
|
177
177
|
(function() {
|
|
178
178
|
"use strict";
|
|
179
|
-
const VANDUO_VERSION = true ? "1.4.
|
|
179
|
+
const VANDUO_VERSION = true ? "1.4.5" : "0.0.0-dev";
|
|
180
180
|
const hasOwn = Object.prototype.hasOwnProperty;
|
|
181
181
|
const Vanduo2 = {
|
|
182
182
|
version: VANDUO_VERSION,
|
|
@@ -461,9 +461,10 @@
|
|
|
461
461
|
init: function(root) {
|
|
462
462
|
const snippets = this.queryWithin(root, ".vd-code-snippet");
|
|
463
463
|
snippets.forEach((snippet) => {
|
|
464
|
-
if (
|
|
465
|
-
|
|
464
|
+
if (snippet.dataset.initialized === "true") {
|
|
465
|
+
return;
|
|
466
466
|
}
|
|
467
|
+
this.initSnippet(snippet);
|
|
467
468
|
});
|
|
468
469
|
},
|
|
469
470
|
/**
|
|
@@ -471,7 +472,9 @@
|
|
|
471
472
|
* @param {HTMLElement} snippet - Code snippet container element
|
|
472
473
|
*/
|
|
473
474
|
initSnippet: function(snippet) {
|
|
474
|
-
snippet.dataset.initialized
|
|
475
|
+
if (snippet.dataset.initialized === "true") {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
475
478
|
snippet._codeSnippetCleanup = [];
|
|
476
479
|
const toggle = snippet.querySelector(".vd-code-snippet-toggle");
|
|
477
480
|
const content = snippet.querySelector(".vd-code-snippet-content");
|
|
@@ -499,6 +502,7 @@
|
|
|
499
502
|
lineNumberPanes.forEach((pane) => {
|
|
500
503
|
this.addLineNumbers(pane);
|
|
501
504
|
});
|
|
505
|
+
snippet.dataset.initialized = "true";
|
|
502
506
|
},
|
|
503
507
|
/**
|
|
504
508
|
* Initialize collapsible functionality
|
|
@@ -508,13 +512,14 @@
|
|
|
508
512
|
*/
|
|
509
513
|
initCollapsible: function(snippet, toggle, content) {
|
|
510
514
|
const isExpanded = snippet.dataset.expanded === "true";
|
|
511
|
-
toggle.setAttribute("aria-expanded", isExpanded);
|
|
512
|
-
content.dataset.visible = isExpanded;
|
|
515
|
+
toggle.setAttribute("aria-expanded", isExpanded ? "true" : "false");
|
|
516
|
+
content.dataset.visible = isExpanded ? "true" : "false";
|
|
513
517
|
this.addListener(snippet, toggle, "click", () => {
|
|
514
518
|
const expanded = snippet.dataset.expanded === "true";
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
519
|
+
const nextExpanded = !expanded;
|
|
520
|
+
snippet.dataset.expanded = nextExpanded ? "true" : "false";
|
|
521
|
+
toggle.setAttribute("aria-expanded", nextExpanded ? "true" : "false");
|
|
522
|
+
content.dataset.visible = nextExpanded ? "true" : "false";
|
|
518
523
|
if (!expanded) {
|
|
519
524
|
const extractPanes = content.querySelectorAll("[data-extract]:not([data-extracted])");
|
|
520
525
|
extractPanes.forEach((pane) => {
|
|
@@ -5045,21 +5050,45 @@
|
|
|
5045
5050
|
// js/components/theme-switcher.js
|
|
5046
5051
|
(function() {
|
|
5047
5052
|
"use strict";
|
|
5053
|
+
const THEME_MODES = ["system", "light", "dark"];
|
|
5054
|
+
const THEME_ICON_CLASSES = {
|
|
5055
|
+
system: "ph ph-desktop",
|
|
5056
|
+
light: "ph ph-sun",
|
|
5057
|
+
dark: "ph ph-moon"
|
|
5058
|
+
};
|
|
5059
|
+
const THEME_LABELS = {
|
|
5060
|
+
system: "Theme: System",
|
|
5061
|
+
light: "Theme: Light",
|
|
5062
|
+
dark: "Theme: Dark"
|
|
5063
|
+
};
|
|
5064
|
+
const THEME_OPTION_TOOLTIPS = {
|
|
5065
|
+
system: "Use system preference",
|
|
5066
|
+
light: "Light theme",
|
|
5067
|
+
dark: "Dark theme"
|
|
5068
|
+
};
|
|
5048
5069
|
const ThemeSwitcher = {
|
|
5049
5070
|
isInitialized: false,
|
|
5050
5071
|
_mediaQuery: null,
|
|
5051
5072
|
_onMediaChange: null,
|
|
5073
|
+
menuInstances: /* @__PURE__ */ new Map(),
|
|
5052
5074
|
getToggles: function(root) {
|
|
5075
|
+
const scope = root || document;
|
|
5076
|
+
const toggles = window.Vanduo && typeof window.Vanduo.queryAll === "function" ? window.Vanduo.queryAll(scope, '[data-toggle="theme"]') : Array.from(scope.querySelectorAll('[data-toggle="theme"]'));
|
|
5077
|
+
return toggles.filter(function(toggle) {
|
|
5078
|
+
return !toggle.closest('.vd-theme-switcher[data-theme-ui="menu"]');
|
|
5079
|
+
});
|
|
5080
|
+
},
|
|
5081
|
+
getMenuSwitchers: function(root) {
|
|
5082
|
+
const scope = root || document;
|
|
5053
5083
|
if (window.Vanduo && typeof window.Vanduo.queryAll === "function") {
|
|
5054
|
-
return window.Vanduo.queryAll(
|
|
5084
|
+
return window.Vanduo.queryAll(scope, '.vd-theme-switcher[data-theme-ui="menu"]');
|
|
5055
5085
|
}
|
|
5056
|
-
return Array.from(
|
|
5086
|
+
return Array.from(scope.querySelectorAll('.vd-theme-switcher[data-theme-ui="menu"]'));
|
|
5057
5087
|
},
|
|
5058
5088
|
init: function(root) {
|
|
5059
5089
|
this.STORAGE_KEY = "vanduo-theme-preference";
|
|
5060
5090
|
this.state = {
|
|
5061
5091
|
preference: this.getPreference()
|
|
5062
|
-
// 'light', 'dark', or 'system'
|
|
5063
5092
|
};
|
|
5064
5093
|
if (this.isInitialized) {
|
|
5065
5094
|
this.applyTheme();
|
|
@@ -5076,6 +5105,9 @@
|
|
|
5076
5105
|
return this.getStorageValue(this.STORAGE_KEY, "system");
|
|
5077
5106
|
},
|
|
5078
5107
|
setPreference: function(pref) {
|
|
5108
|
+
if (!THEME_MODES.includes(pref)) {
|
|
5109
|
+
return;
|
|
5110
|
+
}
|
|
5079
5111
|
this.state.preference = pref;
|
|
5080
5112
|
this.setStorageValue(this.STORAGE_KEY, pref);
|
|
5081
5113
|
this.applyTheme();
|
|
@@ -5129,8 +5161,8 @@
|
|
|
5129
5161
|
};
|
|
5130
5162
|
this._mediaQuery.addEventListener("change", this._onMediaChange);
|
|
5131
5163
|
},
|
|
5132
|
-
// Helper to facilitate UI creation if needed, though often UI is in HTML
|
|
5133
5164
|
renderUI: function(root) {
|
|
5165
|
+
this.renderMenuSwitchers(root);
|
|
5134
5166
|
const toggles = this.getToggles(root);
|
|
5135
5167
|
toggles.forEach((toggle) => {
|
|
5136
5168
|
if (toggle.getAttribute("data-theme-initialized") === "true") {
|
|
@@ -5148,7 +5180,7 @@
|
|
|
5148
5180
|
toggle._themeToggleHandler = onChange;
|
|
5149
5181
|
} else {
|
|
5150
5182
|
const onClick = () => {
|
|
5151
|
-
const modes =
|
|
5183
|
+
const modes = THEME_MODES;
|
|
5152
5184
|
const nextIndex = (modes.indexOf(this.state.preference) + 1) % modes.length;
|
|
5153
5185
|
this.setPreference(modes[nextIndex]);
|
|
5154
5186
|
};
|
|
@@ -5157,6 +5189,188 @@
|
|
|
5157
5189
|
}
|
|
5158
5190
|
toggle.setAttribute("data-theme-initialized", "true");
|
|
5159
5191
|
});
|
|
5192
|
+
this.updateUI(root);
|
|
5193
|
+
},
|
|
5194
|
+
renderMenuSwitchers: function(root) {
|
|
5195
|
+
const switchers = this.getMenuSwitchers(root);
|
|
5196
|
+
switchers.forEach((switcher) => {
|
|
5197
|
+
if (switcher.getAttribute("data-theme-menu-initialized") === "true") {
|
|
5198
|
+
return;
|
|
5199
|
+
}
|
|
5200
|
+
const toggle = switcher.querySelector(".vd-theme-switcher-toggle");
|
|
5201
|
+
const menu = switcher.querySelector(".vd-theme-switcher-menu");
|
|
5202
|
+
if (!toggle || !menu) {
|
|
5203
|
+
return;
|
|
5204
|
+
}
|
|
5205
|
+
const options = menu.querySelectorAll("[data-theme-value]");
|
|
5206
|
+
const cleanupFunctions = [];
|
|
5207
|
+
toggle.setAttribute("aria-haspopup", "true");
|
|
5208
|
+
toggle.setAttribute("aria-expanded", "false");
|
|
5209
|
+
menu.setAttribute("aria-hidden", "true");
|
|
5210
|
+
const toggleClickHandler = (e) => {
|
|
5211
|
+
e.preventDefault();
|
|
5212
|
+
e.stopPropagation();
|
|
5213
|
+
this.toggleMenu(switcher, toggle, menu);
|
|
5214
|
+
};
|
|
5215
|
+
toggle.addEventListener("click", toggleClickHandler);
|
|
5216
|
+
cleanupFunctions.push(() => toggle.removeEventListener("click", toggleClickHandler));
|
|
5217
|
+
options.forEach((option) => {
|
|
5218
|
+
const optionClickHandler = (e) => {
|
|
5219
|
+
e.preventDefault();
|
|
5220
|
+
e.stopPropagation();
|
|
5221
|
+
const value = option.getAttribute("data-theme-value");
|
|
5222
|
+
if (value) {
|
|
5223
|
+
this.setPreference(value);
|
|
5224
|
+
}
|
|
5225
|
+
this.closeMenu(switcher, toggle, menu);
|
|
5226
|
+
};
|
|
5227
|
+
option.addEventListener("click", optionClickHandler);
|
|
5228
|
+
cleanupFunctions.push(() => option.removeEventListener("click", optionClickHandler));
|
|
5229
|
+
const optionKeydownHandler = (e) => {
|
|
5230
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
5231
|
+
e.preventDefault();
|
|
5232
|
+
optionClickHandler(e);
|
|
5233
|
+
}
|
|
5234
|
+
};
|
|
5235
|
+
option.addEventListener("keydown", optionKeydownHandler);
|
|
5236
|
+
cleanupFunctions.push(() => option.removeEventListener("keydown", optionKeydownHandler));
|
|
5237
|
+
});
|
|
5238
|
+
const toggleKeydownHandler = (e) => {
|
|
5239
|
+
if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
|
|
5240
|
+
e.preventDefault();
|
|
5241
|
+
if (!menu.classList.contains("is-open")) {
|
|
5242
|
+
this.openMenu(switcher, toggle, menu);
|
|
5243
|
+
}
|
|
5244
|
+
} else if (e.key === "Escape" && menu.classList.contains("is-open")) {
|
|
5245
|
+
e.preventDefault();
|
|
5246
|
+
this.closeMenu(switcher, toggle, menu);
|
|
5247
|
+
}
|
|
5248
|
+
};
|
|
5249
|
+
toggle.addEventListener("keydown", toggleKeydownHandler);
|
|
5250
|
+
cleanupFunctions.push(() => toggle.removeEventListener("keydown", toggleKeydownHandler));
|
|
5251
|
+
const menuKeydownHandler = (e) => {
|
|
5252
|
+
this.handleMenuKeydown(e, switcher, toggle, menu, options);
|
|
5253
|
+
};
|
|
5254
|
+
menu.addEventListener("keydown", menuKeydownHandler);
|
|
5255
|
+
cleanupFunctions.push(() => menu.removeEventListener("keydown", menuKeydownHandler));
|
|
5256
|
+
const documentClickHandler = (e) => {
|
|
5257
|
+
if (!switcher.contains(e.target) && menu.classList.contains("is-open")) {
|
|
5258
|
+
this.closeMenu(switcher, toggle, menu);
|
|
5259
|
+
}
|
|
5260
|
+
};
|
|
5261
|
+
document.addEventListener("click", documentClickHandler);
|
|
5262
|
+
cleanupFunctions.push(() => document.removeEventListener("click", documentClickHandler));
|
|
5263
|
+
this.menuInstances.set(switcher, { toggle, menu, cleanup: cleanupFunctions });
|
|
5264
|
+
switcher.setAttribute("data-theme-menu-initialized", "true");
|
|
5265
|
+
this.initMenuTooltips(switcher);
|
|
5266
|
+
});
|
|
5267
|
+
},
|
|
5268
|
+
initMenuTooltips: function(switcher) {
|
|
5269
|
+
const tooltips = window.Vanduo && typeof window.Vanduo.getComponent === "function" ? window.Vanduo.getComponent("tooltips") : null;
|
|
5270
|
+
if (tooltips && typeof tooltips.init === "function") {
|
|
5271
|
+
tooltips.init(switcher);
|
|
5272
|
+
}
|
|
5273
|
+
},
|
|
5274
|
+
closeOtherMenus: function(exceptMenu) {
|
|
5275
|
+
this.menuInstances.forEach((instance, switcher) => {
|
|
5276
|
+
if (instance.menu !== exceptMenu && instance.menu.classList.contains("is-open")) {
|
|
5277
|
+
this.closeMenu(switcher, instance.toggle, instance.menu);
|
|
5278
|
+
}
|
|
5279
|
+
});
|
|
5280
|
+
},
|
|
5281
|
+
toggleMenu: function(switcher, toggle, menu) {
|
|
5282
|
+
if (menu.classList.contains("is-open")) {
|
|
5283
|
+
this.closeMenu(switcher, toggle, menu);
|
|
5284
|
+
} else {
|
|
5285
|
+
this.openMenu(switcher, toggle, menu);
|
|
5286
|
+
}
|
|
5287
|
+
},
|
|
5288
|
+
openMenu: function(switcher, toggle, menu) {
|
|
5289
|
+
this.closeOtherMenus(menu);
|
|
5290
|
+
switcher.classList.add("is-open");
|
|
5291
|
+
menu.classList.add("is-open");
|
|
5292
|
+
toggle.setAttribute("aria-expanded", "true");
|
|
5293
|
+
menu.setAttribute("aria-hidden", "false");
|
|
5294
|
+
const activeOption = menu.querySelector("[data-theme-value].is-active") || menu.querySelector("[data-theme-value]");
|
|
5295
|
+
if (activeOption) {
|
|
5296
|
+
setTimeout(() => activeOption.focus(), 0);
|
|
5297
|
+
}
|
|
5298
|
+
},
|
|
5299
|
+
closeMenu: function(switcher, toggle, menu) {
|
|
5300
|
+
switcher.classList.remove("is-open");
|
|
5301
|
+
menu.classList.remove("is-open");
|
|
5302
|
+
toggle.setAttribute("aria-expanded", "false");
|
|
5303
|
+
menu.setAttribute("aria-hidden", "true");
|
|
5304
|
+
},
|
|
5305
|
+
handleMenuKeydown: function(e, switcher, toggle, menu, options) {
|
|
5306
|
+
const items = Array.from(options);
|
|
5307
|
+
const currentIndex = items.indexOf(document.activeElement);
|
|
5308
|
+
if (e.key === "Escape") {
|
|
5309
|
+
e.preventDefault();
|
|
5310
|
+
this.closeMenu(switcher, toggle, menu);
|
|
5311
|
+
toggle.focus();
|
|
5312
|
+
return;
|
|
5313
|
+
}
|
|
5314
|
+
if (e.key === "ArrowDown") {
|
|
5315
|
+
e.preventDefault();
|
|
5316
|
+
const nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;
|
|
5317
|
+
items[nextIndex].focus();
|
|
5318
|
+
return;
|
|
5319
|
+
}
|
|
5320
|
+
if (e.key === "ArrowUp") {
|
|
5321
|
+
e.preventDefault();
|
|
5322
|
+
const prevIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;
|
|
5323
|
+
items[prevIndex].focus();
|
|
5324
|
+
}
|
|
5325
|
+
},
|
|
5326
|
+
updateMenuSwitcher: function(switcher) {
|
|
5327
|
+
const toggle = switcher.querySelector(".vd-theme-switcher-toggle");
|
|
5328
|
+
const menu = switcher.querySelector(".vd-theme-switcher-menu");
|
|
5329
|
+
if (!toggle || !menu) {
|
|
5330
|
+
return;
|
|
5331
|
+
}
|
|
5332
|
+
const pref = this.state.preference;
|
|
5333
|
+
const icon = toggle.querySelector("[data-theme-icon]");
|
|
5334
|
+
const label = THEME_LABELS[pref] || THEME_LABELS.system;
|
|
5335
|
+
if (icon) {
|
|
5336
|
+
icon.className = THEME_ICON_CLASSES[pref] || THEME_ICON_CLASSES.system;
|
|
5337
|
+
}
|
|
5338
|
+
toggle.setAttribute("aria-label", label);
|
|
5339
|
+
if (toggle.hasAttribute("data-tooltip")) {
|
|
5340
|
+
toggle.setAttribute("data-tooltip", label);
|
|
5341
|
+
this.refreshTooltipContent(toggle, label);
|
|
5342
|
+
}
|
|
5343
|
+
menu.querySelectorAll("[data-theme-value]").forEach((option) => {
|
|
5344
|
+
const value = option.getAttribute("data-theme-value");
|
|
5345
|
+
const isActive = value === pref;
|
|
5346
|
+
option.classList.toggle("is-active", isActive);
|
|
5347
|
+
option.setAttribute("aria-checked", isActive ? "true" : "false");
|
|
5348
|
+
const tooltipText = THEME_OPTION_TOOLTIPS[value];
|
|
5349
|
+
if (tooltipText && option.hasAttribute("data-tooltip")) {
|
|
5350
|
+
option.setAttribute("data-tooltip", tooltipText);
|
|
5351
|
+
this.refreshTooltipContent(option, tooltipText);
|
|
5352
|
+
}
|
|
5353
|
+
});
|
|
5354
|
+
},
|
|
5355
|
+
refreshTooltipContent: function(element, text) {
|
|
5356
|
+
const tooltips = window.Vanduo && typeof window.Vanduo.getComponent === "function" ? window.Vanduo.getComponent("tooltips") : null;
|
|
5357
|
+
if (!tooltips || typeof tooltips.update !== "function") {
|
|
5358
|
+
return;
|
|
5359
|
+
}
|
|
5360
|
+
tooltips.update(element, text);
|
|
5361
|
+
},
|
|
5362
|
+
updateCycleToggle: function(toggle) {
|
|
5363
|
+
const pref = this.state.preference;
|
|
5364
|
+
const icon = toggle.querySelector("[data-theme-icon]");
|
|
5365
|
+
const label = THEME_LABELS[pref] || THEME_LABELS.system;
|
|
5366
|
+
if (icon) {
|
|
5367
|
+
icon.className = THEME_ICON_CLASSES[pref] || THEME_ICON_CLASSES.system;
|
|
5368
|
+
}
|
|
5369
|
+
toggle.setAttribute("aria-label", label);
|
|
5370
|
+
if (toggle.hasAttribute("data-tooltip")) {
|
|
5371
|
+
toggle.setAttribute("data-tooltip", label);
|
|
5372
|
+
this.refreshTooltipContent(toggle, label);
|
|
5373
|
+
}
|
|
5160
5374
|
},
|
|
5161
5375
|
updateUI: function(root) {
|
|
5162
5376
|
const toggles = this.getToggles(root);
|
|
@@ -5168,11 +5382,26 @@
|
|
|
5168
5382
|
if (span) {
|
|
5169
5383
|
span.textContent = this.state.preference.charAt(0).toUpperCase() + this.state.preference.slice(1);
|
|
5170
5384
|
}
|
|
5385
|
+
if (toggle.querySelector("[data-theme-icon]")) {
|
|
5386
|
+
this.updateCycleToggle(toggle);
|
|
5387
|
+
}
|
|
5171
5388
|
}
|
|
5172
5389
|
});
|
|
5390
|
+
this.getMenuSwitchers(root).forEach((switcher) => {
|
|
5391
|
+
this.updateMenuSwitcher(switcher);
|
|
5392
|
+
});
|
|
5173
5393
|
},
|
|
5174
5394
|
destroyAll: function(root) {
|
|
5175
5395
|
const scope = root || document;
|
|
5396
|
+
this.getMenuSwitchers(scope).forEach((switcher) => {
|
|
5397
|
+
const instance = this.menuInstances.get(switcher);
|
|
5398
|
+
if (instance) {
|
|
5399
|
+
instance.cleanup.forEach((fn) => fn());
|
|
5400
|
+
this.closeMenu(switcher, instance.toggle, instance.menu);
|
|
5401
|
+
this.menuInstances.delete(switcher);
|
|
5402
|
+
}
|
|
5403
|
+
switcher.removeAttribute("data-theme-menu-initialized");
|
|
5404
|
+
});
|
|
5176
5405
|
const toggles = this.getToggles(scope).filter(function(toggle) {
|
|
5177
5406
|
return toggle.getAttribute("data-theme-initialized") === "true";
|
|
5178
5407
|
});
|