@vanduo-oss/framework 1.3.9 → 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 -42
  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 +20 -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 +34 -34
  48. package/css/core/grid.css +1 -6
  49. package/css/core/helpers.css +11 -11
  50. package/css/core/tokens.css +113 -35
  51. package/css/core/typography.css +14 -12
  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 +929 -289
  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 +7914 -7823
  64. package/dist/vanduo.css.map +1 -1
  65. package/dist/vanduo.esm.js +929 -289
  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 +929 -289
  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 +23 -13
  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 +151 -21
  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 +2 -1
@@ -1,192 +1,400 @@
1
- /*! Vanduo v1.3.9 | Built: 2026-05-10T18:54:59.798Z | git:2945a85 | 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.9" : "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,7 +1497,13 @@
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
  };
@@ -1197,14 +1513,13 @@
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
@@ -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;
@@ -3937,21 +4331,65 @@
3937
4331
  },
3938
4332
  isInitialized: false,
3939
4333
  _cleanup: [],
4334
+ _ownsDynamicPanel: false,
3940
4335
  // DOM references
3941
4336
  elements: {
3942
4337
  customizer: null,
3943
4338
  trigger: null,
4339
+ activeTrigger: null,
3944
4340
  triggers: [],
3945
4341
  panel: null,
3946
4342
  overlay: null
3947
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
+ },
3948
4385
  /**
3949
4386
  * Initialize the Theme Customizer
3950
4387
  */
3951
- init: function() {
4388
+ init: function(root) {
4389
+ const scope = this.normalizeRoot(root);
3952
4390
  if (this.isInitialized) {
3953
- this.bindExistingElements();
3954
- this.bindTriggerEvents();
4391
+ this.bindExistingElements(scope);
4392
+ this.bindTriggerEvents(scope);
3955
4393
  this.bindPanelEvents();
3956
4394
  this.updateUI();
3957
4395
  return;
@@ -3960,9 +4398,8 @@
3960
4398
  this._cleanup = [];
3961
4399
  this.loadPreferences();
3962
4400
  this.applyAllPreferences();
3963
- this.bindExistingElements();
3964
- this.bindEvents();
3965
- console.log("Vanduo Theme Customizer initialized");
4401
+ this.bindExistingElements(scope);
4402
+ this.bindEvents(scope);
3966
4403
  },
3967
4404
  addListener: function(target, event, handler, options) {
3968
4405
  if (!target) return;
@@ -4123,18 +4560,26 @@
4123
4560
  /**
4124
4561
  * Bind to existing DOM elements or create them dynamically
4125
4562
  */
4126
- bindExistingElements: function() {
4127
- this.elements.customizer = document.querySelector(".vd-theme-customizer");
4128
- 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
+ });
4129
4572
  if (!this.elements.trigger && this.elements.triggers.length) {
4130
4573
  this.elements.trigger = this.elements.triggers[0];
4131
4574
  }
4132
- 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;
4133
4578
  this.elements.trigger = this.elements.customizer.querySelector(".vd-theme-customizer-trigger") || this.elements.trigger;
4134
4579
  this.elements.panel = this.elements.customizer.querySelector(".vd-theme-customizer-panel");
4135
4580
  this.elements.overlay = this.elements.customizer.querySelector(".vd-theme-customizer-overlay");
4136
4581
  } else {
4137
- if (this.elements.triggers.length) {
4582
+ if (scopedTriggers.length && !this.elements.panel) {
4138
4583
  this.createDynamicPanel();
4139
4584
  }
4140
4585
  }
@@ -4144,7 +4589,7 @@
4144
4589
  * Create the panel dynamically when only a trigger button exists
4145
4590
  */
4146
4591
  createDynamicPanel: function() {
4147
- if (!this.elements.triggers.length) {
4592
+ if (!this.elements.triggers.length || this.elements.panel && this.elements.panel.isConnected) {
4148
4593
  return;
4149
4594
  }
4150
4595
  this.elements.trigger = this.elements.triggers[0];
@@ -4157,6 +4602,7 @@
4157
4602
  document.body.appendChild(panel);
4158
4603
  this.elements.panel = panel;
4159
4604
  this.elements.overlay = overlay;
4605
+ this._ownsDynamicPanel = true;
4160
4606
  this.elements.customizer = {
4161
4607
  contains: (el) => panel.contains(el) || this.elements.triggers.some((trigger) => trigger.contains(el))
4162
4608
  };
@@ -4339,8 +4785,8 @@
4339
4785
  this.state.primary = expected;
4340
4786
  }
4341
4787
  },
4342
- bindEvents: function() {
4343
- this.bindTriggerEvents();
4788
+ bindEvents: function(root) {
4789
+ this.bindTriggerEvents(root);
4344
4790
  this.bindPanelEvents();
4345
4791
  if (window.matchMedia) {
4346
4792
  const mq = window.matchMedia("(prefers-color-scheme: dark)");
@@ -4367,21 +4813,44 @@
4367
4813
  }
4368
4814
  });
4369
4815
  },
4370
- bindTriggerEvents: function() {
4371
- this.elements.triggers.forEach((trigger) => {
4816
+ bindTriggerEvents: function(root) {
4817
+ const triggers = root ? this.getTriggers(root) : this.elements.triggers;
4818
+ triggers.forEach((trigger) => {
4372
4819
  if (trigger.getAttribute("data-customizer-trigger-initialized") === "true") {
4373
4820
  return;
4374
4821
  }
4375
- this.addListener(trigger, "click", (e) => {
4822
+ const onClick = (e) => {
4376
4823
  e.preventDefault();
4377
4824
  e.stopPropagation();
4378
4825
  this.elements.activeTrigger = trigger;
4379
4826
  this.elements.trigger = trigger;
4380
4827
  this.toggle();
4381
- });
4828
+ };
4829
+ trigger.addEventListener("click", onClick);
4830
+ trigger._themeCustomizerTriggerHandler = onClick;
4382
4831
  trigger.setAttribute("data-customizer-trigger-initialized", "true");
4383
4832
  });
4384
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
+ },
4385
4854
  /**
4386
4855
  * Toggle panel open/close
4387
4856
  */
@@ -4492,13 +4961,42 @@
4492
4961
  return false;
4493
4962
  }
4494
4963
  },
4495
- 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
+ }
4496
4976
  this._cleanup.forEach((fn) => fn());
4497
4977
  this._cleanup = [];
4498
4978
  if (this.elements.panel) {
4499
4979
  this.elements.panel.removeAttribute("data-customizer-initialized");
4500
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
+ }
4501
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;
4502
5000
  this.isInitialized = false;
4503
5001
  }
