@vanduo-oss/framework 1.3.8 → 1.4.0

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.
Files changed (119) hide show
  1. package/README.md +87 -41
  2. package/css/components/affix.css +1 -1
  3. package/css/components/alerts.css +40 -40
  4. package/css/components/avatar.css +33 -33
  5. package/css/components/badges.css +42 -42
  6. package/css/components/breadcrumbs.css +5 -5
  7. package/css/components/bubble.css +4 -4
  8. package/css/components/buttons.css +124 -124
  9. package/css/components/cards.css +10 -10
  10. package/css/components/chips.css +28 -28
  11. package/css/components/code-snippet.css +18 -18
  12. package/css/components/collapsible.css +28 -20
  13. package/css/components/collections.css +21 -21
  14. package/css/components/datepicker.css +13 -13
  15. package/css/components/doc-search.css +46 -53
  16. package/css/components/doc-tabs.css +10 -10
  17. package/css/components/draggable.css +34 -34
  18. package/css/components/dropdown.css +14 -14
  19. package/css/components/expanding-cards.css +1 -1
  20. package/css/components/fab.css +7 -7
  21. package/css/components/flow.css +3 -3
  22. package/css/components/footer.css +26 -26
  23. package/css/components/forms.css +95 -83
  24. package/css/components/image-box.css +13 -17
  25. package/css/components/modals.css +8 -8
  26. package/css/components/music-player.css +26 -26
  27. package/css/components/navbar.css +27 -27
  28. package/css/components/pagination.css +15 -15
  29. package/css/components/preloader.css +10 -10
  30. package/css/components/progress.css +8 -8
  31. package/css/components/rating.css +4 -4
  32. package/css/components/sidenav.css +14 -14
  33. package/css/components/skeleton.css +10 -9
  34. package/css/components/spinner.css +10 -10
  35. package/css/components/spotlight.css +7 -7
  36. package/css/components/stepper.css +13 -13
  37. package/css/components/suggest.css +10 -10
  38. package/css/components/tabs.css +22 -22
  39. package/css/components/theme-customizer.css +87 -87
  40. package/css/components/timeline.css +14 -14
  41. package/css/components/timepicker.css +7 -7
  42. package/css/components/toast.css +31 -31
  43. package/css/components/tooltips.css +11 -11
  44. package/css/components/transfer.css +12 -12
  45. package/css/components/tree.css +9 -9
  46. package/css/components/waypoint.css +3 -3
  47. package/css/core/colors.css +61 -35
  48. package/css/core/grid.css +1 -6
  49. package/css/core/helpers.css +11 -11
  50. package/css/core/tokens.css +114 -36
  51. package/css/core/typography.css +15 -13
  52. package/css/core/vd-aliases.css +100 -52
  53. package/css/effects/morph.css +5 -5
  54. package/css/utilities/media.css +2 -2
  55. package/css/utilities/table.css +34 -34
  56. package/css/utilities/transitions.css +22 -10
  57. package/css/vanduo.css +14 -34
  58. package/dist/build-info.json +3 -3
  59. package/dist/vanduo.cjs.js +935 -294
  60. package/dist/vanduo.cjs.js.map +3 -3
  61. package/dist/vanduo.cjs.min.js +7 -7
  62. package/dist/vanduo.cjs.min.js.map +3 -3
  63. package/dist/vanduo.css +7942 -7824
  64. package/dist/vanduo.css.map +1 -1
  65. package/dist/vanduo.esm.js +935 -294
  66. package/dist/vanduo.esm.js.map +3 -3
  67. package/dist/vanduo.esm.min.js +7 -7
  68. package/dist/vanduo.esm.min.js.map +3 -3
  69. package/dist/vanduo.js +935 -294
  70. package/dist/vanduo.js.map +3 -3
  71. package/dist/vanduo.min.css +2 -2
  72. package/dist/vanduo.min.css.map +1 -1
  73. package/dist/vanduo.min.js +7 -7
  74. package/dist/vanduo.min.js.map +3 -3
  75. package/js/components/affix.js +2 -2
  76. package/js/components/bubble.js +3 -3
  77. package/js/components/code-snippet.js +129 -5
  78. package/js/components/collapsible.js +2 -3
  79. package/js/components/datepicker.js +2 -2
  80. package/js/components/doc-search.js +69 -11
  81. package/js/components/draggable.js +4 -4
  82. package/js/components/dropdown.js +2 -3
  83. package/js/components/expanding-cards.js +2 -2
  84. package/js/components/flow.js +2 -2
  85. package/js/components/font-switcher.js +26 -16
  86. package/js/components/glass.js +2 -2
  87. package/js/components/grid.js +19 -8
  88. package/js/components/image-box.js +49 -10
  89. package/js/components/lazy-load.js +81 -9
  90. package/js/components/modals.js +28 -12
  91. package/js/components/morph.js +2 -2
  92. package/js/components/music-player.js +2 -2
  93. package/js/components/navbar.js +2 -2
  94. package/js/components/pagination.js +2 -3
  95. package/js/components/parallax.js +9 -10
  96. package/js/components/preloader.js +14 -5
  97. package/js/components/rating.js +2 -2
  98. package/js/components/ripple.js +2 -2
  99. package/js/components/select.js +2 -3
  100. package/js/components/sidenav.js +43 -14
  101. package/js/components/spotlight.js +2 -2
  102. package/js/components/stepper.js +2 -2
  103. package/js/components/suggest.js +9 -3
  104. package/js/components/tabs.js +2 -2
  105. package/js/components/theme-customizer.js +154 -23
  106. package/js/components/theme-switcher.js +27 -16
  107. package/js/components/timeline.js +41 -12
  108. package/js/components/timepicker.js +2 -2
  109. package/js/components/toast.js +1 -1
  110. package/js/components/tooltips.js +4 -4
  111. package/js/components/transfer.js +2 -2
  112. package/js/components/tree.js +2 -2
  113. package/js/components/validate.js +2 -2
  114. package/js/components/vd-hex.js +12 -6
  115. package/js/components/waypoint.js +2 -2
  116. package/js/utils/helpers.js +7 -4
  117. package/js/utils/lifecycle.js +158 -83
  118. package/js/vanduo.js +203 -34
  119. package/package.json +3 -4
@@ -1,192 +1,400 @@
1
- /*! Vanduo v1.3.8 | Built: 2026-05-06T18:32:43.703Z | git:6042eac | development */
1
+ /*! Vanduo v1.4.0 | Built: 2026-05-20T14:51:00.085Z | git:46420b0 | development */
2
2
 
3
3
  // js/utils/lifecycle.js
4
4
  (function() {
5
5
  "use strict";
6
+ function normalizeCallbacks(value) {
7
+ if (!value) return [];
8
+ if (Array.isArray(value)) {
9
+ return value.filter(function(fn) {
10
+ return typeof fn === "function";
11
+ });
12
+ }
13
+ return typeof value === "function" ? [value] : [];
14
+ }
15
+ function normalizeOptions(options) {
16
+ if (typeof options === "function") {
17
+ return { onDestroy: [options] };
18
+ }
19
+ return options || {};
20
+ }
21
+ function callSafely(label, fn) {
22
+ try {
23
+ fn();
24
+ } catch (error) {
25
+ console.warn("[Vanduo Lifecycle] " + label + " error:", error);
26
+ }
27
+ }
6
28
  const Lifecycle = {
7
- // Map of element -> { componentName, cleanupFunctions }
29
+ // Map<Element, Map<componentName, { cleanup, onDestroy, registeredAt }>>
8
30
  instances: /* @__PURE__ */ new Map(),
9
- /**
10
- * Register a component instance
11
- * @param {HTMLElement} element - The DOM element
12
- * @param {string} componentName - Name of the component
13
- * @param {Array<Function>} cleanupFns - Functions to call on destroy
14
- */
15
- register: function(element, componentName, cleanupFns = []) {
16
- if (this.instances.has(element)) {
17
- const existing = this.instances.get(element);
18
- existing.cleanup = existing.cleanup.concat(cleanupFns);
31
+ isRoot: function(root) {
32
+ return !!root && (root === document || root.nodeType === 1 || root.nodeType === 9 || root.nodeType === 11);
33
+ },
34
+ normalizeRoot: function(root) {
35
+ return this.isRoot(root) ? root : document;
36
+ },
37
+ isInRoot: function(root, element) {
38
+ const scope = this.normalizeRoot(root);
39
+ if (!(element instanceof Element)) return false;
40
+ if (scope === document) {
41
+ return document.documentElement ? document.documentElement.contains(element) : document.contains(element);
42
+ }
43
+ if (scope === element) return true;
44
+ return typeof scope.contains === "function" && scope.contains(element);
45
+ },
46
+ queryAll: function(root, selector) {
47
+ const scope = this.normalizeRoot(root);
48
+ const matches = [];
49
+ if (scope instanceof Element && typeof scope.matches === "function" && scope.matches(selector)) {
50
+ matches.push(scope);
51
+ }
52
+ if (typeof scope.querySelectorAll === "function") {
53
+ const descendants = scope.querySelectorAll(selector);
54
+ for (let i = 0; i < descendants.length; i++) {
55
+ matches.push(descendants[i]);
56
+ }
57
+ }
58
+ return matches;
59
+ },
60
+ queryOne: function(root, selector) {
61
+ const matches = this.queryAll(root, selector);
62
+ return matches.length ? matches[0] : null;
63
+ },
64
+ runInRoot: function(root, fn) {
65
+ const scope = this.normalizeRoot(root);
66
+ return fn(scope);
67
+ },
68
+ register: function(element, componentName, cleanupFns, options) {
69
+ if (!(element instanceof Element) || !componentName) return;
70
+ const optionBag = normalizeOptions(options);
71
+ const cleanup = normalizeCallbacks(cleanupFns);
72
+ const onDestroy = normalizeCallbacks(optionBag.onDestroy);
73
+ const componentEntries = this.instances.get(element) || /* @__PURE__ */ new Map();
74
+ const existing = componentEntries.get(componentName);
75
+ if (existing) {
76
+ existing.cleanup = existing.cleanup.concat(cleanup);
77
+ existing.onDestroy = existing.onDestroy.concat(onDestroy);
19
78
  return;
20
79
  }
21
- this.instances.set(element, {
80
+ componentEntries.set(componentName, {
22
81
  component: componentName,
23
- cleanup: cleanupFns,
82
+ cleanup,
83
+ onDestroy,
24
84
  registeredAt: Date.now()
25
85
  });
86
+ this.instances.set(element, componentEntries);
26
87
  },
27
- /**
28
- * Unregister a single element and run its cleanup
29
- * @param {HTMLElement} element - The element to unregister
30
- */
31
- unregister: function(element) {
32
- const instance = this.instances.get(element);
33
- if (!instance) return;
34
- instance.cleanup.forEach(function(fn) {
35
- try {
36
- fn();
37
- } catch (e) {
38
- console.warn("[Vanduo Lifecycle] Cleanup error:", e);
88
+ unregister: function(element, componentName) {
89
+ const componentEntries = this.instances.get(element);
90
+ if (!componentEntries) return;
91
+ if (componentName) {
92
+ const entry = componentEntries.get(componentName);
93
+ if (!entry) return;
94
+ componentEntries.delete(componentName);
95
+ if (!componentEntries.size) {
96
+ this.instances.delete(element);
39
97
  }
40
- });
98
+ entry.cleanup.forEach(function(fn) {
99
+ callSafely("Cleanup", fn);
100
+ });
101
+ entry.onDestroy.forEach(function(fn) {
102
+ callSafely("Destroy", fn);
103
+ });
104
+ return;
105
+ }
106
+ const entries = Array.from(componentEntries.values());
41
107
  this.instances.delete(element);
108
+ entries.forEach(function(entry) {
109
+ entry.cleanup.forEach(function(fn) {
110
+ callSafely("Cleanup", fn);
111
+ });
112
+ entry.onDestroy.forEach(function(fn) {
113
+ callSafely("Destroy", fn);
114
+ });
115
+ });
42
116
  },
43
- /**
44
- * Destroy all instances of a specific component
45
- * @param {string} componentName - Optional component name filter
46
- */
47
117
  destroyAll: function(componentName) {
48
118
  const toRemove = [];
49
- this.instances.forEach(function(instance, element) {
50
- if (!componentName || instance.component === componentName) {
51
- toRemove.push(element);
119
+ this.instances.forEach(function(componentEntries, element) {
120
+ if (!componentName) {
121
+ toRemove.push([element, null]);
122
+ return;
123
+ }
124
+ if (componentEntries.has(componentName)) {
125
+ toRemove.push([element, componentName]);
52
126
  }
53
127
  });
54
- toRemove.forEach(function(element) {
55
- Lifecycle.unregister(element);
128
+ toRemove.forEach(function(entry) {
129
+ Lifecycle.unregister(entry[0], entry[1] || void 0);
56
130
  });
131
+ return toRemove.length;
57
132
  },
58
- /**
59
- * Destroy all instances within a specific container
60
- * Useful for SPAs when navigating between pages
61
- * @param {HTMLElement} container - Container element
62
- */
63
- destroyAllInContainer: function(container) {
133
+ destroyAllInContainer: function(container, componentName) {
134
+ const scope = this.normalizeRoot(container);
64
135
  const toRemove = [];
65
- this.instances.forEach(function(instance, element) {
66
- if (container.contains(element)) {
67
- toRemove.push(element);
136
+ this.instances.forEach(function(componentEntries, element) {
137
+ if (!Lifecycle.isInRoot(scope, element)) return;
138
+ if (!componentName) {
139
+ toRemove.push([element, null]);
140
+ return;
141
+ }
142
+ if (componentEntries.has(componentName)) {
143
+ toRemove.push([element, componentName]);
68
144
  }
69
145
  });
70
- toRemove.forEach(function(element) {
71
- Lifecycle.unregister(element);
146
+ toRemove.forEach(function(entry) {
147
+ Lifecycle.unregister(entry[0], entry[1] || void 0);
72
148
  });
149
+ return toRemove.length;
73
150
  },
74
- /**
75
- * Get all registered instances (for debugging)
76
- * @returns {Array} Array of instance info objects
77
- */
78
151
  getAll: function() {
79
152
  const result = [];
80
- this.instances.forEach(function(instance, element) {
81
- result.push({
82
- element,
83
- component: instance.component,
84
- registeredAt: instance.registeredAt
153
+ this.instances.forEach(function(componentEntries, element) {
154
+ componentEntries.forEach(function(entry) {
155
+ result.push({
156
+ element,
157
+ component: entry.component,
158
+ registeredAt: entry.registeredAt
159
+ });
85
160
  });
86
161
  });
87
162
  return result;
88
163
  },
89
- /**
90
- * Check if an element is registered
91
- * @param {HTMLElement} element - The element to check
92
- * @returns {boolean}
93
- */
94
- has: function(element) {
95
- return this.instances.has(element);
164
+ has: function(element, componentName) {
165
+ const componentEntries = this.instances.get(element);
166
+ if (!componentEntries) return false;
167
+ return componentName ? componentEntries.has(componentName) : componentEntries.size > 0;
96
168
  }
97
169
  };
98
170
  window.addEventListener("beforeunload", function() {
99
171
  Lifecycle.destroyAll();
100
172
  });
101
173
  window.VanduoLifecycle = Lifecycle;
102
- if (typeof window.Vanduo !== "undefined") {
103
- window.Vanduo.register("lifecycle", Lifecycle);
104
- }
105
174
  })();
106
175
 
107
176
  // js/vanduo.js
108
177
  (function() {
109
178
  "use strict";
110
- const VANDUO_VERSION = true ? "1.3.8" : "0.0.0-dev";
179
+ const VANDUO_VERSION = true ? "1.4.0" : "0.0.0-dev";
180
+ const hasOwn = Object.prototype.hasOwnProperty;
111
181
  const Vanduo2 = {
112
182
  version: VANDUO_VERSION,
113
183
  components: {},
184
+ aliases: {},
185
+ _decoratedComponents: /* @__PURE__ */ new WeakSet(),
186
+ resolveComponentName: function(name) {
187
+ return this.aliases[name] || name;
188
+ },
189
+ _isRoot: function(root) {
190
+ if (typeof window.VanduoLifecycle !== "undefined" && typeof window.VanduoLifecycle.isRoot === "function") {
191
+ return window.VanduoLifecycle.isRoot(root);
192
+ }
193
+ return !!root && (root === document || root.nodeType === 1 || root.nodeType === 9 || root.nodeType === 11);
194
+ },
195
+ _normalizeRoot: function(root) {
196
+ return this._isRoot(root) ? root : document;
197
+ },
198
+ _queryAll: function(root, selector) {
199
+ const scope = this._normalizeRoot(root);
200
+ const matches = [];
201
+ if (scope instanceof Element && typeof scope.matches === "function" && scope.matches(selector)) {
202
+ matches.push(scope);
203
+ }
204
+ if (typeof scope.querySelectorAll === "function") {
205
+ const descendants = scope.querySelectorAll(selector);
206
+ for (let i = 0; i < descendants.length; i++) {
207
+ matches.push(descendants[i]);
208
+ }
209
+ }
210
+ return matches;
211
+ },
212
+ queryAll: function(root, selector) {
213
+ if (typeof selector === "undefined") {
214
+ selector = root;
215
+ root = document;
216
+ }
217
+ return this._queryAll(root, selector);
218
+ },
219
+ queryOne: function(root, selector) {
220
+ const matches = this.queryAll(root, selector);
221
+ return matches.length ? matches[0] : null;
222
+ },
223
+ _isLifecycleManagedComponent: function(component) {
224
+ if (!component || typeof component !== "object") return false;
225
+ for (const key in component) {
226
+ if (hasOwn.call(component, key) && component[key] instanceof Map) {
227
+ return true;
228
+ }
229
+ }
230
+ return false;
231
+ },
232
+ _syncComponentLifecycle: function(name, component, root) {
233
+ const lifecycle = window.VanduoLifecycle;
234
+ if (!lifecycle || !this._isLifecycleManagedComponent(component)) return;
235
+ const componentName = this.resolveComponentName(name);
236
+ const scope = this._normalizeRoot(root);
237
+ for (const key in component) {
238
+ if (!hasOwn.call(component, key) || !(component[key] instanceof Map)) {
239
+ continue;
240
+ }
241
+ component[key].forEach(function(instance, element) {
242
+ if (!(element instanceof Element) || !lifecycle.isInRoot(scope, element) || lifecycle.has(element, componentName)) {
243
+ return;
244
+ }
245
+ if (typeof component.destroy === "function") {
246
+ lifecycle.register(element, componentName, [], function() {
247
+ component.destroy(element);
248
+ });
249
+ return;
250
+ }
251
+ const cleanup = instance && Array.isArray(instance.cleanup) ? instance.cleanup : [];
252
+ lifecycle.register(element, componentName, cleanup, function() {
253
+ component[key].delete(element);
254
+ });
255
+ });
256
+ }
257
+ },
258
+ _decorateComponent: function(name, component) {
259
+ const framework = this;
260
+ const lifecycle = window.VanduoLifecycle;
261
+ if (!component || typeof component !== "object" || this._decoratedComponents.has(component)) {
262
+ return;
263
+ }
264
+ const originalInit = typeof component.init === "function" ? component.init : null;
265
+ if (originalInit) {
266
+ component.init = function(...args) {
267
+ const scopedRoot = framework._isRoot(args[0]) ? args[0] : null;
268
+ const result = originalInit.apply(this, args);
269
+ if (window.Vanduo) {
270
+ const syncRoot = scopedRoot || document;
271
+ window.Vanduo._syncComponentLifecycle(name, this, syncRoot);
272
+ }
273
+ return result;
274
+ };
275
+ }
276
+ const originalDestroyAll = typeof component.destroyAll === "function" ? component.destroyAll : null;
277
+ if (originalDestroyAll) {
278
+ component.destroyAll = function(...args) {
279
+ const scopedRoot = framework._isRoot(args[0]) ? args[0] : null;
280
+ const componentName = window.Vanduo ? window.Vanduo.resolveComponentName(name) : name;
281
+ if (lifecycle && window.Vanduo && window.Vanduo._isLifecycleManagedComponent(this)) {
282
+ if (scopedRoot && scopedRoot !== document) {
283
+ lifecycle.destroyAllInContainer(scopedRoot, componentName);
284
+ if (this.__vanduoScopedDestroyAll === true) {
285
+ return originalDestroyAll.apply(this, args);
286
+ }
287
+ return;
288
+ }
289
+ lifecycle.destroyAll(componentName);
290
+ }
291
+ return originalDestroyAll.apply(this, args);
292
+ };
293
+ }
294
+ this._decoratedComponents.add(component);
295
+ },
114
296
  /**
115
297
  * Initialize framework
116
298
  * Call this after DOM is ready and all components are loaded
117
299
  */
118
- init: function() {
300
+ init: function(root) {
301
+ const scope = this._normalizeRoot(root);
302
+ if (scope !== document) {
303
+ this.initComponents(scope);
304
+ return;
305
+ }
119
306
  if (typeof ready !== "undefined") {
120
307
  ready(() => {
121
- this.initComponents();
308
+ this.initComponents(document);
122
309
  });
123
- } else {
124
- if (document.readyState === "loading") {
125
- document.addEventListener("DOMContentLoaded", () => {
126
- this.initComponents();
127
- });
128
- } else {
129
- this.initComponents();
130
- }
310
+ return;
131
311
  }
312
+ if (document.readyState === "loading") {
313
+ document.addEventListener("DOMContentLoaded", () => {
314
+ this.initComponents(document);
315
+ });
316
+ return;
317
+ }
318
+ this.initComponents(document);
132
319
  },
133
320
  /**
134
321
  * Initialize all components
135
322
  */
136
- initComponents: function() {
323
+ initComponents: function(root) {
324
+ const scope = this._normalizeRoot(root);
137
325
  Object.keys(this.components).forEach((name) => {
138
326
  const component = this.components[name];
139
327
  if (component.init && typeof component.init === "function") {
140
328
  try {
141
- component.init();
329
+ component.init(scope);
142
330
  } catch (e) {
143
331
  console.warn('[Vanduo] Failed to initialize component "' + name + '":', e);
144
332
  }
145
333
  }
146
334
  });
147
- console.log("Vanduo Framework v" + this.version + " initialized");
148
335
  },
149
336
  /**
150
337
  * Register a component
151
338
  * @param {string} name - Component name
152
339
  * @param {Object} component - Component object with init method
153
340
  */
154
- register: function(name, component) {
341
+ register: function(name, component, options) {
342
+ const opts = options || {};
343
+ this._decorateComponent(name, component);
155
344
  this.components[name] = component;
345
+ if (Array.isArray(opts.aliases)) {
346
+ opts.aliases.forEach((alias) => {
347
+ this.aliases[alias] = name;
348
+ });
349
+ }
350
+ },
351
+ registerAlias: function(alias, name) {
352
+ const canonicalName = this.resolveComponentName(name);
353
+ if (this.components[canonicalName]) {
354
+ this.aliases[alias] = canonicalName;
355
+ }
156
356
  },
157
357
  /**
158
358
  * Re-initialize a component (useful after dynamic DOM changes)
159
359
  * @param {string} name - Component name
160
360
  */
161
- reinit: function(name) {
162
- const component = this.components[name];
361
+ reinit: function(name, root) {
362
+ const scope = this._normalizeRoot(root);
363
+ const componentName = this.resolveComponentName(name);
364
+ const component = this.components[componentName];
163
365
  if (component && component.init && typeof component.init === "function") {
164
366
  try {
165
- component.init();
367
+ if (component.destroyAll && typeof component.destroyAll === "function") {
368
+ component.destroyAll(scope);
369
+ }
370
+ component.init(scope);
166
371
  } catch (e) {
167
- console.warn('[Vanduo] Failed to reinitialize component "' + name + '":', e);
372
+ console.warn('[Vanduo] Failed to reinitialize component "' + componentName + '":', e);
168
373
  }
169
374
  }
170
375
  },
171
376
  /**
172
- * Destroy all component instances and clean up event listeners
173
- * Uses lifecycle manager for memory leak prevention
377
+ * Destroy component instances within the provided root.
174
378
  */
175
- destroyAll: function() {
379
+ destroy: function(root) {
380
+ const scope = this._normalizeRoot(root);
176
381
  const names = Object.keys(this.components);
177
382
  for (let i = 0; i < names.length; i++) {
178
383
  const component = this.components[names[i]];
179
384
  if (component && component.destroyAll && typeof component.destroyAll === "function") {
180
385
  try {
181
- component.destroyAll();
386
+ component.destroyAll(scope);
182
387
  } catch (e) {
183
388
  console.warn('[Vanduo] Failed to destroy component "' + names[i] + '":', e);
184
389
  }
185
390
  }
186
391
  }
187
- if (typeof window.VanduoLifecycle !== "undefined") {
188
- window.VanduoLifecycle.destroyAll();
189
- }
392
+ },
393
+ /**
394
+ * Destroy all component instances and clean up event listeners.
395
+ */
396
+ destroyAll: function() {
397
+ this.destroy(document);
190
398
  },
191
399
  /**
192
400
  * Get component instance
@@ -194,7 +402,8 @@
194
402
  * @returns {Object|null}
195
403
  */
196
404
  getComponent: function(name) {
197
- return this.components[name] || null;
405
+ const componentName = this.resolveComponentName(name);
406
+ return this.components[componentName] || null;
198
407
  }
199
408
  };
200
409
  window.Vanduo = Vanduo2;
@@ -205,6 +414,26 @@
205
414
  "use strict";
206
415
  const CodeSnippet = {
207
416
  _snippetIdCounter: 0,
417
+ resolveRoot: function(root) {
418
+ if (root && (root.nodeType === 1 || root.nodeType === 9 || root.nodeType === 11)) {
419
+ return root;
420
+ }
421
+ return document;
422
+ },
423
+ queryWithin: function(root, selector) {
424
+ const scope = this.resolveRoot(root);
425
+ const matches = [];
426
+ if (scope instanceof Element && typeof scope.matches === "function" && scope.matches(selector)) {
427
+ matches.push(scope);
428
+ }
429
+ if (typeof scope.querySelectorAll === "function") {
430
+ const descendants = scope.querySelectorAll(selector);
431
+ for (let i = 0; i < descendants.length; i++) {
432
+ matches.push(descendants[i]);
433
+ }
434
+ }
435
+ return matches;
436
+ },
208
437
  getSnippetInstanceId: function(snippet) {
209
438
  if (snippet.dataset.codeSnippetId) {
210
439
  return snippet.dataset.codeSnippetId;
@@ -229,8 +458,8 @@
229
458
  /**
230
459
  * Initialize all code snippet components
231
460
  */
232
- init: function() {
233
- const snippets = document.querySelectorAll(".vd-code-snippet");
461
+ init: function(root) {
462
+ const snippets = this.queryWithin(root, ".vd-code-snippet");
234
463
  snippets.forEach((snippet) => {
235
464
  if (!snippet.dataset.initialized) {
236
465
  this.initSnippet(snippet);
@@ -262,6 +491,10 @@
262
491
  extractPanes.forEach((pane) => {
263
492
  this.extractHtml(pane);
264
493
  });
494
+ const panesToHighlight = snippet.querySelectorAll(".vd-code-snippet-pane:not([data-extract])");
495
+ panesToHighlight.forEach((pane) => {
496
+ this.applyPaneHighlighting(pane);
497
+ });
265
498
  const lineNumberPanes = snippet.querySelectorAll(".has-line-numbers");
266
499
  lineNumberPanes.forEach((pane) => {
267
500
  this.addLineNumbers(pane);
@@ -497,6 +730,7 @@
497
730
  codeEl.innerHTML = html;
498
731
  pane.replaceChildren(codeEl);
499
732
  pane.dataset.extracted = "true";
733
+ pane.dataset.highlighted = "true";
500
734
  },
501
735
  /**
502
736
  * Format HTML with proper indentation
@@ -538,6 +772,77 @@
538
772
  div.textContent = html;
539
773
  return div.innerHTML;
540
774
  },
775
+ needsHighlighting: function(pane, codeEl) {
776
+ if (!codeEl) return false;
777
+ if (pane.dataset.highlighted === "true") return false;
778
+ if (codeEl.querySelector('[class^="code-"], [class*=" code-"]')) return false;
779
+ return true;
780
+ },
781
+ getHighlightMode: function(lang) {
782
+ const normalized = String(lang || "").trim().toLowerCase();
783
+ if ([
784
+ "html",
785
+ "xml",
786
+ "svg",
787
+ "vue",
788
+ "svelte",
789
+ "astro"
790
+ ].includes(normalized)) {
791
+ return "html";
792
+ }
793
+ if ([
794
+ "css",
795
+ "scss",
796
+ "sass",
797
+ "less"
798
+ ].includes(normalized)) {
799
+ return "css";
800
+ }
801
+ if ([
802
+ "js",
803
+ "mjs",
804
+ "cjs",
805
+ "ts",
806
+ "jsx",
807
+ "tsx",
808
+ "json",
809
+ "bash",
810
+ "sh"
811
+ ].includes(normalized)) {
812
+ return "js";
813
+ }
814
+ return "plain";
815
+ },
816
+ highlightCodeByLang: function(rawCode, lang) {
817
+ const escaped = this.escapeHtml(rawCode);
818
+ const mode = this.getHighlightMode(lang);
819
+ if (mode === "html") {
820
+ return this.highlightHtml(escaped);
821
+ }
822
+ if (mode === "css") {
823
+ return this.highlightCss(escaped);
824
+ }
825
+ if (mode === "js") {
826
+ return this.highlightJs(escaped);
827
+ }
828
+ return escaped;
829
+ },
830
+ applyPaneHighlighting: function(pane) {
831
+ if (!pane) return;
832
+ const codeEl = pane.querySelector("code") || pane;
833
+ if (!this.needsHighlighting(pane, codeEl)) {
834
+ pane.dataset.highlighted = "true";
835
+ return;
836
+ }
837
+ const rawCode = codeEl.textContent || "";
838
+ const highlighted = this.highlightCodeByLang(rawCode, pane.dataset.lang);
839
+ const nextCodeEl = codeEl.tagName === "CODE" ? codeEl : document.createElement("code");
840
+ nextCodeEl.innerHTML = highlighted;
841
+ if (nextCodeEl !== codeEl) {
842
+ pane.replaceChildren(nextCodeEl);
843
+ }
844
+ pane.dataset.highlighted = "true";
845
+ },
541
846
  /**
542
847
  * Apply syntax highlighting to HTML
543
848
  * @param {string} html - Escaped HTML string
@@ -545,7 +850,9 @@
545
850
  */
546
851
  highlightHtml: function(html) {
547
852
  html = html.replace(/(&lt;\/?)([\w-]+)/g, '$1<span class="code-tag">$2</span>');
853
+ html = html.replace(/([\w-]+)(=)(["'])/g, '<span class="code-attr">$1</span>$2$3');
548
854
  html = html.replace(/([\w-]+)(=)(&quot;|&#39;)/g, '<span class="code-attr">$1</span>$2$3');
855
+ html = html.replace(/(["'])([^"']*)(["'])/g, '$1<span class="code-string">$2</span>$3');
549
856
  html = html.replace(/(&quot;|&#39;)([^&]*)(&quot;|&#39;)/g, '$1<span class="code-string">$2</span>$3');
550
857
  html = html.replace(/(&lt;!--)(.*?)(--&gt;)/g, '<span class="code-comment">$1$2$3</span>');
551
858
  return html;
@@ -670,9 +977,12 @@
670
977
  /**
671
978
  * Destroy all code snippet instances
672
979
  */
673
- destroyAll: function() {
674
- const snippets = document.querySelectorAll('.vd-code-snippet[data-initialized="true"]');
675
- snippets.forEach((snippet) => this.destroy(snippet));
980
+ destroyAll: function(root) {
981
+ const scope = this.resolveRoot(root);
982
+ const snippets = this.queryWithin(scope, '.vd-code-snippet[data-initialized="true"]');
983
+ snippets.forEach((snippet) => {
984
+ this.destroy(snippet);
985
+ });
676
986
  }
677
987
  };
678
988
  if (typeof window.Vanduo !== "undefined") {
@@ -690,8 +1000,8 @@
690
1000
  /**
691
1001
  * Initialize collapsible components
692
1002
  */
693
- init: function() {
694
- const collapsibles = document.querySelectorAll(".vd-collapsible, .accordion");
1003
+ init: function(root) {
1004
+ const collapsibles = window.Vanduo.queryAll(root, ".vd-collapsible, .accordion");
695
1005
  collapsibles.forEach((container) => {
696
1006
  if (this.instances.has(container)) {
697
1007
  return;
@@ -868,8 +1178,8 @@
868
1178
  /**
869
1179
  * Initialize dropdown components
870
1180
  */
871
- init: function() {
872
- const dropdowns = document.querySelectorAll(".vd-dropdown");
1181
+ init: function(root) {
1182
+ const dropdowns = window.Vanduo.queryAll(root, ".vd-dropdown");
873
1183
  dropdowns.forEach((dropdown) => {
874
1184
  if (this.instances.has(dropdown)) {
875
1185
  return;
@@ -1187,31 +1497,36 @@
1187
1497
  description: "Friendly, rounded sans-serif"
1188
1498
  }
1189
1499
  },
1190
- init: function() {
1500
+ getToggles: function(root) {
1501
+ if (window.Vanduo && typeof window.Vanduo.queryAll === "function") {
1502
+ return window.Vanduo.queryAll(root, '[data-toggle="font"]');
1503
+ }
1504
+ return Array.from(document.querySelectorAll('[data-toggle="font"]'));
1505
+ },
1506
+ init: function(root) {
1191
1507
  this.state = {
1192
1508
  preference: this.getPreference()
1193
1509
  };
1194
1510
  if (!this.fonts[this.state.preference]) {
1195
- this.state.preference = "lato";
1511
+ this.state.preference = "ubuntu";
1196
1512
  this.setStorageValue(this.STORAGE_KEY, this.state.preference);
1197
1513
  }
1198
1514
  if (this.isInitialized) {
1199
1515
  this.applyFont();
1200
- this.renderUI();
1201
- this.updateUI();
1516
+ this.renderUI(root);
1517
+ this.updateUI(root);
1202
1518
  return;
1203
1519
  }
1204
1520
  this.isInitialized = true;
1205
1521
  this.applyFont();
1206
- this.renderUI();
1207
- console.log("Vanduo Font Switcher initialized");
1522
+ this.renderUI(root);
1208
1523
  },
1209
1524
  /**
1210
1525
  * Get saved font preference from localStorage
1211
- * @returns {string} Font key or 'lato' (default)
1526
+ * @returns {string} Font key or 'ubuntu' (default)
1212
1527
  */
1213
1528
  getPreference: function() {
1214
- return this.getStorageValue(this.STORAGE_KEY, "lato");
1529
+ return this.getStorageValue(this.STORAGE_KEY, "ubuntu");
1215
1530
  },
1216
1531
  /**
1217
1532
  * Set font preference and apply it
@@ -1246,8 +1561,8 @@
1246
1561
  /**
1247
1562
  * Initialize UI elements with data-toggle="font"
1248
1563
  */
1249
- renderUI: function() {
1250
- const toggles = document.querySelectorAll('[data-toggle="font"]');
1564
+ renderUI: function(root) {
1565
+ const toggles = this.getToggles(root);
1251
1566
  toggles.forEach((toggle) => {
1252
1567
  if (toggle.getAttribute("data-font-initialized") === "true") {
1253
1568
  if (toggle.tagName === "SELECT") {
@@ -1278,8 +1593,8 @@
1278
1593
  /**
1279
1594
  * Update all UI elements to reflect current state
1280
1595
  */
1281
- updateUI: function() {
1282
- const toggles = document.querySelectorAll('[data-toggle="font"]');
1596
+ updateUI: function(root) {
1597
+ const toggles = this.getToggles(root);
1283
1598
  toggles.forEach((toggle) => {
1284
1599
  if (toggle.tagName === "SELECT") {
1285
1600
  toggle.value = this.state.preference;
@@ -1306,8 +1621,10 @@
1306
1621
  getFontData: function(fontKey) {
1307
1622
  return this.fonts[fontKey] || null;
1308
1623
  },
1309
- destroyAll: function() {
1310
- const toggles = document.querySelectorAll('[data-toggle="font"][data-font-initialized="true"]');
1624
+ destroyAll: function(root) {
1625
+ const toggles = this.getToggles(root || document).filter(function(toggle) {
1626
+ return toggle.getAttribute("data-font-initialized") === "true";
1627
+ });
1311
1628
  toggles.forEach((toggle) => {
1312
1629
  if (toggle._fontToggleHandler) {
1313
1630
  const eventName = toggle.tagName === "SELECT" ? "change" : "click";
@@ -1316,7 +1633,9 @@
1316
1633
  }
1317
1634
  toggle.removeAttribute("data-font-initialized");
1318
1635
  });
1319
- this.isInitialized = false;
1636
+ if (!root || root === document) {
1637
+ this.isInitialized = false;
1638
+ }
1320
1639
  },
1321
1640
  getStorageValue: function(key, fallback) {
1322
1641
  if (typeof window.safeStorageGet === "function") {
@@ -1359,18 +1678,19 @@
1359
1678
  })();
1360
1679
  const GridLayout = {
1361
1680
  instances: /* @__PURE__ */ new Map(),
1681
+ __vanduoScopedDestroyAll: true,
1362
1682
  /**
1363
1683
  * Initialize all grid layout containers
1364
1684
  */
1365
- init: function() {
1366
- const containers = document.querySelectorAll("[data-layout-mode]");
1685
+ init: function(root) {
1686
+ const containers = window.Vanduo.queryAll(root, "[data-layout-mode]");
1367
1687
  containers.forEach(function(container) {
1368
1688
  if (this.instances.has(container)) {
1369
1689
  return;
1370
1690
  }
1371
1691
  this.initContainer(container);
1372
1692
  }.bind(this));
1373
- this.initToggleButtons();
1693
+ this.initToggleButtons(root);
1374
1694
  },
1375
1695
  /**
1376
1696
  * Initialize a single grid container
@@ -1390,8 +1710,8 @@
1390
1710
  /**
1391
1711
  * Initialize toggle buttons that target grid containers
1392
1712
  */
1393
- initToggleButtons: function() {
1394
- const toggleButtons = document.querySelectorAll("[data-grid-toggle]");
1713
+ initToggleButtons: function(root) {
1714
+ const toggleButtons = window.Vanduo && typeof window.Vanduo.queryAll === "function" ? window.Vanduo.queryAll(root, "[data-grid-toggle]") : document.querySelectorAll("[data-grid-toggle]");
1395
1715
  toggleButtons.forEach(function(button) {
1396
1716
  if (button.getAttribute("data-grid-initialized") === "true") {
1397
1717
  return;
@@ -1559,11 +1879,14 @@
1559
1879
  /**
1560
1880
  * Destroy all grid layout instances and clean up toggle buttons
1561
1881
  */
1562
- destroyAll: function() {
1882
+ destroyAll: function(root) {
1883
+ const scope = window.Vanduo && typeof window.Vanduo._normalizeRoot === "function" ? window.Vanduo._normalizeRoot(root) : root || document;
1563
1884
  this.instances.forEach(function(instance, container) {
1564
- this.destroy(container);
1885
+ if (scope === document || scope === container || typeof scope.contains === "function" && scope.contains(container)) {
1886
+ this.destroy(container);
1887
+ }
1565
1888
  }.bind(this));
1566
- const toggleButtons = document.querySelectorAll('[data-grid-initialized="true"]');
1889
+ const toggleButtons = window.Vanduo && typeof window.Vanduo.queryAll === "function" ? window.Vanduo.queryAll(scope, '[data-grid-toggle][data-grid-initialized="true"]') : document.querySelectorAll('[data-grid-initialized="true"]');
1567
1890
  toggleButtons.forEach(function(button) {
1568
1891
  if (button._gridCleanup) {
1569
1892
  button._gridCleanup();
@@ -1593,12 +1916,18 @@
1593
1916
  isOpen: false,
1594
1917
  // Store cleanup functions for event listeners
1595
1918
  _cleanupFunctions: [],
1919
+ getTriggers: function(root) {
1920
+ if (window.Vanduo && typeof window.Vanduo.queryAll === "function") {
1921
+ return window.Vanduo.queryAll(root, "[data-image-box]");
1922
+ }
1923
+ return Array.from(document.querySelectorAll("[data-image-box]"));
1924
+ },
1596
1925
  /**
1597
1926
  * Initialize Image Box component
1598
1927
  */
1599
- init: function() {
1928
+ init: function(root) {
1600
1929
  this.createBackdrop();
1601
- this.bindTriggers();
1930
+ this.bindTriggers(root);
1602
1931
  },
1603
1932
  /**
1604
1933
  * Create backdrop elements
@@ -1682,9 +2011,9 @@
1682
2011
  /**
1683
2012
  * Bind triggers to all images with data-image-box attribute
1684
2013
  */
1685
- bindTriggers: function() {
2014
+ bindTriggers: function(root) {
1686
2015
  const self = this;
1687
- const triggers = document.querySelectorAll("[data-image-box]");
2016
+ const triggers = this.getTriggers(root);
1688
2017
  triggers.forEach(function(trigger) {
1689
2018
  if (trigger.dataset.imageBoxInitialized) return;
1690
2019
  trigger.dataset.imageBoxInitialized = "true";
@@ -1701,6 +2030,8 @@
1701
2030
  trigger.classList.remove("is-broken");
1702
2031
  };
1703
2032
  trigger.addEventListener("load", loadHandler);
2033
+ trigger._imageBoxErrorHandler = errorHandler;
2034
+ trigger._imageBoxLoadHandler = loadHandler;
1704
2035
  }
1705
2036
  const clickHandler = function(e) {
1706
2037
  e.preventDefault();
@@ -1719,12 +2050,24 @@
1719
2050
  }
1720
2051
  };
1721
2052
  trigger.addEventListener("keydown", keyHandler);
1722
- const originalCleanup = trigger._imageBoxCleanup;
2053
+ const originalCleanup2 = trigger._imageBoxCleanup;
1723
2054
  trigger._imageBoxCleanup = () => {
1724
- originalCleanup();
2055
+ originalCleanup2();
1725
2056
  trigger.removeEventListener("keydown", keyHandler);
1726
2057
  };
1727
2058
  }
2059
+ const originalCleanup = trigger._imageBoxCleanup;
2060
+ trigger._imageBoxCleanup = () => {
2061
+ originalCleanup();
2062
+ if (trigger._imageBoxErrorHandler) {
2063
+ trigger.removeEventListener("error", trigger._imageBoxErrorHandler);
2064
+ delete trigger._imageBoxErrorHandler;
2065
+ }
2066
+ if (trigger._imageBoxLoadHandler) {
2067
+ trigger.removeEventListener("load", trigger._imageBoxLoadHandler);
2068
+ delete trigger._imageBoxLoadHandler;
2069
+ }
2070
+ };
1728
2071
  });
1729
2072
  },
1730
2073
  /**
@@ -1795,13 +2138,25 @@
1795
2138
  /**
1796
2139
  * Reinitialize - useful after dynamic DOM changes
1797
2140
  */
1798
- reinit: function() {
1799
- this.bindTriggers();
2141
+ reinit: function(root) {
2142
+ this.bindTriggers(root);
1800
2143
  },
1801
2144
  /**
1802
2145
  * Destroy component and clean up
1803
2146
  */
1804
- destroy: function() {
2147
+ destroy: function(root) {
2148
+ if (root && root !== document) {
2149
+ const triggersInRoot = this.getTriggers(root);
2150
+ triggersInRoot.forEach((trigger) => {
2151
+ trigger.classList.remove("vd-image-box-trigger");
2152
+ if (trigger._imageBoxCleanup) {
2153
+ trigger._imageBoxCleanup();
2154
+ delete trigger._imageBoxCleanup;
2155
+ }
2156
+ delete trigger.dataset.imageBoxInitialized;
2157
+ });
2158
+ return;
2159
+ }
1805
2160
  if (this.isOpen) {
1806
2161
  this.close();
1807
2162
  }
@@ -1810,7 +2165,7 @@
1810
2165
  }
1811
2166
  this._cleanupFunctions.forEach((fn) => fn());
1812
2167
  this._cleanupFunctions = [];
1813
- const triggers = document.querySelectorAll("[data-image-box-initialized]");
2168
+ const triggers = window.Vanduo && typeof window.Vanduo.queryAll === "function" ? window.Vanduo.queryAll(root, "[data-image-box-initialized]") : document.querySelectorAll("[data-image-box-initialized]");
1814
2169
  triggers.forEach((trigger) => {
1815
2170
  trigger.classList.remove("vd-image-box-trigger");
1816
2171
  if (trigger._imageBoxCleanup) {
@@ -1827,8 +2182,8 @@
1827
2182
  this.currentTrigger = null;
1828
2183
  this.isOpen = false;
1829
2184
  },
1830
- destroyAll: function() {
1831
- this.destroy();
2185
+ destroyAll: function(root) {
2186
+ this.destroy(root);
1832
2187
  }
1833
2188
  };
1834
2189
  if (typeof window.Vanduo !== "undefined") {
@@ -1844,6 +2199,7 @@
1844
2199
  modals: /* @__PURE__ */ new Map(),
1845
2200
  openModals: [],
1846
2201
  zIndexCounter: 1050,
2202
+ __vanduoScopedDestroyAll: true,
1847
2203
  // Store trigger cleanup functions
1848
2204
  _triggerCleanups: [],
1849
2205
  // Shared ESC key handler (installed once)
@@ -1899,15 +2255,15 @@
1899
2255
  /**
1900
2256
  * Initialize modals
1901
2257
  */
1902
- init: function() {
1903
- const modals = document.querySelectorAll(".vd-modal");
2258
+ init: function(root) {
2259
+ const modals = window.Vanduo.queryAll(root, ".vd-modal");
1904
2260
  modals.forEach((modal) => {
1905
2261
  if (this.modals.has(modal)) {
1906
2262
  return;
1907
2263
  }
1908
2264
  this.initModal(modal);
1909
2265
  });
1910
- const triggers = document.querySelectorAll("[data-modal]");
2266
+ const triggers = window.Vanduo && typeof window.Vanduo.queryAll === "function" ? window.Vanduo.queryAll(root, "[data-modal]") : document.querySelectorAll("[data-modal]");
1911
2267
  triggers.forEach((trigger) => {
1912
2268
  if (trigger.dataset.modalTriggerInitialized) return;
1913
2269
  trigger.dataset.modalTriggerInitialized = "true";
@@ -1920,7 +2276,7 @@
1920
2276
  }
1921
2277
  };
1922
2278
  trigger.addEventListener("click", triggerClickHandler);
1923
- this._triggerCleanups.push(() => trigger.removeEventListener("click", triggerClickHandler));
2279
+ trigger._modalTriggerCleanup = () => trigger.removeEventListener("click", triggerClickHandler);
1924
2280
  });
1925
2281
  },
1926
2282
  /**
@@ -2158,13 +2514,22 @@
2158
2514
  /**
2159
2515
  * Destroy all modal instances
2160
2516
  */
2161
- destroyAll: function() {
2517
+ destroyAll: function(root) {
2518
+ const scope = window.Vanduo && typeof window.Vanduo._normalizeRoot === "function" ? window.Vanduo._normalizeRoot(root) : root || document;
2162
2519
  this.modals.forEach((data, modal) => {
2163
- this.destroy(modal);
2520
+ if (scope === document || scope === modal || typeof scope.contains === "function" && scope.contains(modal)) {
2521
+ this.destroy(modal);
2522
+ }
2164
2523
  });
2165
- this._triggerCleanups.forEach((fn) => fn());
2166
- this._triggerCleanups = [];
2167
- if (this._sharedEscHandler) {
2524
+ const triggers = window.Vanduo && typeof window.Vanduo.queryAll === "function" ? window.Vanduo.queryAll(scope, "[data-modal][data-modal-trigger-initialized]") : document.querySelectorAll("[data-modal][data-modal-trigger-initialized]");
2525
+ triggers.forEach((trigger) => {
2526
+ if (trigger._modalTriggerCleanup) {
2527
+ trigger._modalTriggerCleanup();
2528
+ delete trigger._modalTriggerCleanup;
2529
+ }
2530
+ delete trigger.dataset.modalTriggerInitialized;
2531
+ });
2532
+ if (scope === document && this._sharedEscHandler) {
2168
2533
  document.removeEventListener("keydown", this._sharedEscHandler);
2169
2534
  this._sharedEscHandler = null;
2170
2535
  }
@@ -2195,8 +2560,8 @@
2195
2560
  /**
2196
2561
  * Initialize navbar component
2197
2562
  */
2198
- init: function() {
2199
- const navbars = document.querySelectorAll(".vd-navbar");
2563
+ init: function(root) {
2564
+ const navbars = window.Vanduo.queryAll(root, ".vd-navbar");
2200
2565
  navbars.forEach((navbar) => {
2201
2566
  if (this.instances.has(navbar)) {
2202
2567
  return;
@@ -2417,8 +2782,8 @@
2417
2782
  /**
2418
2783
  * Initialize pagination components
2419
2784
  */
2420
- init: function() {
2421
- const paginations = document.querySelectorAll(".vd-pagination[data-pagination]");
2785
+ init: function(root) {
2786
+ const paginations = window.Vanduo.queryAll(root, ".vd-pagination[data-pagination]");
2422
2787
  paginations.forEach((pagination) => {
2423
2788
  if (this.instances.has(pagination)) {
2424
2789
  return;
@@ -2641,21 +3006,21 @@
2641
3006
  /**
2642
3007
  * Initialize parallax components
2643
3008
  */
2644
- init: function() {
2645
- if (this.isInitialized) {
2646
- this.refresh();
2647
- return;
2648
- }
2649
- this.isInitialized = true;
3009
+ init: function(root) {
2650
3010
  if (this.reducedMotion) {
2651
3011
  return;
2652
3012
  }
2653
- const parallaxElements = document.querySelectorAll(".vd-parallax");
3013
+ const parallaxElements = window.Vanduo.queryAll(root, ".vd-parallax");
2654
3014
  parallaxElements.forEach((element) => {
2655
3015
  if (!element.dataset.parallaxInitialized) {
2656
3016
  this.initParallax(element);
2657
3017
  }
2658
3018
  });
3019
+ if (this.isInitialized) {
3020
+ this.refresh();
3021
+ return;
3022
+ }
3023
+ this.isInitialized = true;
2659
3024
  this.handleScroll();
2660
3025
  this._onScroll = () => {
2661
3026
  this.handleScroll();
@@ -2798,11 +3163,17 @@
2798
3163
  (function() {
2799
3164
  "use strict";
2800
3165
  const Preloader = {
3166
+ getProgressBars: function(root) {
3167
+ if (window.Vanduo && typeof window.Vanduo.queryAll === "function") {
3168
+ return window.Vanduo.queryAll(root, ".vd-progress-bar[data-progress], .progress-bar[data-progress]");
3169
+ }
3170
+ return Array.from(document.querySelectorAll(".vd-progress-bar[data-progress], .progress-bar[data-progress]"));
3171
+ },
2801
3172
  /**
2802
3173
  * Initialize preloader components
2803
3174
  */
2804
- init: function() {
2805
- const progressBars = document.querySelectorAll(".vd-progress-bar[data-progress], .progress-bar[data-progress]");
3175
+ init: function(root) {
3176
+ const progressBars = this.getProgressBars(root);
2806
3177
  progressBars.forEach((bar) => {
2807
3178
  if (!bar.dataset.progressInitialized) {
2808
3179
  this.initProgressBar(bar);
@@ -2922,8 +3293,10 @@
2922
3293
  /**
2923
3294
  * Destroy all progress bar instances
2924
3295
  */
2925
- destroyAll: function() {
2926
- const progressBars = document.querySelectorAll('.vd-progress-bar[data-progress-initialized="true"], .progress-bar[data-progress-initialized="true"]');
3296
+ destroyAll: function(root) {
3297
+ const progressBars = this.getProgressBars(root || document).filter(function(bar) {
3298
+ return bar.dataset.progressInitialized === "true";
3299
+ });
2927
3300
  progressBars.forEach((bar) => {
2928
3301
  delete bar.dataset.progressInitialized;
2929
3302
  });
@@ -2944,8 +3317,8 @@
2944
3317
  /**
2945
3318
  * Initialize select components
2946
3319
  */
2947
- init: function() {
2948
- const selects = document.querySelectorAll("select.vd-custom-select-input, select[data-custom-select]");
3320
+ init: function(root) {
3321
+ const selects = window.Vanduo.queryAll(root, "select.vd-custom-select-input, select[data-custom-select]");
2949
3322
  selects.forEach((select) => {
2950
3323
  if (this.instances.has(select)) {
2951
3324
  return;
@@ -3306,8 +3679,10 @@
3306
3679
  breakpoint: 992,
3307
3680
  // Desktop breakpoint
3308
3681
  restoreDelayMs: 450,
3682
+ __vanduoScopedDestroyAll: true,
3309
3683
  // Global cleanup functions (toggles, resize)
3310
3684
  _globalCleanups: [],
3685
+ _resizeCleanup: null,
3311
3686
  isFixedVariant: function(sidenav) {
3312
3687
  return sidenav.classList.contains("vd-sidenav-fixed") || sidenav.classList.contains("sidenav-fixed");
3313
3688
  },
@@ -3408,15 +3783,15 @@
3408
3783
  /**
3409
3784
  * Initialize sidenav components
3410
3785
  */
3411
- init: function() {
3412
- const sidenavs = document.querySelectorAll(".vd-sidenav, .vd-offcanvas");
3786
+ init: function(root) {
3787
+ const sidenavs = window.Vanduo.queryAll(root, ".vd-sidenav, .vd-offcanvas");
3413
3788
  sidenavs.forEach((sidenav) => {
3414
3789
  if (this.sidenavs.has(sidenav)) {
3415
3790
  return;
3416
3791
  }
3417
3792
  this.initSidenav(sidenav);
3418
3793
  });
3419
- const toggles = document.querySelectorAll("[data-sidenav-toggle]");
3794
+ const toggles = window.Vanduo && typeof window.Vanduo.queryAll === "function" ? window.Vanduo.queryAll(root, "[data-sidenav-toggle]") : document.querySelectorAll("[data-sidenav-toggle]");
3420
3795
  toggles.forEach((toggle) => {
3421
3796
  if (toggle.dataset.sidenavToggleInitialized) return;
3422
3797
  toggle.dataset.sidenavToggleInitialized = "true";
@@ -3429,14 +3804,16 @@
3429
3804
  }
3430
3805
  };
3431
3806
  toggle.addEventListener("click", toggleClickHandler);
3432
- this._globalCleanups.push(() => toggle.removeEventListener("click", toggleClickHandler));
3807
+ toggle._sidenavToggleCleanup = () => toggle.removeEventListener("click", toggleClickHandler);
3433
3808
  });
3434
3809
  this.handleResize();
3435
- const resizeHandler = () => {
3436
- this.handleResize();
3437
- };
3438
- window.addEventListener("resize", resizeHandler);
3439
- this._globalCleanups.push(() => window.removeEventListener("resize", resizeHandler));
3810
+ if (!this._resizeCleanup) {
3811
+ const resizeHandler = () => {
3812
+ this.handleResize();
3813
+ };
3814
+ window.addEventListener("resize", resizeHandler);
3815
+ this._resizeCleanup = () => window.removeEventListener("resize", resizeHandler);
3816
+ }
3440
3817
  },
3441
3818
  /**
3442
3819
  * Initialize a single sidenav
@@ -3609,12 +3986,29 @@
3609
3986
  /**
3610
3987
  * Destroy all sidenav instances
3611
3988
  */
3612
- destroyAll: function() {
3989
+ destroyAll: function(root) {
3990
+ const scope = window.Vanduo && typeof window.Vanduo._normalizeRoot === "function" ? window.Vanduo._normalizeRoot(root) : root || document;
3613
3991
  this.sidenavs.forEach((data, sidenav) => {
3614
- this.destroy(sidenav);
3992
+ if (scope === document || scope === sidenav || typeof scope.contains === "function" && scope.contains(sidenav)) {
3993
+ this.destroy(sidenav);
3994
+ }
3615
3995
  });
3616
- this._globalCleanups.forEach((fn) => fn());
3617
- this._globalCleanups = [];
3996
+ const toggles = window.Vanduo && typeof window.Vanduo.queryAll === "function" ? window.Vanduo.queryAll(scope, "[data-sidenav-toggle][data-sidenav-toggle-initialized]") : document.querySelectorAll("[data-sidenav-toggle][data-sidenav-toggle-initialized]");
3997
+ toggles.forEach((toggle) => {
3998
+ if (toggle._sidenavToggleCleanup) {
3999
+ toggle._sidenavToggleCleanup();
4000
+ delete toggle._sidenavToggleCleanup;
4001
+ }
4002
+ delete toggle.dataset.sidenavToggleInitialized;
4003
+ });
4004
+ if (scope === document) {
4005
+ if (this._resizeCleanup) {
4006
+ this._resizeCleanup();
4007
+ this._resizeCleanup = null;
4008
+ }
4009
+ this._globalCleanups.forEach((fn) => fn());
4010
+ this._globalCleanups = [];
4011
+ }
3618
4012
  }
3619
4013
  };
3620
4014
  if (typeof window.Vanduo !== "undefined") {
@@ -3632,8 +4026,8 @@
3632
4026
  /**
3633
4027
  * Initialize all tab components
3634
4028
  */
3635
- init: function() {
3636
- const tabContainers = document.querySelectorAll(".vd-tabs, [data-tabs]");
4029
+ init: function(root) {
4030
+ const tabContainers = window.Vanduo.queryAll(root, ".vd-tabs, [data-tabs]");
3637
4031
  tabContainers.forEach((container) => {
3638
4032
  if (this.instances.has(container)) {
3639
4033
  return;
@@ -3879,9 +4273,9 @@
3879
4273
  DEFAULTS: {
3880
4274
  PRIMARY_LIGHT: "black",
3881
4275
  PRIMARY_DARK: "amber",
3882
- NEUTRAL: "neutral",
4276
+ NEUTRAL: "charcoal",
3883
4277
  RADIUS: "0.5",
3884
- FONT: "lato",
4278
+ FONT: "ubuntu",
3885
4279
  THEME: "system"
3886
4280
  },
3887
4281
  // Primary color definitions (Open Color based)
@@ -3907,6 +4301,7 @@
3907
4301
  },
3908
4302
  // Neutral color definitions
3909
4303
  NEUTRAL_COLORS: {
4304
+ "charcoal": { name: "Charcoal", color: "#0d1117" },
3910
4305
  "slate": { name: "Slate", color: "#64748b" },
3911
4306
  "gray": { name: "Gray", color: "#6b7280" },
3912
4307
  "zinc": { name: "Zinc", color: "#71717a" },
@@ -3936,21 +4331,65 @@
3936
4331
  },
3937
4332
  isInitialized: false,
3938
4333
  _cleanup: [],
4334
+ _ownsDynamicPanel: false,
3939
4335
  // DOM references
3940
4336
  elements: {
3941
4337
  customizer: null,
3942
4338
  trigger: null,
4339
+ activeTrigger: null,
3943
4340
  triggers: [],
3944
4341
  panel: null,
3945
4342
  overlay: null
3946
4343
  },
4344
+ isRoot: function(root) {
4345
+ return !!root && (root === document || root.nodeType === 1 || root.nodeType === 9 || root.nodeType === 11);
4346
+ },
4347
+ normalizeRoot: function(root) {
4348
+ return this.isRoot(root) ? root : document;
4349
+ },
4350
+ queryAll: function(root, selector) {
4351
+ const scope = this.normalizeRoot(root);
4352
+ if (typeof window.VanduoLifecycle !== "undefined" && typeof window.VanduoLifecycle.queryAll === "function") {
4353
+ return window.VanduoLifecycle.queryAll(scope, selector);
4354
+ }
4355
+ const matches = [];
4356
+ if (scope instanceof Element && typeof scope.matches === "function" && scope.matches(selector)) {
4357
+ matches.push(scope);
4358
+ }
4359
+ if (typeof scope.querySelectorAll === "function") {
4360
+ const descendants = scope.querySelectorAll(selector);
4361
+ for (let i = 0; i < descendants.length; i++) {
4362
+ matches.push(descendants[i]);
4363
+ }
4364
+ }
4365
+ return matches;
4366
+ },
4367
+ queryOne: function(root, selector) {
4368
+ const matches = this.queryAll(root, selector);
4369
+ return matches.length ? matches[0] : null;
4370
+ },
4371
+ getTriggers: function(root) {
4372
+ return this.queryAll(root, "[data-theme-customizer-trigger]");
4373
+ },
4374
+ pruneTriggers: function() {
4375
+ this.elements.triggers = this.elements.triggers.filter(function(trigger) {
4376
+ return trigger && trigger.isConnected;
4377
+ });
4378
+ if (this.elements.trigger && !this.elements.trigger.isConnected) {
4379
+ this.elements.trigger = null;
4380
+ }
4381
+ if (this.elements.activeTrigger && !this.elements.activeTrigger.isConnected) {
4382
+ this.elements.activeTrigger = null;
4383
+ }
4384
+ },
3947
4385
  /**
3948
4386
  * Initialize the Theme Customizer
3949
4387
  */
3950
- init: function() {
4388
+ init: function(root) {
4389
+ const scope = this.normalizeRoot(root);
3951
4390
  if (this.isInitialized) {
3952
- this.bindExistingElements();
3953
- this.bindTriggerEvents();
4391
+ this.bindExistingElements(scope);
4392
+ this.bindTriggerEvents(scope);
3954
4393
  this.bindPanelEvents();
3955
4394
  this.updateUI();
3956
4395
  return;
@@ -3959,9 +4398,8 @@
3959
4398
  this._cleanup = [];
3960
4399
  this.loadPreferences();
3961
4400
  this.applyAllPreferences();
3962
- this.bindExistingElements();
3963
- this.bindEvents();
3964
- console.log("Vanduo Theme Customizer initialized");
4401
+ this.bindExistingElements(scope);
4402
+ this.bindEvents(scope);
3965
4403
  },
3966
4404
  addListener: function(target, event, handler, options) {
3967
4405
  if (!target) return;
@@ -4122,18 +4560,26 @@
4122
4560
  /**
4123
4561
  * Bind to existing DOM elements or create them dynamically
4124
4562
  */
4125
- bindExistingElements: function() {
4126
- this.elements.customizer = document.querySelector(".vd-theme-customizer");
4127
- this.elements.triggers = Array.from(document.querySelectorAll("[data-theme-customizer-trigger]"));
4563
+ bindExistingElements: function(root) {
4564
+ const scope = this.normalizeRoot(root);
4565
+ this.pruneTriggers();
4566
+ const scopedTriggers = this.getTriggers(scope);
4567
+ scopedTriggers.forEach((trigger) => {
4568
+ if (!this.elements.triggers.includes(trigger)) {
4569
+ this.elements.triggers.push(trigger);
4570
+ }
4571
+ });
4128
4572
  if (!this.elements.trigger && this.elements.triggers.length) {
4129
4573
  this.elements.trigger = this.elements.triggers[0];
4130
4574
  }
4131
- if (this.elements.customizer) {
4575
+ const existingCustomizer = this.queryOne(scope, ".vd-theme-customizer") || (this.elements.customizer && typeof this.elements.customizer.contains === "function" ? this.elements.customizer : null) || document.querySelector(".vd-theme-customizer");
4576
+ if (existingCustomizer instanceof Element) {
4577
+ this.elements.customizer = existingCustomizer;
4132
4578
  this.elements.trigger = this.elements.customizer.querySelector(".vd-theme-customizer-trigger") || this.elements.trigger;
4133
4579
  this.elements.panel = this.elements.customizer.querySelector(".vd-theme-customizer-panel");
4134
4580
  this.elements.overlay = this.elements.customizer.querySelector(".vd-theme-customizer-overlay");
4135
4581
  } else {
4136
- if (this.elements.triggers.length) {
4582
+ if (scopedTriggers.length && !this.elements.panel) {
4137
4583
  this.createDynamicPanel();
4138
4584
  }
4139
4585
  }
@@ -4143,7 +4589,7 @@
4143
4589
  * Create the panel dynamically when only a trigger button exists
4144
4590
  */
4145
4591
  createDynamicPanel: function() {
4146
- if (!this.elements.triggers.length) {
4592
+ if (!this.elements.triggers.length || this.elements.panel && this.elements.panel.isConnected) {
4147
4593
  return;
4148
4594
  }
4149
4595
  this.elements.trigger = this.elements.triggers[0];
@@ -4156,6 +4602,7 @@
4156
4602
  document.body.appendChild(panel);
4157
4603
  this.elements.panel = panel;
4158
4604
  this.elements.overlay = overlay;
4605
+ this._ownsDynamicPanel = true;
4159
4606
  this.elements.customizer = {
4160
4607
  contains: (el) => panel.contains(el) || this.elements.triggers.some((trigger) => trigger.contains(el))
4161
4608
  };
@@ -4338,8 +4785,8 @@
4338
4785
  this.state.primary = expected;
4339
4786
  }
4340
4787
  },
4341
- bindEvents: function() {
4342
- this.bindTriggerEvents();
4788
+ bindEvents: function(root) {
4789
+ this.bindTriggerEvents(root);
4343
4790
  this.bindPanelEvents();
4344
4791
  if (window.matchMedia) {
4345
4792
  const mq = window.matchMedia("(prefers-color-scheme: dark)");
@@ -4366,21 +4813,44 @@
4366
4813
  }
4367
4814
  });
4368
4815
  },
4369
- bindTriggerEvents: function() {
4370
- this.elements.triggers.forEach((trigger) => {
4816
+ bindTriggerEvents: function(root) {
4817
+ const triggers = root ? this.getTriggers(root) : this.elements.triggers;
4818
+ triggers.forEach((trigger) => {
4371
4819
  if (trigger.getAttribute("data-customizer-trigger-initialized") === "true") {
4372
4820
  return;
4373
4821
  }
4374
- this.addListener(trigger, "click", (e) => {
4822
+ const onClick = (e) => {
4375
4823
  e.preventDefault();
4376
4824
  e.stopPropagation();
4377
4825
  this.elements.activeTrigger = trigger;
4378
4826
  this.elements.trigger = trigger;
4379
4827
  this.toggle();
4380
- });
4828
+ };
4829
+ trigger.addEventListener("click", onClick);
4830
+ trigger._themeCustomizerTriggerHandler = onClick;
4381
4831
  trigger.setAttribute("data-customizer-trigger-initialized", "true");
4382
4832
  });
4383
4833
  },
4834
+ cleanupTrigger: function(trigger) {
4835
+ if (!trigger || trigger.getAttribute("data-customizer-trigger-initialized") !== "true") {
4836
+ return;
4837
+ }
4838
+ if (trigger._themeCustomizerTriggerHandler) {
4839
+ trigger.removeEventListener("click", trigger._themeCustomizerTriggerHandler);
4840
+ delete trigger._themeCustomizerTriggerHandler;
4841
+ }
4842
+ trigger.setAttribute("aria-expanded", "false");
4843
+ trigger.removeAttribute("data-customizer-trigger-initialized");
4844
+ if (this.elements.activeTrigger === trigger) {
4845
+ this.elements.activeTrigger = null;
4846
+ }
4847
+ this.elements.triggers = this.elements.triggers.filter(function(candidate) {
4848
+ return candidate !== trigger;
4849
+ });
4850
+ if (this.elements.trigger === trigger) {
4851
+ this.elements.trigger = this.elements.triggers[0] || null;
4852
+ }
4853
+ },
4384
4854
  /**
4385
4855
  * Toggle panel open/close
4386
4856
  */
@@ -4491,13 +4961,42 @@
4491
4961
  return false;
4492
4962
  }
4493
4963
  },
4494
- destroyAll: function() {
4964
+ destroyAll: function(root) {
4965
+ const scope = this.normalizeRoot(root);
4966
+ if (scope !== document) {
4967
+ this.getTriggers(scope).forEach((trigger) => {
4968
+ this.cleanupTrigger(trigger);
4969
+ });
4970
+ this.pruneTriggers();
4971
+ if (!this.elements.triggers.length) {
4972
+ this.destroyAll(document);
4973
+ }
4974
+ return;
4975
+ }
4495
4976
  this._cleanup.forEach((fn) => fn());
4496
4977
  this._cleanup = [];
4497
4978
  if (this.elements.panel) {
4498
4979
  this.elements.panel.removeAttribute("data-customizer-initialized");
4499
4980
  }
4981
+ this.elements.triggers.slice().forEach((trigger) => {
4982
+ this.cleanupTrigger(trigger);
4983
+ });
4984
+ if (this._ownsDynamicPanel) {
4985
+ if (this.elements.panel && this.elements.panel.parentNode) {
4986
+ this.elements.panel.parentNode.removeChild(this.elements.panel);
4987
+ }
4988
+ if (this.elements.overlay && this.elements.overlay.parentNode) {
4989
+ this.elements.overlay.parentNode.removeChild(this.elements.overlay);
4990
+ }
4991
+ this._ownsDynamicPanel = false;
4992
+ }
4500
4993
  this.close();
4994
+ this.elements.customizer = null;
4995
+ this.elements.trigger = null;
4996
+ this.elements.activeTrigger = null;
4997
+ this.elements.triggers = [];
4998
+ this.elements.panel = null;
4999
+ this.elements.overlay = null;
4501
5000
  this.isInitialized = false;
4502
5001
  }
4503
5002
  };
@@ -4514,7 +5013,13 @@
4514
5013
  isInitialized: false,
4515
5014
  _mediaQuery: null,
4516
5015
  _onMediaChange: null,
4517
- init: function() {
5016
+ getToggles: function(root) {
5017
+ if (window.Vanduo && typeof window.Vanduo.queryAll === "function") {
5018
+ return window.Vanduo.queryAll(root, '[data-toggle="theme"]');
5019
+ }
5020
+ return Array.from(document.querySelectorAll('[data-toggle="theme"]'));
5021
+ },
5022
+ init: function(root) {
4518
5023
  this.STORAGE_KEY = "vanduo-theme-preference";
4519
5024
  this.state = {
4520
5025
  preference: this.getPreference()
@@ -4522,15 +5027,14 @@
4522
5027
  };
4523
5028
  if (this.isInitialized) {
4524
5029
  this.applyTheme();
4525
- this.renderUI();
4526
- this.updateUI();
5030
+ this.renderUI(root);
5031
+ this.updateUI(root);
4527
5032
  return;
4528
5033
  }
4529
5034
  this.isInitialized = true;
4530
5035
  this.applyTheme();
4531
5036
  this.listenForSystemChanges();
4532
- this.renderUI();
4533
- console.log("Vanduo Theme Switcher initialized");
5037
+ this.renderUI(root);
4534
5038
  },
4535
5039
  getPreference: function() {
4536
5040
  return this.getStorageValue(this.STORAGE_KEY, "system");
@@ -4590,8 +5094,8 @@
4590
5094
  this._mediaQuery.addEventListener("change", this._onMediaChange);
4591
5095
  },
4592
5096
  // Helper to facilitate UI creation if needed, though often UI is in HTML
4593
- renderUI: function() {
4594
- const toggles = document.querySelectorAll('[data-toggle="theme"]');
5097
+ renderUI: function(root) {
5098
+ const toggles = this.getToggles(root);
4595
5099
  toggles.forEach((toggle) => {
4596
5100
  if (toggle.getAttribute("data-theme-initialized") === "true") {
4597
5101
  if (toggle.tagName === "SELECT") {
@@ -4618,8 +5122,8 @@
4618
5122
  toggle.setAttribute("data-theme-initialized", "true");
4619
5123
  });
4620
5124
  },
4621
- updateUI: function() {
4622
- const toggles = document.querySelectorAll('[data-toggle="theme"]');
5125
+ updateUI: function(root) {
5126
+ const toggles = this.getToggles(root);
4623
5127
  toggles.forEach((toggle) => {
4624
5128
  if (toggle.tagName === "SELECT") {
4625
5129
  toggle.value = this.state.preference;
@@ -4631,8 +5135,11 @@
4631
5135
  }
4632
5136
  });
4633
5137
  },
4634
- destroyAll: function() {
4635
- const toggles = document.querySelectorAll('[data-toggle="theme"][data-theme-initialized="true"]');
5138
+ destroyAll: function(root) {
5139
+ const scope = root || document;
5140
+ const toggles = this.getToggles(scope).filter(function(toggle) {
5141
+ return toggle.getAttribute("data-theme-initialized") === "true";
5142
+ });
4636
5143
  toggles.forEach((toggle) => {
4637
5144
  if (toggle._themeToggleHandler) {
4638
5145
  const eventName = toggle.tagName === "SELECT" ? "change" : "click";
@@ -4641,12 +5148,14 @@
4641
5148
  }
4642
5149
  toggle.removeAttribute("data-theme-initialized");
4643
5150
  });
4644
- if (this._mediaQuery && this._onMediaChange) {
5151
+ if (scope === document && this._mediaQuery && this._onMediaChange) {
4645
5152
  this._mediaQuery.removeEventListener("change", this._onMediaChange);
4646
5153
  }
4647
- this._mediaQuery = null;
4648
- this._onMediaChange = null;
4649
- this.isInitialized = false;
5154
+ if (scope === document) {
5155
+ this._mediaQuery = null;
5156
+ this._onMediaChange = null;
5157
+ this.isInitialized = false;
5158
+ }
4650
5159
  }
4651
5160
  };
4652
5161
  if (window.Vanduo) {
@@ -4723,7 +5232,7 @@
4723
5232
  let html = "";
4724
5233
  if (config.icon) {
4725
5234
  const allowSvg = config.iconAllowSvg === true;
4726
- const safeIcon = typeof sanitizeHtml === "function" ? sanitizeHtml(config.icon, { allowSvg }) : escapeHtml(config.icon);
5235
+ const safeIcon = typeof sanitizeHtml === "function" ? sanitizeHtml(config.icon, { allowSvg, allowStyle: false }) : escapeHtml(config.icon);
4727
5236
  html += `<span class="vd-toast-icon">${safeIcon}</span>`;
4728
5237
  } else if (config.type) {
4729
5238
  html += `<span class="vd-toast-icon">${this.getDefaultIcon(config.type)}</span>`;
@@ -4943,8 +5452,8 @@
4943
5452
  /**
4944
5453
  * Initialize tooltips
4945
5454
  */
4946
- init: function() {
4947
- const elements = document.querySelectorAll("[data-tooltip], [data-tooltip-html]");
5455
+ init: function(root) {
5456
+ const elements = window.Vanduo.queryAll(root, "[data-tooltip], [data-tooltip-html]");
4948
5457
  elements.forEach((element) => {
4949
5458
  if (this.tooltips.has(element)) {
4950
5459
  return;
@@ -5000,7 +5509,7 @@
5000
5509
  const textContent = element.dataset.tooltip;
5001
5510
  if (htmlContent) {
5002
5511
  const allowSvg = element.hasAttribute("data-tooltip-allow-svg");
5003
- tooltip.innerHTML = this.sanitizeHtml(htmlContent, { allowSvg });
5512
+ tooltip.innerHTML = this.sanitizeHtml(htmlContent, { allowSvg, allowStyle: false });
5004
5513
  tooltip.classList.add("vd-tooltip-html");
5005
5514
  } else if (textContent) {
5006
5515
  tooltip.textContent = textContent;
@@ -5133,7 +5642,7 @@
5133
5642
  const { tooltip } = this.tooltips.get(el);
5134
5643
  if (isHtml) {
5135
5644
  const allowSvg = el.hasAttribute("data-tooltip-allow-svg");
5136
- tooltip.innerHTML = this.sanitizeHtml(content, { allowSvg });
5645
+ tooltip.innerHTML = this.sanitizeHtml(content, { allowSvg, allowStyle: false });
5137
5646
  tooltip.classList.add("vd-tooltip-html");
5138
5647
  } else {
5139
5648
  tooltip.textContent = content;
@@ -5223,8 +5732,26 @@
5223
5732
  emptyText: "Try different keywords or check spelling",
5224
5733
  placeholder: "Search..."
5225
5734
  };
5735
+ const ALLOWED_HIGHLIGHT_TAGS = {
5736
+ mark: true,
5737
+ span: true,
5738
+ strong: true,
5739
+ em: true
5740
+ };
5741
+ function isRoot(value) {
5742
+ return typeof window.VanduoLifecycle !== "undefined" && window.VanduoLifecycle.isRoot(value);
5743
+ }
5744
+ function normalizeRoot(root) {
5745
+ return isRoot(root) ? root : document;
5746
+ }
5747
+ function normalizeHighlightTag(tagName) {
5748
+ const normalized = typeof tagName === "string" ? tagName.toLowerCase() : "mark";
5749
+ return ALLOWED_HIGHLIGHT_TAGS[normalized] ? normalized : "mark";
5750
+ }
5226
5751
  function createSearch(options) {
5227
5752
  const config = Object.assign({}, DEFAULTS, options || {});
5753
+ config.root = normalizeRoot(config.root);
5754
+ config.highlightTag = normalizeHighlightTag(config.highlightTag);
5228
5755
  const state = {
5229
5756
  initialized: false,
5230
5757
  index: [],
@@ -5238,6 +5765,24 @@
5238
5765
  debounceTimer: null,
5239
5766
  boundHandlers: {}
5240
5767
  };
5768
+ function queryAll(selector) {
5769
+ if (window.Vanduo && typeof window.Vanduo.queryAll === "function") {
5770
+ return window.Vanduo.queryAll(config.root, selector);
5771
+ }
5772
+ const scope = normalizeRoot(config.root);
5773
+ if (scope === document) {
5774
+ return Array.from(document.querySelectorAll(selector));
5775
+ }
5776
+ const matches = [];
5777
+ if (scope instanceof Element && scope.matches(selector)) {
5778
+ matches.push(scope);
5779
+ }
5780
+ return matches.concat(Array.from(scope.querySelectorAll(selector)));
5781
+ }
5782
+ function queryOne(selector) {
5783
+ const matches = queryAll(selector);
5784
+ return matches.length ? matches[0] : null;
5785
+ }
5241
5786
  function safeInvokeCallback(name, fn, ...args) {
5242
5787
  try {
5243
5788
  fn(...args);
@@ -5257,7 +5802,7 @@
5257
5802
  if (state.initialized) {
5258
5803
  return instance;
5259
5804
  }
5260
- state.container = document.querySelector(config.containerSelector);
5805
+ state.container = queryOne(config.containerSelector);
5261
5806
  if (!state.container) {
5262
5807
  state.initialized = false;
5263
5808
  return null;
@@ -5294,7 +5839,7 @@
5294
5839
  });
5295
5840
  return;
5296
5841
  }
5297
- const sections = document.querySelectorAll(config.contentSelector);
5842
+ const sections = queryAll(config.contentSelector);
5298
5843
  const categoryMap = buildCategoryMap();
5299
5844
  sections.forEach(function(section) {
5300
5845
  const id = section.id;
@@ -5329,7 +5874,7 @@
5329
5874
  function buildCategoryMap() {
5330
5875
  const map = {};
5331
5876
  let currentCategory = "Documentation";
5332
- const navItems = document.querySelectorAll(config.navSelector + ", " + config.sectionSelector);
5877
+ const navItems = queryAll(config.navSelector + ", " + config.sectionSelector);
5333
5878
  navItems.forEach(function(item) {
5334
5879
  if (item.classList.contains("doc-nav-section")) {
5335
5880
  currentCategory = item.textContent.trim();
@@ -5658,7 +6203,7 @@
5658
6203
  safeInvokeCallback("onSelect", config.onSelect, result);
5659
6204
  return;
5660
6205
  }
5661
- const section = document.querySelector(result.url);
6206
+ const section = queryOne(result.url) || document.querySelector(result.url);
5662
6207
  if (section) {
5663
6208
  section.scrollIntoView({ behavior: "smooth", block: "start" });
5664
6209
  window.history.pushState(null, "", result.url);
@@ -5666,7 +6211,7 @@
5666
6211
  }
5667
6212
  }
5668
6213
  function updateSidebarActive(sectionId) {
5669
- const navLinks = document.querySelectorAll(config.navSelector);
6214
+ const navLinks = queryAll(config.navSelector);
5670
6215
  navLinks.forEach(function(link) {
5671
6216
  link.classList.remove("active");
5672
6217
  if (link.getAttribute("href") === "#" + sectionId) {
@@ -5713,6 +6258,8 @@
5713
6258
  }
5714
6259
  function setConfig(newConfig) {
5715
6260
  Object.assign(config, newConfig);
6261
+ config.root = normalizeRoot(config.root);
6262
+ config.highlightTag = normalizeHighlightTag(config.highlightTag);
5716
6263
  }
5717
6264
  function getConfig() {
5718
6265
  return Object.assign({}, config);
@@ -5729,7 +6276,10 @@
5729
6276
  close,
5730
6277
  setConfig,
5731
6278
  getConfig,
5732
- getIndex
6279
+ getIndex,
6280
+ getContainer: function() {
6281
+ return state.container;
6282
+ }
5733
6283
  };
5734
6284
  return instance;
5735
6285
  }
@@ -5751,27 +6301,34 @@
5751
6301
  /**
5752
6302
  * Initialize the default search instance
5753
6303
  */
5754
- init: function(options) {
6304
+ init: function(rootOrOptions, maybeOptions) {
6305
+ const root = isRoot(rootOrOptions) ? rootOrOptions : null;
6306
+ const options = root ? maybeOptions : rootOrOptions;
5755
6307
  if (this._instance) {
5756
6308
  this._instance.destroy();
5757
6309
  }
5758
6310
  if (options) {
5759
6311
  Object.assign(this.config, options);
5760
6312
  }
5761
- this._instance = createSearch(this.config);
6313
+ this._instance = createSearch(Object.assign({}, this.config, root ? { root } : {}));
5762
6314
  return this._instance ? this._instance.init() : null;
5763
6315
  },
5764
6316
  /**
5765
6317
  * Destroy the default instance
5766
6318
  */
5767
- destroy: function() {
6319
+ destroy: function(root) {
6320
+ if (root && this._instance && this._instance.getContainer() && typeof window.VanduoLifecycle !== "undefined") {
6321
+ if (!window.VanduoLifecycle.isInRoot(root, this._instance.getContainer())) {
6322
+ return;
6323
+ }
6324
+ }
5768
6325
  if (this._instance) {
5769
6326
  this._instance.destroy();
5770
6327
  this._instance = null;
5771
6328
  }
5772
6329
  },
5773
- destroyAll: function() {
5774
- this.destroy();
6330
+ destroyAll: function(root) {
6331
+ this.destroy(root);
5775
6332
  },
5776
6333
  /**
5777
6334
  * Rebuild the default instance index
@@ -5829,21 +6386,21 @@
5829
6386
  /**
5830
6387
  * Initialize draggable components
5831
6388
  */
5832
- init: function() {
5833
- const draggables = document.querySelectorAll(".vd-draggable, [data-draggable]");
6389
+ init: function(root) {
6390
+ const draggables = window.Vanduo.queryAll(root, ".vd-draggable, [data-draggable]");
5834
6391
  draggables.forEach((element) => {
5835
6392
  if (this.instances.has(element)) {
5836
6393
  return;
5837
6394
  }
5838
6395
  this.initDraggable(element);
5839
6396
  });
5840
- const containers = document.querySelectorAll(this.containerSelector);
6397
+ const containers = window.Vanduo.queryAll(root, this.containerSelector);
5841
6398
  containers.forEach((container) => {
5842
6399
  if (!this.instances.has(container)) {
5843
6400
  this.initContainer(container);
5844
6401
  }
5845
6402
  });
5846
- const dropZones = document.querySelectorAll(".vd-drop-zone");
6403
+ const dropZones = window.Vanduo.queryAll(root, ".vd-drop-zone");
5847
6404
  dropZones.forEach((zone) => {
5848
6405
  if (!this.instances.has(zone)) {
5849
6406
  this.initDropZone(zone);
@@ -6453,6 +7010,35 @@
6453
7010
  (function() {
6454
7011
  "use strict";
6455
7012
  const _observerMap = /* @__PURE__ */ new Map();
7013
+ function _isRoot(root) {
7014
+ return !!root && (root === document || root.nodeType === 1 || root.nodeType === 9 || root.nodeType === 11);
7015
+ }
7016
+ function _normalizeRoot(root) {
7017
+ return _isRoot(root) ? root : document;
7018
+ }
7019
+ function _isInRoot(root, element) {
7020
+ const scope = _normalizeRoot(root);
7021
+ if (!(element instanceof Element)) return false;
7022
+ if (scope === document) {
7023
+ return document.documentElement ? document.documentElement.contains(element) : document.contains(element);
7024
+ }
7025
+ if (scope === element) return true;
7026
+ return typeof scope.contains === "function" && scope.contains(element);
7027
+ }
7028
+ function _queryAll(root, selector) {
7029
+ const scope = _normalizeRoot(root);
7030
+ const matches = [];
7031
+ if (scope instanceof Element && typeof scope.matches === "function" && scope.matches(selector)) {
7032
+ matches.push(scope);
7033
+ }
7034
+ if (typeof scope.querySelectorAll === "function") {
7035
+ const descendants = scope.querySelectorAll(selector);
7036
+ for (let i = 0; i < descendants.length; i++) {
7037
+ matches.push(descendants[i]);
7038
+ }
7039
+ }
7040
+ return matches;
7041
+ }
6456
7042
  function _isSafeUrl(url) {
6457
7043
  try {
6458
7044
  const resolved = new URL(url, window.location.href);
@@ -6540,6 +7126,9 @@
6540
7126
  if (entry.isIntersecting) {
6541
7127
  obs.unobserve(entry.target);
6542
7128
  _observerMap.delete(entry.target);
7129
+ if (typeof window.VanduoLifecycle !== "undefined" && window.VanduoLifecycle.has(entry.target, "lazyLoad")) {
7130
+ window.VanduoLifecycle.unregister(entry.target, "lazyLoad");
7131
+ }
6543
7132
  try {
6544
7133
  callback(entry.target);
6545
7134
  } catch (e) {
@@ -6549,27 +7138,39 @@
6549
7138
  });
6550
7139
  }, { threshold, rootMargin });
6551
7140
  _observerMap.set(element, observer);
7141
+ if (typeof window.VanduoLifecycle !== "undefined" && !window.VanduoLifecycle.has(element, "lazyLoad")) {
7142
+ window.VanduoLifecycle.register(element, "lazyLoad", [() => {
7143
+ VanduoLazyLoad.unobserve(element, { skipLifecycle: true });
7144
+ }]);
7145
+ }
6552
7146
  observer.observe(element);
6553
7147
  },
6554
7148
  /**
6555
7149
  * Stop observing an element that was previously passed to observe().
6556
7150
  * @param {Element} element
6557
7151
  */
6558
- unobserve: function(element) {
7152
+ unobserve: function(element, options) {
7153
+ const opts = options || {};
6559
7154
  const observer = _observerMap.get(element);
6560
7155
  if (observer) {
6561
7156
  observer.unobserve(element);
7157
+ if (typeof observer.disconnect === "function") {
7158
+ observer.disconnect();
7159
+ }
6562
7160
  _observerMap.delete(element);
6563
7161
  }
7162
+ if (!opts.skipLifecycle && typeof window.VanduoLifecycle !== "undefined" && window.VanduoLifecycle.has(element, "lazyLoad")) {
7163
+ window.VanduoLifecycle.unregister(element, "lazyLoad");
7164
+ }
6564
7165
  },
6565
7166
  /**
6566
7167
  * Stop observing ALL currently observed elements.
6567
7168
  */
6568
7169
  unobserveAll: function() {
6569
- _observerMap.forEach(function(observer, element) {
6570
- observer.unobserve(element);
7170
+ const observed = Array.from(_observerMap.keys());
7171
+ observed.forEach(function(element) {
7172
+ VanduoLazyLoad.unobserve(element);
6571
7173
  });
6572
- _observerMap.clear();
6573
7174
  },
6574
7175
  /* ─────────────────────────────────────────────────
6575
7176
  * HIGH-LEVEL API
@@ -6618,7 +7219,7 @@
6618
7219
  _safeInjectHtml(containerEl, html);
6619
7220
  _dispatch(containerEl, "lazysection:loaded", { url });
6620
7221
  if (typeof window.Vanduo !== "undefined") {
6621
- window.Vanduo.init();
7222
+ window.Vanduo.init(containerEl);
6622
7223
  }
6623
7224
  if (typeof opts.onLoaded === "function") {
6624
7225
  opts.onLoaded(containerEl);
@@ -6653,9 +7254,9 @@
6653
7254
  * Scan the DOM for [data-vd-lazy] elements and wire them up.
6654
7255
  * Safe to call multiple times — already-observed elements are skipped.
6655
7256
  */
6656
- init: function() {
7257
+ init: function(root) {
6657
7258
  const self = this;
6658
- const elements = document.querySelectorAll("[data-vd-lazy]");
7259
+ const elements = _queryAll(root, "[data-vd-lazy]");
6659
7260
  elements.forEach(function(el) {
6660
7261
  if (_observerMap.has(el) || el.dataset.vdLazyState === "loading" || el.dataset.vdLazyState === "loaded") return;
6661
7262
  const url = el.getAttribute("data-vd-lazy");
@@ -6672,10 +7273,26 @@
6672
7273
  }
6673
7274
  });
6674
7275
  });
7276
+ },
7277
+ destroy: function(element) {
7278
+ this.unobserve(element);
7279
+ },
7280
+ destroyAll: function(root) {
7281
+ const scope = _normalizeRoot(root);
7282
+ if (scope === document) {
7283
+ this.unobserveAll();
7284
+ return;
7285
+ }
7286
+ const observed = Array.from(_observerMap.keys());
7287
+ observed.forEach((element) => {
7288
+ if (_isInRoot(scope, element)) {
7289
+ this.unobserve(element);
7290
+ }
7291
+ });
6675
7292
  }
6676
7293
  };
6677
7294
  if (typeof window.Vanduo !== "undefined") {
6678
- window.Vanduo.register("LazyLoad", VanduoLazyLoad);
7295
+ window.Vanduo.register("lazyLoad", VanduoLazyLoad, { aliases: ["LazyLoad"] });
6679
7296
  }
6680
7297
  window.VanduoLazyLoad = VanduoLazyLoad;
6681
7298
  })();
@@ -6686,8 +7303,8 @@
6686
7303
  const GlassScroll = {
6687
7304
  /** @type {Map<Element, IntersectionObserver>} */
6688
7305
  observers: /* @__PURE__ */ new Map(),
6689
- init: function() {
6690
- document.querySelectorAll("[data-glass-scroll]").forEach((el) => {
7306
+ init: function(root) {
7307
+ window.Vanduo.queryAll(root, "[data-glass-scroll]").forEach((el) => {
6691
7308
  if (this.observers.has(el)) return;
6692
7309
  this.initElement(el);
6693
7310
  });
@@ -6747,8 +7364,8 @@
6747
7364
  const MORPH_DURATION_MS = 750;
6748
7365
  const Morph = {
6749
7366
  instances: /* @__PURE__ */ new Map(),
6750
- init: function() {
6751
- const elements = document.querySelectorAll(".vd-morph, [data-vd-morph]");
7367
+ init: function(root) {
7368
+ const elements = window.Vanduo.queryAll(root, ".vd-morph, [data-vd-morph]");
6752
7369
  elements.forEach(function(el) {
6753
7370
  if (Morph.instances.has(el)) return;
6754
7371
  if (el.getAttribute("data-vd-morph") === "manual") return;
@@ -6853,8 +7470,8 @@
6853
7470
  "use strict";
6854
7471
  const ExpandingCards = {
6855
7472
  instances: /* @__PURE__ */ new Map(),
6856
- init: function() {
6857
- document.querySelectorAll(".vd-expanding-cards").forEach(function(el) {
7473
+ init: function(root) {
7474
+ window.Vanduo.queryAll(root, ".vd-expanding-cards").forEach(function(el) {
6858
7475
  if (el.getAttribute("data-vd-expanding-cards") === "manual") return;
6859
7476
  if (ExpandingCards.instances.has(el)) return;
6860
7477
  ExpandingCards.initContainer(el);
@@ -6986,6 +7603,8 @@
6986
7603
  const playBtn = scope.querySelector("[data-vd-timeline-play]");
6987
7604
  const pauseBtn = scope.querySelector("[data-vd-timeline-pause]");
6988
7605
  let playTimer = null;
7606
+ let isPlaying = false;
7607
+ let playToken = 0;
6989
7608
  function updateNavButtons() {
6990
7609
  const k = countRevealedPrefix(items);
6991
7610
  const n = items.length;
@@ -7000,10 +7619,10 @@
7000
7619
  nextBtn.setAttribute("aria-disabled", atEnd ? "true" : "false");
7001
7620
  }
7002
7621
  if (playBtn) {
7003
- playBtn.setAttribute("aria-pressed", playTimer ? "true" : "false");
7622
+ playBtn.setAttribute("aria-pressed", isPlaying ? "true" : "false");
7004
7623
  }
7005
7624
  if (pauseBtn) {
7006
- pauseBtn.disabled = !playTimer;
7625
+ pauseBtn.disabled = !isPlaying;
7007
7626
  }
7008
7627
  }
7009
7628
  function stepNext() {
@@ -7020,20 +7639,36 @@
7020
7639
  }
7021
7640
  updateNavButtons();
7022
7641
  }
7023
- function play() {
7024
- if (playTimer) return;
7025
- playTimer = setInterval(function() {
7642
+ function scheduleNext() {
7643
+ const token = ++playToken;
7644
+ playTimer = setTimeout(function() {
7645
+ playTimer = null;
7646
+ if (!isPlaying || token !== playToken) {
7647
+ return;
7648
+ }
7026
7649
  if (countRevealedPrefix(items) >= items.length) {
7027
7650
  pause();
7028
7651
  return;
7029
7652
  }
7030
7653
  stepNext();
7654
+ if (countRevealedPrefix(items) >= items.length) {
7655
+ pause();
7656
+ return;
7657
+ }
7658
+ scheduleNext();
7031
7659
  }, PLAY_INTERVAL_MS);
7660
+ }
7661
+ function play() {
7662
+ if (isPlaying) return;
7663
+ isPlaying = true;
7664
+ scheduleNext();
7032
7665
  updateNavButtons();
7033
7666
  }
7034
7667
  function pause() {
7668
+ isPlaying = false;
7669
+ playToken++;
7035
7670
  if (playTimer) {
7036
- clearInterval(playTimer);
7671
+ clearTimeout(playTimer);
7037
7672
  playTimer = null;
7038
7673
  }
7039
7674
  updateNavButtons();
@@ -7066,15 +7701,15 @@
7066
7701
  }
7067
7702
  const Timeline = {
7068
7703
  instances: /* @__PURE__ */ new Map(),
7069
- init: function() {
7070
- document.querySelectorAll(".vd-timeline.vd-timeline-animated").forEach(function(el) {
7704
+ init: function(root) {
7705
+ window.Vanduo.queryAll(root, ".vd-timeline.vd-timeline-animated").forEach(function(el) {
7071
7706
  if (Timeline.instances.has(el)) return;
7072
7707
  Timeline.initInstance(el);
7073
7708
  });
7074
7709
  },
7075
- reinit: function() {
7076
- Timeline.destroyAll();
7077
- Timeline.init();
7710
+ reinit: function(root) {
7711
+ Timeline.destroyAll(root);
7712
+ Timeline.init(root);
7078
7713
  },
7079
7714
  initInstance: function(container) {
7080
7715
  const cleanup = [];
@@ -7133,8 +7768,10 @@
7133
7768
  });
7134
7769
  this.instances.delete(container);
7135
7770
  },
7136
- destroyAll: function() {
7771
+ destroyAll: function(root) {
7772
+ const scope = window.Vanduo && typeof window.Vanduo._normalizeRoot === "function" ? window.Vanduo._normalizeRoot(root) : document;
7137
7773
  this.instances.forEach(function(_, el) {
7774
+ if (scope !== document && scope !== el && (!scope.contains || !scope.contains(el))) return;
7138
7775
  Timeline.destroy(el);
7139
7776
  });
7140
7777
  }
@@ -7150,8 +7787,8 @@
7150
7787
  "use strict";
7151
7788
  const Flow = {
7152
7789
  instances: /* @__PURE__ */ new Map(),
7153
- init: function() {
7154
- const carousels = document.querySelectorAll(".vd-flow, .vd-carousel");
7790
+ init: function(root) {
7791
+ const carousels = window.Vanduo.queryAll(root, ".vd-flow, .vd-carousel");
7155
7792
  carousels.forEach((el) => {
7156
7793
  if (this.instances.has(el)) return;
7157
7794
  this.initInstance(el);
@@ -7368,8 +8005,8 @@
7368
8005
  const Bubble = {
7369
8006
  instances: /* @__PURE__ */ new Map(),
7370
8007
  _globalCleanups: [],
7371
- init: function() {
7372
- const triggers = document.querySelectorAll("[data-vd-bubble], [data-vd-popover]");
8008
+ init: function(root) {
8009
+ const triggers = window.Vanduo.queryAll(root, "[data-vd-bubble], [data-vd-popover]");
7373
8010
  triggers.forEach((el) => {
7374
8011
  if (this.instances.has(el)) return;
7375
8012
  this.initInstance(el);
@@ -7430,7 +8067,7 @@
7430
8067
  body.className = "vd-bubble-body";
7431
8068
  if (htmlContent) {
7432
8069
  if (typeof sanitizeHtml === "function") {
7433
- body.innerHTML = sanitizeHtml(htmlContent, { allowSvg });
8070
+ body.innerHTML = sanitizeHtml(htmlContent, { allowSvg, allowStyle: false });
7434
8071
  } else {
7435
8072
  body.textContent = htmlContent;
7436
8073
  }
@@ -7541,8 +8178,8 @@
7541
8178
  "use strict";
7542
8179
  const Waypoint = {
7543
8180
  instances: /* @__PURE__ */ new Map(),
7544
- init: function() {
7545
- const navs = document.querySelectorAll("[data-vd-waypoint-nav], [data-vd-scrollspy-nav]");
8181
+ init: function(root) {
8182
+ const navs = window.Vanduo.queryAll(root, "[data-vd-waypoint-nav], [data-vd-scrollspy-nav]");
7546
8183
  navs.forEach((nav) => {
7547
8184
  if (this.instances.has(nav)) return;
7548
8185
  this.initInstance(nav);
@@ -7635,8 +8272,8 @@
7635
8272
  "use strict";
7636
8273
  const Ripple = {
7637
8274
  instances: /* @__PURE__ */ new Map(),
7638
- init: function() {
7639
- const elements = document.querySelectorAll(".vd-ripple, [data-vd-ripple]");
8275
+ init: function(root) {
8276
+ const elements = window.Vanduo.queryAll(root, ".vd-ripple, [data-vd-ripple]");
7640
8277
  elements.forEach((el) => {
7641
8278
  if (this.instances.has(el)) return;
7642
8279
  this.initInstance(el);
@@ -7707,8 +8344,8 @@
7707
8344
  }
7708
8345
  const Affix = {
7709
8346
  instances: /* @__PURE__ */ new Map(),
7710
- init: function() {
7711
- const elements = document.querySelectorAll(".vd-affix, .vd-sticky, [data-vd-affix]");
8347
+ init: function(root) {
8348
+ const elements = window.Vanduo.queryAll(root, ".vd-affix, .vd-sticky, [data-vd-affix]");
7712
8349
  elements.forEach((el) => {
7713
8350
  if (this.instances.has(el)) return;
7714
8351
  this.initInstance(el);
@@ -7810,8 +8447,8 @@
7810
8447
  }
7811
8448
  const Suggest = {
7812
8449
  instances: /* @__PURE__ */ new Map(),
7813
- init: function() {
7814
- const inputs = document.querySelectorAll("[data-vd-suggest], [data-vd-autocomplete]");
8450
+ init: function(root) {
8451
+ const inputs = window.Vanduo.queryAll(root, "[data-vd-suggest], [data-vd-autocomplete]");
7815
8452
  inputs.forEach((el) => {
7816
8453
  if (this.instances.has(el)) return;
7817
8454
  this.initInstance(el);
@@ -7973,16 +8610,20 @@
7973
8610
  const blurHandler = () => {
7974
8611
  setTimeout(close, 200);
7975
8612
  };
8613
+ const focusHandler = () => {
8614
+ if (input.value.length >= minChars) {
8615
+ doSearch(input.value);
8616
+ }
8617
+ };
7976
8618
  input.addEventListener("input", inputHandler);
7977
8619
  input.addEventListener("keydown", keyHandler);
7978
8620
  input.addEventListener("blur", blurHandler);
7979
- input.addEventListener("focus", () => {
7980
- if (input.value.length >= minChars) doSearch(input.value);
7981
- });
8621
+ input.addEventListener("focus", focusHandler);
7982
8622
  cleanup.push(
7983
8623
  () => input.removeEventListener("input", inputHandler),
7984
8624
  () => input.removeEventListener("keydown", keyHandler),
7985
8625
  () => input.removeEventListener("blur", blurHandler),
8626
+ () => input.removeEventListener("focus", focusHandler),
7986
8627
  () => clearTimeout(debounceTimer),
7987
8628
  () => {
7988
8629
  if (list.parentNode) list.parentNode.removeChild(list);
@@ -8057,8 +8698,8 @@
8057
8698
  pattern: "Invalid format",
8058
8699
  match: "Fields do not match"
8059
8700
  },
8060
- init: function() {
8061
- const forms = document.querySelectorAll("[data-vd-validate], .vd-validate");
8701
+ init: function(root) {
8702
+ const forms = window.Vanduo.queryAll(root, "[data-vd-validate], .vd-validate");
8062
8703
  forms.forEach((form) => {
8063
8704
  if (this.instances.has(form)) return;
8064
8705
  this.initInstance(form);
@@ -8295,8 +8936,8 @@
8295
8936
  }
8296
8937
  const Datepicker = {
8297
8938
  instances: /* @__PURE__ */ new Map(),
8298
- init: function() {
8299
- const inputs = document.querySelectorAll("[data-vd-datepicker]");
8939
+ init: function(root) {
8940
+ const inputs = window.Vanduo.queryAll(root, "[data-vd-datepicker]");
8300
8941
  inputs.forEach((el) => {
8301
8942
  if (this.instances.has(el)) return;
8302
8943
  this.initInstance(el);
@@ -8748,8 +9389,8 @@
8748
9389
  "use strict";
8749
9390
  const Timepicker = {
8750
9391
  instances: /* @__PURE__ */ new Map(),
8751
- init: function() {
8752
- const inputs = document.querySelectorAll("[data-vd-timepicker]");
9392
+ init: function(root) {
9393
+ const inputs = window.Vanduo.queryAll(root, "[data-vd-timepicker]");
8753
9394
  inputs.forEach((el) => {
8754
9395
  if (this.instances.has(el)) return;
8755
9396
  this.initInstance(el);
@@ -8863,8 +9504,8 @@
8863
9504
  "use strict";
8864
9505
  const Stepper = {
8865
9506
  instances: /* @__PURE__ */ new Map(),
8866
- init: function() {
8867
- const steppers = document.querySelectorAll(".vd-stepper");
9507
+ init: function(root) {
9508
+ const steppers = window.Vanduo.queryAll(root, ".vd-stepper");
8868
9509
  steppers.forEach((el) => {
8869
9510
  if (this.instances.has(el)) return;
8870
9511
  this.initInstance(el);
@@ -8939,8 +9580,8 @@
8939
9580
  "use strict";
8940
9581
  const Rating = {
8941
9582
  instances: /* @__PURE__ */ new Map(),
8942
- init: function() {
8943
- const ratings = document.querySelectorAll("[data-vd-rating]");
9583
+ init: function(root) {
9584
+ const ratings = window.Vanduo.queryAll(root, "[data-vd-rating]");
8944
9585
  ratings.forEach((el) => {
8945
9586
  if (this.instances.has(el)) return;
8946
9587
  this.initInstance(el);
@@ -9074,8 +9715,8 @@
9074
9715
  "use strict";
9075
9716
  const Transfer = {
9076
9717
  instances: /* @__PURE__ */ new Map(),
9077
- init: function() {
9078
- const transfers = document.querySelectorAll("[data-vd-transfer]");
9718
+ init: function(root) {
9719
+ const transfers = window.Vanduo.queryAll(root, "[data-vd-transfer]");
9079
9720
  transfers.forEach((el) => {
9080
9721
  if (this.instances.has(el)) return;
9081
9722
  this.initInstance(el);
@@ -9238,8 +9879,8 @@
9238
9879
  "use strict";
9239
9880
  const Tree = {
9240
9881
  instances: /* @__PURE__ */ new Map(),
9241
- init: function() {
9242
- const trees = document.querySelectorAll("[data-vd-tree]");
9882
+ init: function(root) {
9883
+ const trees = window.Vanduo.queryAll(root, "[data-vd-tree]");
9243
9884
  trees.forEach((el) => {
9244
9885
  if (this.instances.has(el)) return;
9245
9886
  this.initInstance(el);
@@ -9403,8 +10044,8 @@
9403
10044
  _cleanup: [],
9404
10045
  _boundTriggers: /* @__PURE__ */ new WeakMap(),
9405
10046
  _triggerElement: null,
9406
- init: function() {
9407
- const triggers = document.querySelectorAll("[data-vd-spotlight]");
10047
+ init: function(root) {
10048
+ const triggers = window.Vanduo.queryAll(root, "[data-vd-spotlight]");
9408
10049
  triggers.forEach((trigger) => {
9409
10050
  if (this._boundTriggers.has(trigger)) return;
9410
10051
  const clickHandler = (event) => {
@@ -9677,8 +10318,8 @@
9677
10318
  * Auto-initialize all .vd-music-player / [data-music-player] elements.
9678
10319
  * Options can be provided via data-music-player-options (JSON string).
9679
10320
  */
9680
- init: function() {
9681
- document.querySelectorAll(".vd-music-player, [data-music-player]").forEach((el) => {
10321
+ init: function(root) {
10322
+ window.Vanduo.queryAll(root, ".vd-music-player, [data-music-player]").forEach((el) => {
9682
10323
  if (this.instances.has(el)) return;
9683
10324
  let opts = {};
9684
10325
  const attr = el.getAttribute("data-music-player-options");