4504
5002
  };
@@ -4515,7 +5013,13 @@
4515
5013
  isInitialized: false,
4516
5014
  _mediaQuery: null,
4517
5015
  _onMediaChange: null,
4518
- 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) {
4519
5023
  this.STORAGE_KEY = "vanduo-theme-preference";
4520
5024
  this.state = {
4521
5025
  preference: this.getPreference()
@@ -4523,15 +5027,14 @@
4523
5027
  };
4524
5028
  if (this.isInitialized) {
4525
5029
  this.applyTheme();
4526
- this.renderUI();
4527
- this.updateUI();
5030
+ this.renderUI(root);
5031
+ this.updateUI(root);
4528
5032
  return;
4529
5033
  }
4530
5034
  this.isInitialized = true;
4531
5035
  this.applyTheme();
4532
5036
  this.listenForSystemChanges();
4533
- this.renderUI();
4534
- console.log("Vanduo Theme Switcher initialized");
5037
+ this.renderUI(root);
4535
5038
  },
4536
5039
  getPreference: function() {
4537
5040
  return this.getStorageValue(this.STORAGE_KEY, "system");
@@ -4591,8 +5094,8 @@
4591
5094
  this._mediaQuery.addEventListener("change", this._onMediaChange);
4592
5095
  },
4593
5096
  // Helper to facilitate UI creation if needed, though often UI is in HTML
4594
- renderUI: function() {
4595
- const toggles = document.querySelectorAll('[data-toggle="theme"]');
5097
+ renderUI: function(root) {
5098
+ const toggles = this.getToggles(root);
4596
5099
  toggles.forEach((toggle) => {
4597
5100
  if (toggle.getAttribute("data-theme-initialized") === "true") {
4598
5101
  if (toggle.tagName === "SELECT") {
@@ -4619,8 +5122,8 @@
4619
5122
  toggle.setAttribute("data-theme-initialized", "true");
4620
5123
  });
4621
5124
  },
4622
- updateUI: function() {
4623
- const toggles = document.querySelectorAll('[data-toggle="theme"]');
5125
+ updateUI: function(root) {
5126
+ const toggles = this.getToggles(root);
4624
5127
  toggles.forEach((toggle) => {
4625
5128
  if (toggle.tagName === "SELECT") {
4626
5129
  toggle.value = this.state.preference;
@@ -4632,8 +5135,11 @@
4632
5135
  }
4633
5136
  });
4634
5137
  },
4635
- destroyAll: function() {
4636
- 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
+ });
4637
5143
  toggles.forEach((toggle) => {
4638
5144
  if (toggle._themeToggleHandler) {
4639
5145
  const eventName = toggle.tagName === "SELECT" ? "change" : "click";
@@ -4642,12 +5148,14 @@
4642
5148
  }
4643
5149
  toggle.removeAttribute("data-theme-initialized");
4644
5150
  });
4645
- if (this._mediaQuery && this._onMediaChange) {
5151
+ if (scope === document && this._mediaQuery && this._onMediaChange) {
4646
5152
  this._mediaQuery.removeEventListener("change", this._onMediaChange);
4647
5153
  }
4648
- this._mediaQuery = null;
4649
- this._onMediaChange = null;
4650
- this.isInitialized = false;
5154
+ if (scope === document) {
5155
+ this._mediaQuery = null;
5156
+ this._onMediaChange = null;
5157
+ this.isInitialized = false;
5158
+ }
4651
5159
  }
4652
5160
  };
4653
5161
  if (window.Vanduo) {
@@ -4724,7 +5232,7 @@
4724
5232
  let html = "";
4725
5233
  if (config.icon) {
4726
5234
  const allowSvg = config.iconAllowSvg === true;
4727
- 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);
4728
5236
  html += `<span class="vd-toast-icon">${safeIcon}</span>`;
4729
5237
  } else if (config.type) {
4730
5238
  html += `<span class="vd-toast-icon">${this.getDefaultIcon(config.type)}</span>`;
@@ -4944,8 +5452,8 @@
4944
5452
  /**
4945
5453
  * Initialize tooltips
4946
5454
  */
4947
- init: function() {
4948
- 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]");
4949
5457
  elements.forEach((element) => {
4950
5458
  if (this.tooltips.has(element)) {
4951
5459
  return;
@@ -5001,7 +5509,7 @@
5001
5509
  const textContent = element.dataset.tooltip;
5002
5510
  if (htmlContent) {
5003
5511
  const allowSvg = element.hasAttribute("data-tooltip-allow-svg");
5004
- tooltip.innerHTML = this.sanitizeHtml(htmlContent, { allowSvg });
5512
+ tooltip.innerHTML = this.sanitizeHtml(htmlContent, { allowSvg, allowStyle: false });
5005
5513
  tooltip.classList.add("vd-tooltip-html");
5006
5514
  } else if (textContent) {
5007
5515
  tooltip.textContent = textContent;
@@ -5134,7 +5642,7 @@
5134
5642
  const { tooltip } = this.tooltips.get(el);
5135
5643
  if (isHtml) {
5136
5644
  const allowSvg = el.hasAttribute("data-tooltip-allow-svg");
5137
- tooltip.innerHTML = this.sanitizeHtml(content, { allowSvg });
5645
+ tooltip.innerHTML = this.sanitizeHtml(content, { allowSvg, allowStyle: false });
5138
5646
  tooltip.classList.add("vd-tooltip-html");
5139
5647
  } else {
5140
5648
  tooltip.textContent = content;
@@ -5224,8 +5732,26 @@
5224
5732
  emptyText: "Try different keywords or check spelling",
5225
5733
  placeholder: "Search..."
5226
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
+ }
5227
5751
  function createSearch(options) {
5228
5752
  const config = Object.assign({}, DEFAULTS, options || {});
5753
+ config.root = normalizeRoot(config.root);
5754
+ config.highlightTag = normalizeHighlightTag(config.highlightTag);
5229
5755
  const state = {
5230
5756
  initialized: false,
5231
5757
  index: [],
@@ -5239,6 +5765,24 @@
5239
5765
  debounceTimer: null,
5240
5766
  boundHandlers: {}
5241
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
+ }
5242
5786
  function safeInvokeCallback(name, fn, ...args) {
5243
5787
  try {
5244
5788
  fn(...args);
@@ -5258,7 +5802,7 @@
5258
5802
  if (state.initialized) {
5259
5803
  return instance;
5260
5804
  }
5261
- state.container = document.querySelector(config.containerSelector);
5805
+ state.container = queryOne(config.containerSelector);
5262
5806
  if (!state.container) {
5263
5807
  state.initialized = false;
5264
5808
  return null;
@@ -5295,7 +5839,7 @@
5295
5839
  });
5296
5840
  return;
5297
5841
  }
5298
- const sections = document.querySelectorAll(config.contentSelector);
5842
+ const sections = queryAll(config.contentSelector);
5299
5843
  const categoryMap = buildCategoryMap();
5300
5844
  sections.forEach(function(section) {
5301
5845
  const id = section.id;
@@ -5330,7 +5874,7 @@
5330
5874
  function buildCategoryMap() {
5331
5875
  const map = {};
5332
5876
  let currentCategory = "Documentation";
5333
- const navItems = document.querySelectorAll(config.navSelector + ", " + config.sectionSelector);
5877
+ const navItems = queryAll(config.navSelector + ", " + config.sectionSelector);
5334
5878
  navItems.forEach(function(item) {
5335
5879
  if (item.classList.contains("doc-nav-section")) {
5336
5880
  currentCategory = item.textContent.trim();
@@ -5659,7 +6203,7 @@
5659
6203
  safeInvokeCallback("onSelect", config.onSelect, result);
5660
6204
  return;
5661
6205
  }
5662
- const section = document.querySelector(result.url);
6206
+ const section = queryOne(result.url) || document.querySelector(result.url);
5663
6207
  if (section) {
5664
6208
  section.scrollIntoView({ behavior: "smooth", block: "start" });
5665
6209
  window.history.pushState(null, "", result.url);
@@ -5667,7 +6211,7 @@
5667
6211
  }
5668
6212
  }
5669
6213
  function updateSidebarActive(sectionId) {
5670
- const navLinks = document.querySelectorAll(config.navSelector);
6214
+ const navLinks = queryAll(config.navSelector);
5671
6215
  navLinks.forEach(function(link) {
5672
6216
  link.classList.remove("active");
5673
6217
  if (link.getAttribute("href") === "#" + sectionId) {
@@ -5714,6 +6258,8 @@
5714
6258
  }
5715
6259
  function setConfig(newConfig) {
5716
6260
  Object.assign(config, newConfig);
6261
+ config.root = normalizeRoot(config.root);
6262
+ config.highlightTag = normalizeHighlightTag(config.highlightTag);
5717
6263
  }
5718
6264
  function getConfig() {
5719
6265
  return Object.assign({}, config);
@@ -5730,7 +6276,10 @@
5730
6276
  close,
5731
6277
  setConfig,
5732
6278
  getConfig,
5733
- getIndex
6279
+ getIndex,
6280
+ getContainer: function() {
6281
+ return state.container;
6282
+ }
5734
6283
  };
5735
6284
  return instance;
5736
6285
  }
@@ -5752,27 +6301,34 @@
5752
6301
  /**
5753
6302
  * Initialize the default search instance
5754
6303
  */
5755
- init: function(options) {
6304
+ init: function(rootOrOptions, maybeOptions) {
6305
+ const root = isRoot(rootOrOptions) ? rootOrOptions : null;
6306
+ const options = root ? maybeOptions : rootOrOptions;
5756
6307
  if (this._instance) {
5757
6308
  this._instance.destroy();
5758
6309
  }
5759
6310
  if (options) {
5760
6311
  Object.assign(this.config, options);
5761
6312
  }
5762
- this._instance = createSearch(this.config);
6313
+ this._instance = createSearch(Object.assign({}, this.config, root ? { root } : {}));
5763
6314
  return this._instance ? this._instance.init() : null;
5764
6315
  },
5765
6316
  /**
5766
6317
  * Destroy the default instance
5767
6318
  */
5768
- 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
+ }
5769
6325
  if (this._instance) {
5770
6326
  this._instance.destroy();
5771
6327
  this._instance = null;
5772
6328
  }
5773
6329
  },
5774
- destroyAll: function() {
5775
- this.destroy();
6330
+ destroyAll: function(root) {
6331
+ this.destroy(root);
5776
6332
  },
5777
6333
  /**
5778
6334
  * Rebuild the default instance index
@@ -5830,21 +6386,21 @@
5830
6386
  /**
5831
6387
  * Initialize draggable components
5832
6388
  */
5833
- init: function() {
5834
- const draggables = document.querySelectorAll(".vd-draggable, [data-draggable]");
6389
+ init: function(root) {
6390
+ const draggables = window.Vanduo.queryAll(root, ".vd-draggable, [data-draggable]");
5835
6391
  draggables.forEach((element) => {
5836
6392
  if (this.instances.has(element)) {
5837
6393
  return;
5838
6394
  }
5839
6395
  this.initDraggable(element);
5840
6396
  });
5841
- const containers = document.querySelectorAll(this.containerSelector);
6397
+ const containers = window.Vanduo.queryAll(root, this.containerSelector);
5842
6398
  containers.forEach((container) => {
5843
6399
  if (!this.instances.has(container)) {
5844
6400
  this.initContainer(container);
5845
6401
  }
5846
6402
  });
5847
- const dropZones = document.querySelectorAll(".vd-drop-zone");
6403
+ const dropZones = window.Vanduo.queryAll(root, ".vd-drop-zone");
5848
6404
  dropZones.forEach((zone) => {
5849
6405
  if (!this.instances.has(zone)) {
5850
6406
  this.initDropZone(zone);
@@ -6454,6 +7010,35 @@
6454
7010
  (function() {
6455
7011
  "use strict";
6456
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
+ }
6457
7042
  function _isSafeUrl(url) {
6458
7043
  try {
6459
7044
  const resolved = new URL(url, window.location.href);
@@ -6541,6 +7126,9 @@
6541
7126
  if (entry.isIntersecting) {
6542
7127
  obs.unobserve(entry.target);
6543
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
+ }
6544
7132
  try {
6545
7133
  callback(entry.target);
6546
7134
  } catch (e) {
@@ -6550,27 +7138,39 @@
6550
7138
  });
6551
7139
  }, { threshold, rootMargin });
6552
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
+ }
6553
7146
  observer.observe(element);
6554
7147
  },
6555
7148
  /**
6556
7149
  * Stop observing an element that was previously passed to observe().
6557
7150
  * @param {Element} element
6558
7151
  */
6559
- unobserve: function(element) {
7152
+ unobserve: function(element, options) {
7153
+ const opts = options || {};
6560
7154
  const observer = _observerMap.get(element);
6561
7155
  if (observer) {
6562
7156
  observer.unobserve(element);
7157
+ if (typeof observer.disconnect === "function") {
7158
+ observer.disconnect();
7159
+ }
6563
7160
  _observerMap.delete(element);
6564
7161
  }
7162
+ if (!opts.skipLifecycle && typeof window.VanduoLifecycle !== "undefined" && window.VanduoLifecycle.has(element, "lazyLoad")) {
7163
+ window.VanduoLifecycle.unregister(element, "lazyLoad");
7164
+ }
6565
7165
  },
6566
7166
  /**
6567
7167
  * Stop observing ALL currently observed elements.
6568
7168
  */
6569
7169
  unobserveAll: function() {
6570
- _observerMap.forEach(function(observer, element) {
6571
- observer.unobserve(element);
7170
+ const observed = Array.from(_observerMap.keys());
7171
+ observed.forEach(function(element) {
7172
+ VanduoLazyLoad.unobserve(element);
6572
7173
  });
6573
- _observerMap.clear();
6574
7174
  },
6575
7175
  /* ─────────────────────────────────────────────────
6576
7176
  * HIGH-LEVEL API
@@ -6619,7 +7219,7 @@
6619
7219
  _safeInjectHtml(containerEl, html);
6620
7220
  _dispatch(containerEl, "lazysection:loaded", { url });
6621
7221
  if (typeof window.Vanduo !== "undefined") {
6622
- window.Vanduo.init();
7222
+ window.Vanduo.init(containerEl);
6623
7223
  }
6624
7224
  if (typeof opts.onLoaded === "function") {
6625
7225
  opts.onLoaded(containerEl);
@@ -6654,9 +7254,9 @@
6654
7254
  * Scan the DOM for [data-vd-lazy] elements and wire them up.
6655
7255
  * Safe to call multiple times — already-observed elements are skipped.
6656
7256
  */
6657
- init: function() {
7257
+ init: function(root) {
6658
7258
  const self = this;
6659
- const elements = document.querySelectorAll("[data-vd-lazy]");
7259
+ const elements = _queryAll(root, "[data-vd-lazy]");
6660
7260
  elements.forEach(function(el) {
6661
7261
  if (_observerMap.has(el) || el.dataset.vdLazyState === "loading" || el.dataset.vdLazyState === "loaded") return;
6662
7262
  const url = el.getAttribute("data-vd-lazy");
@@ -6673,10 +7273,26 @@
6673
7273
  }
6674
7274
  });
6675
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
+ });
6676
7292
  }
6677
7293
  };
6678
7294
  if (typeof window.Vanduo !== "undefined") {
6679
- window.Vanduo.register("LazyLoad", VanduoLazyLoad);
7295
+ window.Vanduo.register("lazyLoad", VanduoLazyLoad, { aliases: ["LazyLoad"] });
6680
7296
  }
6681
7297
  window.VanduoLazyLoad = VanduoLazyLoad;
6682
7298
  })();
@@ -6687,8 +7303,8 @@
6687
7303
  const GlassScroll = {
6688
7304
  /** @type {Map<Element, IntersectionObserver>} */
6689
7305
  observers: /* @__PURE__ */ new Map(),
6690
- init: function() {
6691
- document.querySelectorAll("[data-glass-scroll]").forEach((el) => {
7306
+ init: function(root) {
7307
+ window.Vanduo.queryAll(root, "[data-glass-scroll]").forEach((el) => {
6692
7308
  if (this.observers.has(el)) return;
6693
7309
  this.initElement(el);
6694
7310
  });
@@ -6748,8 +7364,8 @@
6748
7364
  const MORPH_DURATION_MS = 750;
6749
7365
  const Morph = {
6750
7366
  instances: /* @__PURE__ */ new Map(),
6751
- init: function() {
6752
- 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]");
6753
7369
  elements.forEach(function(el) {
6754
7370
  if (Morph.instances.has(el)) return;
6755
7371
  if (el.getAttribute("data-vd-morph") === "manual") return;
@@ -6854,8 +7470,8 @@
6854
7470
  "use strict";
6855
7471
  const ExpandingCards = {
6856
7472
  instances: /* @__PURE__ */ new Map(),
6857
- init: function() {
6858
- document.querySelectorAll(".vd-expanding-cards").forEach(function(el) {
7473
+ init: function(root) {
7474
+ window.Vanduo.queryAll(root, ".vd-expanding-cards").forEach(function(el) {
6859
7475
  if (el.getAttribute("data-vd-expanding-cards") === "manual") return;
6860
7476
  if (ExpandingCards.instances.has(el)) return;
6861
7477
  ExpandingCards.initContainer(el);
@@ -6987,6 +7603,8 @@
6987
7603
  const playBtn = scope.querySelector("[data-vd-timeline-play]");
6988
7604
  const pauseBtn = scope.querySelector("[data-vd-timeline-pause]");
6989
7605
  let playTimer = null;
7606
+ let isPlaying = false;
7607
+ let playToken = 0;
6990
7608
  function updateNavButtons() {
6991
7609
  const k = countRevealedPrefix(items);
6992
7610
  const n = items.length;
@@ -7001,10 +7619,10 @@
7001
7619
  nextBtn.setAttribute("aria-disabled", atEnd ? "true" : "false");
7002
7620
  }
7003
7621
  if (playBtn) {
7004
- playBtn.setAttribute("aria-pressed", playTimer ? "true" : "false");
7622
+ playBtn.setAttribute("aria-pressed", isPlaying ? "true" : "false");
7005
7623
  }
7006
7624
  if (pauseBtn) {
7007
- pauseBtn.disabled = !playTimer;
7625
+ pauseBtn.disabled = !isPlaying;
7008
7626
  }
7009
7627
  }
7010
7628
  function stepNext() {
@@ -7021,20 +7639,36 @@
7021
7639
  }
7022
7640
  updateNavButtons();
7023
7641
  }
7024
- function play() {
7025
- if (playTimer) return;
7026
- 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
+ }
7027
7649
  if (countRevealedPrefix(items) >= items.length) {
7028
7650
  pause();
7029
7651
  return;
7030
7652
  }
7031
7653
  stepNext();
7654
+ if (countRevealedPrefix(items) >= items.length) {
7655
+ pause();
7656
+ return;
7657
+ }
7658
+ scheduleNext();
7032
7659
  }, PLAY_INTERVAL_MS);
7660
+ }
7661
+ function play() {
7662
+ if (isPlaying) return;
7663
+ isPlaying = true;
7664
+ scheduleNext();
7033
7665
  updateNavButtons();
7034
7666
  }
7035
7667
  function pause() {
7668
+ isPlaying = false;
7669
+ playToken++;
7036
7670
  if (playTimer) {
7037
- clearInterval(playTimer);
7671
+ clearTimeout(playTimer);
7038
7672
  playTimer = null;
7039
7673
  }
7040
7674
  updateNavButtons();
@@ -7067,15 +7701,15 @@
7067
7701
  }
7068
7702
  const Timeline = {
7069
7703
  instances: /* @__PURE__ */ new Map(),
7070
- init: function() {
7071
- 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) {
7072
7706
  if (Timeline.instances.has(el)) return;
7073
7707
  Timeline.initInstance(el);
7074
7708
  });
7075
7709
  },
7076
- reinit: function() {
7077
- Timeline.destroyAll();
7078
- Timeline.init();
7710
+ reinit: function(root) {
7711
+ Timeline.destroyAll(root);
7712
+ Timeline.init(root);
7079
7713
  },
7080
7714
  initInstance: function(container) {
7081
7715
  const cleanup = [];
@@ -7134,8 +7768,10 @@
7134
7768
  });
7135
7769
  this.instances.delete(container);
7136
7770
  },
7137
- destroyAll: function() {
7771
+ destroyAll: function(root) {
7772
+ const scope = window.Vanduo && typeof window.Vanduo._normalizeRoot === "function" ? window.Vanduo._normalizeRoot(root) : document;
7138
7773
  this.instances.forEach(function(_, el) {
7774
+ if (scope !== document && scope !== el && (!scope.contains || !scope.contains(el))) return;
7139
7775
  Timeline.destroy(el);
7140
7776
  });
7141
7777
  }
@@ -7151,8 +7787,8 @@
7151
7787
  "use strict";
7152
7788
  const Flow = {
7153
7789
  instances: /* @__PURE__ */ new Map(),
7154
- init: function() {
7155
- const carousels = document.querySelectorAll(".vd-flow, .vd-carousel");
7790
+ init: function(root) {
7791
+ const carousels = window.Vanduo.queryAll(root, ".vd-flow, .vd-carousel");
7156
7792
  carousels.forEach((el) => {
7157
7793
  if (this.instances.has(el)) return;
7158
7794
  this.initInstance(el);
@@ -7369,8 +8005,8 @@
7369
8005
  const Bubble = {
7370
8006
  instances: /* @__PURE__ */ new Map(),
7371
8007
  _globalCleanups: [],
7372
- init: function() {
7373
- 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]");
7374
8010
  triggers.forEach((el) => {
7375
8011
  if (this.instances.has(el)) return;
7376
8012
  this.initInstance(el);
@@ -7431,7 +8067,7 @@
7431
8067
  body.className = "vd-bubble-body";
7432
8068
  if (htmlContent) {
7433
8069
  if (typeof sanitizeHtml === "function") {
7434
- body.innerHTML = sanitizeHtml(htmlContent, { allowSvg });
8070
+ body.innerHTML = sanitizeHtml(htmlContent, { allowSvg, allowStyle: false });
7435
8071
  } else {
7436
8072
  body.textContent = htmlContent;
7437
8073
  }
@@ -7542,8 +8178,8 @@
7542
8178
  "use strict";
7543
8179
  const Waypoint = {
7544
8180
  instances: /* @__PURE__ */ new Map(),
7545
- init: function() {
7546
- 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]");
7547
8183
  navs.forEach((nav) => {
7548
8184
  if (this.instances.has(nav)) return;
7549
8185
  this.initInstance(nav);
@@ -7636,8 +8272,8 @@
7636
8272
  "use strict";
7637
8273
  const Ripple = {
7638
8274
  instances: /* @__PURE__ */ new Map(),
7639
- init: function() {
7640
- 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]");
7641
8277
  elements.forEach((el) => {
7642
8278
  if (this.instances.has(el)) return;
7643
8279
  this.initInstance(el);
@@ -7708,8 +8344,8 @@
7708
8344
  }
7709
8345
  const Affix = {
7710
8346
  instances: /* @__PURE__ */ new Map(),
7711
- init: function() {
7712
- 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]");
7713
8349
  elements.forEach((el) => {
7714
8350
  if (this.instances.has(el)) return;
7715
8351
  this.initInstance(el);
@@ -7811,8 +8447,8 @@
7811
8447
  }
7812
8448
  const Suggest = {
7813
8449
  instances: /* @__PURE__ */ new Map(),
7814
- init: function() {
7815
- 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]");
7816
8452
  inputs.forEach((el) => {
7817
8453
  if (this.instances.has(el)) return;
7818
8454
  this.initInstance(el);
@@ -7974,16 +8610,20 @@
7974
8610
  const blurHandler = () => {
7975
8611
  setTimeout(close, 200);
7976
8612
  };
8613
+ const focusHandler = () => {
8614
+ if (input.value.length >= minChars) {
8615
+ doSearch(input.value);
8616
+ }
8617
+ };
7977
8618
  input.addEventListener("input", inputHandler);
7978
8619
  input.addEventListener("keydown", keyHandler);
7979
8620
  input.addEventListener("blur", blurHandler);
7980
- input.addEventListener("focus", () => {
7981
- if (input.value.length >= minChars) doSearch(input.value);
7982
- });
8621
+ input.addEventListener("focus", focusHandler);
7983
8622
  cleanup.push(
7984
8623
  () => input.removeEventListener("input", inputHandler),
7985
8624
  () => input.removeEventListener("keydown", keyHandler),
7986
8625
  () => input.removeEventListener("blur", blurHandler),
8626
+ () => input.removeEventListener("focus", focusHandler),
7987
8627
  () => clearTimeout(debounceTimer),
7988
8628
  () => {
7989
8629
  if (list.parentNode) list.parentNode.removeChild(list);
@@ -8058,8 +8698,8 @@
8058
8698
  pattern: "Invalid format",
8059
8699
  match: "Fields do not match"
8060
8700
  },
8061
- init: function() {
8062
- 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");
8063
8703
  forms.forEach((form) => {
8064
8704
  if (this.instances.has(form)) return;
8065
8705
  this.initInstance(form);
@@ -8296,8 +8936,8 @@
8296
8936
  }
8297
8937
  const Datepicker = {
8298
8938
  instances: /* @__PURE__ */ new Map(),
8299
- init: function() {
8300
- const inputs = document.querySelectorAll("[data-vd-datepicker]");
8939
+ init: function(root) {
8940
+ const inputs = window.Vanduo.queryAll(root, "[data-vd-datepicker]");
8301
8941
  inputs.forEach((el) => {
8302
8942
  if (this.instances.has(el)) return;
8303
8943
  this.initInstance(el);
@@ -8749,8 +9389,8 @@
8749
9389
  "use strict";
8750
9390
  const Timepicker = {
8751
9391
  instances: /* @__PURE__ */ new Map(),
8752
- init: function() {
8753
- const inputs = document.querySelectorAll("[data-vd-timepicker]");
9392
+ init: function(root) {
9393
+ const inputs = window.Vanduo.queryAll(root, "[data-vd-timepicker]");
8754
9394
  inputs.forEach((el) => {
8755
9395
  if (this.instances.has(el)) return;
8756
9396
  this.initInstance(el);
@@ -8864,8 +9504,8 @@
8864
9504
  "use strict";
8865
9505
  const Stepper = {
8866
9506
  instances: /* @__PURE__ */ new Map(),
8867
- init: function() {
8868
- const steppers = document.querySelectorAll(".vd-stepper");
9507
+ init: function(root) {
9508
+ const steppers = window.Vanduo.queryAll(root, ".vd-stepper");
8869
9509
  steppers.forEach((el) => {
8870
9510
  if (this.instances.has(el)) return;
8871
9511
  this.initInstance(el);
@@ -8940,8 +9580,8 @@
8940
9580
  "use strict";
8941
9581
  const Rating = {
8942
9582
  instances: /* @__PURE__ */ new Map(),
8943
- init: function() {
8944
- const ratings = document.querySelectorAll("[data-vd-rating]");
9583
+ init: function(root) {
9584
+ const ratings = window.Vanduo.queryAll(root, "[data-vd-rating]");
8945
9585
  ratings.forEach((el) => {
8946
9586
  if (this.instances.has(el)) return;
8947
9587
  this.initInstance(el);
@@ -9075,8 +9715,8 @@
9075
9715
  "use strict";
9076
9716
  const Transfer = {
9077
9717
  instances: /* @__PURE__ */ new Map(),
9078
- init: function() {
9079
- const transfers = document.querySelectorAll("[data-vd-transfer]");
9718
+ init: function(root) {
9719
+ const transfers = window.Vanduo.queryAll(root, "[data-vd-transfer]");
9080
9720
  transfers.forEach((el) => {
9081
9721
  if (this.instances.has(el)) return;
9082
9722
  this.initInstance(el);
@@ -9239,8 +9879,8 @@
9239
9879
  "use strict";
9240
9880
  const Tree = {
9241
9881
  instances: /* @__PURE__ */ new Map(),
9242
- init: function() {
9243
- const trees = document.querySelectorAll("[data-vd-tree]");
9882
+ init: function(root) {
9883
+ const trees = window.Vanduo.queryAll(root, "[data-vd-tree]");
9244
9884
  trees.forEach((el) => {
9245
9885
  if (this.instances.has(el)) return;
9246
9886
  this.initInstance(el);
@@ -9404,8 +10044,8 @@
9404
10044
  _cleanup: [],
9405
10045
  _boundTriggers: /* @__PURE__ */ new WeakMap(),
9406
10046
  _triggerElement: null,
9407
- init: function() {
9408
- const triggers = document.querySelectorAll("[data-vd-spotlight]");
10047
+ init: function(root) {
10048
+ const triggers = window.Vanduo.queryAll(root, "[data-vd-spotlight]");
9409
10049
  triggers.forEach((trigger) => {
9410
10050
  if (this._boundTriggers.has(trigger)) return;
9411
10051
  const clickHandler = (event) => {
@@ -9678,8 +10318,8 @@
9678
10318
  * Auto-initialize all .vd-music-player / [data-music-player] elements.
9679
10319
  * Options can be provided via data-music-player-options (JSON string).
9680
10320
  */
9681
- init: function() {
9682
- 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) => {
9683
10323
  if (this.instances.has(el)) return;
9684
10324
  let opts = {};
9685
10325
  const attr = el.getAttribute("data-music-player-options");