vanduo-framework 1.1.8-docs-update

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 (196) hide show
  1. package/LICENSE +35 -0
  2. package/README.md +216 -0
  3. package/css/components/alerts.css +224 -0
  4. package/css/components/avatar.css +275 -0
  5. package/css/components/badges.css +230 -0
  6. package/css/components/breadcrumbs.css +146 -0
  7. package/css/components/button-group.css +82 -0
  8. package/css/components/buttons.css +530 -0
  9. package/css/components/cards.css +304 -0
  10. package/css/components/chips.css +259 -0
  11. package/css/components/code-snippet.css +555 -0
  12. package/css/components/collapsible.css +267 -0
  13. package/css/components/collections.css +253 -0
  14. package/css/components/doc-search.css +464 -0
  15. package/css/components/doc-tabs.css +38 -0
  16. package/css/components/draggable.css +317 -0
  17. package/css/components/dropdown.css +266 -0
  18. package/css/components/footer.css +375 -0
  19. package/css/components/forms.css +1774 -0
  20. package/css/components/image-box.css +279 -0
  21. package/css/components/modals.css +285 -0
  22. package/css/components/navbar.css +530 -0
  23. package/css/components/pagination.css +186 -0
  24. package/css/components/preloader.css +340 -0
  25. package/css/components/progress.css +107 -0
  26. package/css/components/sidenav.css +301 -0
  27. package/css/components/skeleton.css +241 -0
  28. package/css/components/spinner.css +144 -0
  29. package/css/components/tabs.css +327 -0
  30. package/css/components/theme-customizer.css +835 -0
  31. package/css/components/toast.css +357 -0
  32. package/css/components/tooltips.css +270 -0
  33. package/css/core/colors.css +1017 -0
  34. package/css/core/fonts.css +266 -0
  35. package/css/core/grid.css +1699 -0
  36. package/css/core/helpers.css +2202 -0
  37. package/css/core/reset.css +128 -0
  38. package/css/core/tokens.css +213 -0
  39. package/css/core/typography.css +405 -0
  40. package/css/core/vd-aliases.css +47 -0
  41. package/css/effects/parallax.css +113 -0
  42. package/css/icons/icons-all.css +23 -0
  43. package/css/icons/icons.css +25 -0
  44. package/css/utilities/media.css +167 -0
  45. package/css/utilities/print.css +111 -0
  46. package/css/utilities/shadow.css +243 -0
  47. package/css/utilities/table.css +381 -0
  48. package/css/utilities/transforms.css +71 -0
  49. package/css/utilities/transitions.css +87 -0
  50. package/css/vanduo.css +80 -0
  51. package/dist/build-info.json +6 -0
  52. package/dist/fonts/fira-sans/fira-sans-bold.woff2 +0 -0
  53. package/dist/fonts/fira-sans/fira-sans-medium.woff2 +0 -0
  54. package/dist/fonts/fira-sans/fira-sans-regular.woff2 +0 -0
  55. package/dist/fonts/ibm-plex/ibm-plex-sans-bold.woff2 +0 -0
  56. package/dist/fonts/ibm-plex/ibm-plex-sans-medium.woff2 +0 -0
  57. package/dist/fonts/ibm-plex/ibm-plex-sans-regular.woff2 +0 -0
  58. package/dist/fonts/inter/inter-bold.woff2 +0 -0
  59. package/dist/fonts/inter/inter-medium.woff2 +0 -0
  60. package/dist/fonts/inter/inter-regular.woff2 +0 -0
  61. package/dist/fonts/inter/inter-semibold.woff2 +0 -0
  62. package/dist/fonts/jetbrains-mono/jetbrains-mono-bold.woff2 +0 -0
  63. package/dist/fonts/jetbrains-mono/jetbrains-mono-regular.woff2 +0 -0
  64. package/dist/fonts/open-sans/open-sans-bold.woff2 +0 -0
  65. package/dist/fonts/open-sans/open-sans-medium.woff2 +0 -0
  66. package/dist/fonts/open-sans/open-sans-regular.woff2 +0 -0
  67. package/dist/fonts/rubik/rubik-bold.woff2 +0 -0
  68. package/dist/fonts/rubik/rubik-medium.woff2 +0 -0
  69. package/dist/fonts/rubik/rubik-regular.woff2 +0 -0
  70. package/dist/fonts/source-sans/source-sans-bold.woff2 +0 -0
  71. package/dist/fonts/source-sans/source-sans-regular.woff2 +0 -0
  72. package/dist/fonts/source-sans/source-sans-semibold.woff2 +0 -0
  73. package/dist/fonts/titillium-web/titillium-web-bold.woff2 +0 -0
  74. package/dist/fonts/titillium-web/titillium-web-regular.woff2 +0 -0
  75. package/dist/fonts/titillium-web/titillium-web-semibold.woff2 +0 -0
  76. package/dist/fonts/ubuntu/ubuntu-bold.woff2 +0 -0
  77. package/dist/fonts/ubuntu/ubuntu-medium.woff2 +0 -0
  78. package/dist/fonts/ubuntu/ubuntu-regular.woff2 +0 -0
  79. package/dist/icons/phosphor/LICENSE +21 -0
  80. package/dist/icons/phosphor/bold/Phosphor-Bold.ttf +0 -0
  81. package/dist/icons/phosphor/bold/Phosphor-Bold.woff +0 -0
  82. package/dist/icons/phosphor/bold/Phosphor-Bold.woff2 +0 -0
  83. package/dist/icons/phosphor/bold/style.css +4627 -0
  84. package/dist/icons/phosphor/duotone/Phosphor-Duotone.ttf +0 -0
  85. package/dist/icons/phosphor/duotone/Phosphor-Duotone.woff +0 -0
  86. package/dist/icons/phosphor/duotone/Phosphor-Duotone.woff2 +0 -0
  87. package/dist/icons/phosphor/duotone/style.css +12115 -0
  88. package/dist/icons/phosphor/fill/Phosphor-Fill.ttf +0 -0
  89. package/dist/icons/phosphor/fill/Phosphor-Fill.woff +0 -0
  90. package/dist/icons/phosphor/fill/Phosphor-Fill.woff2 +0 -0
  91. package/dist/icons/phosphor/fill/style.css +4627 -0
  92. package/dist/icons/phosphor/light/Phosphor-Light.ttf +0 -0
  93. package/dist/icons/phosphor/light/Phosphor-Light.woff +0 -0
  94. package/dist/icons/phosphor/light/Phosphor-Light.woff2 +0 -0
  95. package/dist/icons/phosphor/light/style.css +4627 -0
  96. package/dist/icons/phosphor/regular/Phosphor.ttf +0 -0
  97. package/dist/icons/phosphor/regular/Phosphor.woff +0 -0
  98. package/dist/icons/phosphor/regular/Phosphor.woff2 +0 -0
  99. package/dist/icons/phosphor/regular/style.css +4627 -0
  100. package/dist/icons/phosphor/thin/Phosphor-Thin.ttf +0 -0
  101. package/dist/icons/phosphor/thin/Phosphor-Thin.woff +0 -0
  102. package/dist/icons/phosphor/thin/Phosphor-Thin.woff2 +0 -0
  103. package/dist/icons/phosphor/thin/style.css +4627 -0
  104. package/dist/vanduo.cjs.js +5569 -0
  105. package/dist/vanduo.cjs.js.map +7 -0
  106. package/dist/vanduo.cjs.min.js +48 -0
  107. package/dist/vanduo.cjs.min.js.map +7 -0
  108. package/dist/vanduo.css +60666 -0
  109. package/dist/vanduo.css.map +1 -0
  110. package/dist/vanduo.esm.js +5548 -0
  111. package/dist/vanduo.esm.js.map +7 -0
  112. package/dist/vanduo.esm.min.js +48 -0
  113. package/dist/vanduo.esm.min.js.map +7 -0
  114. package/dist/vanduo.js +5545 -0
  115. package/dist/vanduo.js.map +7 -0
  116. package/dist/vanduo.min.css +2 -0
  117. package/dist/vanduo.min.css.map +1 -0
  118. package/dist/vanduo.min.js +48 -0
  119. package/dist/vanduo.min.js.map +7 -0
  120. package/fonts/fira-sans/fira-sans-bold.woff2 +0 -0
  121. package/fonts/fira-sans/fira-sans-medium.woff2 +0 -0
  122. package/fonts/fira-sans/fira-sans-regular.woff2 +0 -0
  123. package/fonts/ibm-plex/ibm-plex-sans-bold.woff2 +0 -0
  124. package/fonts/ibm-plex/ibm-plex-sans-medium.woff2 +0 -0
  125. package/fonts/ibm-plex/ibm-plex-sans-regular.woff2 +0 -0
  126. package/fonts/inter/inter-bold.woff2 +0 -0
  127. package/fonts/inter/inter-medium.woff2 +0 -0
  128. package/fonts/inter/inter-regular.woff2 +0 -0
  129. package/fonts/inter/inter-semibold.woff2 +0 -0
  130. package/fonts/jetbrains-mono/jetbrains-mono-bold.woff2 +0 -0
  131. package/fonts/jetbrains-mono/jetbrains-mono-regular.woff2 +0 -0
  132. package/fonts/open-sans/open-sans-bold.woff2 +0 -0
  133. package/fonts/open-sans/open-sans-medium.woff2 +0 -0
  134. package/fonts/open-sans/open-sans-regular.woff2 +0 -0
  135. package/fonts/rubik/rubik-bold.woff2 +0 -0
  136. package/fonts/rubik/rubik-medium.woff2 +0 -0
  137. package/fonts/rubik/rubik-regular.woff2 +0 -0
  138. package/fonts/source-sans/source-sans-bold.woff2 +0 -0
  139. package/fonts/source-sans/source-sans-regular.woff2 +0 -0
  140. package/fonts/source-sans/source-sans-semibold.woff2 +0 -0
  141. package/fonts/titillium-web/titillium-web-bold.woff2 +0 -0
  142. package/fonts/titillium-web/titillium-web-regular.woff2 +0 -0
  143. package/fonts/titillium-web/titillium-web-semibold.woff2 +0 -0
  144. package/fonts/ubuntu/ubuntu-bold.woff2 +0 -0
  145. package/fonts/ubuntu/ubuntu-medium.woff2 +0 -0
  146. package/fonts/ubuntu/ubuntu-regular.woff2 +0 -0
  147. package/icons/phosphor/LICENSE +21 -0
  148. package/icons/phosphor/bold/Phosphor-Bold.ttf +0 -0
  149. package/icons/phosphor/bold/Phosphor-Bold.woff +0 -0
  150. package/icons/phosphor/bold/Phosphor-Bold.woff2 +0 -0
  151. package/icons/phosphor/bold/style.css +4627 -0
  152. package/icons/phosphor/duotone/Phosphor-Duotone.ttf +0 -0
  153. package/icons/phosphor/duotone/Phosphor-Duotone.woff +0 -0
  154. package/icons/phosphor/duotone/Phosphor-Duotone.woff2 +0 -0
  155. package/icons/phosphor/duotone/style.css +12115 -0
  156. package/icons/phosphor/fill/Phosphor-Fill.ttf +0 -0
  157. package/icons/phosphor/fill/Phosphor-Fill.woff +0 -0
  158. package/icons/phosphor/fill/Phosphor-Fill.woff2 +0 -0
  159. package/icons/phosphor/fill/style.css +4627 -0
  160. package/icons/phosphor/light/Phosphor-Light.ttf +0 -0
  161. package/icons/phosphor/light/Phosphor-Light.woff +0 -0
  162. package/icons/phosphor/light/Phosphor-Light.woff2 +0 -0
  163. package/icons/phosphor/light/style.css +4627 -0
  164. package/icons/phosphor/regular/Phosphor.ttf +0 -0
  165. package/icons/phosphor/regular/Phosphor.woff +0 -0
  166. package/icons/phosphor/regular/Phosphor.woff2 +0 -0
  167. package/icons/phosphor/regular/style.css +4627 -0
  168. package/icons/phosphor/thin/Phosphor-Thin.ttf +0 -0
  169. package/icons/phosphor/thin/Phosphor-Thin.woff +0 -0
  170. package/icons/phosphor/thin/Phosphor-Thin.woff2 +0 -0
  171. package/icons/phosphor/thin/style.css +4627 -0
  172. package/js/components/code-snippet.js +639 -0
  173. package/js/components/collapsible.js +226 -0
  174. package/js/components/doc-search.js +936 -0
  175. package/js/components/draggable.js +725 -0
  176. package/js/components/dropdown.js +362 -0
  177. package/js/components/font-switcher.js +253 -0
  178. package/js/components/grid.js +279 -0
  179. package/js/components/image-box.js +372 -0
  180. package/js/components/modals.js +367 -0
  181. package/js/components/navbar.js +264 -0
  182. package/js/components/pagination.js +286 -0
  183. package/js/components/parallax.js +216 -0
  184. package/js/components/preloader.js +183 -0
  185. package/js/components/select.js +444 -0
  186. package/js/components/sidenav.js +303 -0
  187. package/js/components/tabs.js +303 -0
  188. package/js/components/theme-customizer.js +784 -0
  189. package/js/components/theme-switcher.js +183 -0
  190. package/js/components/toast.js +343 -0
  191. package/js/components/tooltips.js +306 -0
  192. package/js/index.js +52 -0
  193. package/js/utils/helpers.js +306 -0
  194. package/js/utils/lifecycle.js +135 -0
  195. package/js/vanduo.js +120 -0
  196. package/package.json +78 -0
@@ -0,0 +1,306 @@
1
+ /**
2
+ * Vanduo Framework - Tooltips Component
3
+ * JavaScript functionality for tooltips
4
+ */
5
+
6
+ (function () {
7
+ 'use strict';
8
+
9
+ /**
10
+ * Tooltips Component
11
+ */
12
+ const Tooltips = {
13
+ tooltips: new Map(),
14
+ delayTimers: new Map(),
15
+
16
+ /**
17
+ * Sanitize HTML — delegates to shared sanitizeHtml from helpers.js
18
+ * @param {string} input
19
+ * @returns {string} sanitized HTML
20
+ */
21
+ sanitizeHtml: function (input) {
22
+ if (typeof sanitizeHtml === 'function') {
23
+ return sanitizeHtml(input);
24
+ }
25
+ // Fallback: strip all HTML
26
+ var div = document.createElement('div');
27
+ div.textContent = input || '';
28
+ return div.innerHTML;
29
+ },
30
+
31
+ /**
32
+ * Initialize tooltips
33
+ */
34
+ init: function () {
35
+ const elements = document.querySelectorAll('[data-tooltip], [data-tooltip-html]');
36
+
37
+ elements.forEach(element => {
38
+ if (this.tooltips.has(element)) {
39
+ return;
40
+ }
41
+ this.initTooltip(element);
42
+ });
43
+ },
44
+
45
+ /**
46
+ * Initialize a single tooltip
47
+ * @param {HTMLElement} element - Element with tooltip
48
+ */
49
+ initTooltip: function (element) {
50
+ const tooltip = this.createTooltip(element);
51
+ const cleanupFunctions = [];
52
+
53
+ // Show on hover/focus
54
+ const enterHandler = () => { this.showTooltip(element, tooltip); };
55
+ const leaveHandler = () => { this.hideTooltip(element, tooltip); };
56
+ const focusHandler = () => { this.showTooltip(element, tooltip); };
57
+ const blurHandler = () => { this.hideTooltip(element, tooltip); };
58
+
59
+ element.addEventListener('mouseenter', enterHandler);
60
+ element.addEventListener('mouseleave', leaveHandler);
61
+ element.addEventListener('focus', focusHandler);
62
+ element.addEventListener('blur', blurHandler);
63
+
64
+ cleanupFunctions.push(
65
+ () => element.removeEventListener('mouseenter', enterHandler),
66
+ () => element.removeEventListener('mouseleave', leaveHandler),
67
+ () => element.removeEventListener('focus', focusHandler),
68
+ () => element.removeEventListener('blur', blurHandler)
69
+ );
70
+
71
+ this.tooltips.set(element, { tooltip, cleanup: cleanupFunctions });
72
+ },
73
+
74
+ /**
75
+ * Create tooltip element
76
+ * @param {HTMLElement} element - Target element
77
+ * @returns {HTMLElement} Tooltip element
78
+ */
79
+ createTooltip: function (element) {
80
+ const tooltip = document.createElement('div');
81
+ tooltip.className = 'vd-tooltip';
82
+ tooltip.setAttribute('role', 'tooltip');
83
+ tooltip.setAttribute('aria-hidden', 'true');
84
+
85
+ // Generate unique ID and link via aria-describedby
86
+ const tooltipId = 'tooltip-' + Math.random().toString(36).substr(2, 9);
87
+ tooltip.id = tooltipId;
88
+ element.setAttribute('aria-describedby', tooltipId);
89
+
90
+ // Get content
91
+ const htmlContent = element.dataset.tooltipHtml;
92
+ const textContent = element.dataset.tooltip;
93
+
94
+ if (htmlContent) {
95
+ tooltip.innerHTML = this.sanitizeHtml(htmlContent);
96
+ tooltip.classList.add('vd-tooltip-html');
97
+ } else if (textContent) {
98
+ tooltip.textContent = textContent;
99
+ }
100
+
101
+ // Get placement
102
+ const placement = element.dataset.tooltipPlacement || element.dataset.placement || 'top';
103
+ tooltip.setAttribute('data-placement', placement);
104
+ tooltip.classList.add(`vd-tooltip-${placement}`);
105
+
106
+ // Get variant
107
+ if (element.dataset.tooltipVariant) {
108
+ tooltip.classList.add(`vd-tooltip-${element.dataset.tooltipVariant}`);
109
+ }
110
+
111
+ // Get size
112
+ if (element.dataset.tooltipSize) {
113
+ tooltip.classList.add(`vd-tooltip-${element.dataset.tooltipSize}`);
114
+ }
115
+
116
+ // Get delay
117
+ const delay = parseInt(element.dataset.tooltipDelay) || 0;
118
+ tooltip.dataset.delay = delay;
119
+
120
+ document.body.appendChild(tooltip);
121
+
122
+ return tooltip;
123
+ },
124
+
125
+ /**
126
+ * Show tooltip
127
+ * @param {HTMLElement} element - Target element
128
+ * @param {HTMLElement} tooltip - Tooltip element
129
+ */
130
+ showTooltip: function (element, tooltip) {
131
+ const delay = parseInt(tooltip.dataset.delay) || 0;
132
+
133
+ if (delay > 0) {
134
+ const timer = setTimeout(() => {
135
+ this.positionTooltip(element, tooltip);
136
+ tooltip.classList.add('is-visible');
137
+ tooltip.setAttribute('aria-hidden', 'false');
138
+ }, delay);
139
+ this.delayTimers.set(element, timer);
140
+ } else {
141
+ this.positionTooltip(element, tooltip);
142
+ tooltip.classList.add('is-visible');
143
+ tooltip.setAttribute('aria-hidden', 'false');
144
+ }
145
+ },
146
+
147
+ /**
148
+ * Hide tooltip
149
+ * @param {HTMLElement} element - Target element
150
+ * @param {HTMLElement} tooltip - Tooltip element
151
+ */
152
+ hideTooltip: function (element, tooltip) {
153
+ // Clear delay timer if exists
154
+ const timer = this.delayTimers.get(element);
155
+ if (timer) {
156
+ clearTimeout(timer);
157
+ this.delayTimers.delete(element);
158
+ }
159
+
160
+ tooltip.classList.remove('is-visible');
161
+ tooltip.setAttribute('aria-hidden', 'true');
162
+ },
163
+
164
+ /**
165
+ * Position tooltip relative to element
166
+ * @param {HTMLElement} element - Target element
167
+ * @param {HTMLElement} tooltip - Tooltip element
168
+ */
169
+ positionTooltip: function (element, tooltip) {
170
+ const placement = tooltip.dataset.placement || 'top';
171
+ const rect = element.getBoundingClientRect();
172
+ const tooltipRect = tooltip.getBoundingClientRect();
173
+ const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
174
+ const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
175
+
176
+ let top = 0;
177
+ let left = 0;
178
+
179
+ switch (placement) {
180
+ case 'top':
181
+ top = rect.top + scrollTop - tooltipRect.height - 8;
182
+ left = rect.left + scrollLeft + (rect.width / 2) - (tooltipRect.width / 2);
183
+ break;
184
+ case 'bottom':
185
+ top = rect.bottom + scrollTop + 8;
186
+ left = rect.left + scrollLeft + (rect.width / 2) - (tooltipRect.width / 2);
187
+ break;
188
+ case 'left':
189
+ top = rect.top + scrollTop + (rect.height / 2) - (tooltipRect.height / 2);
190
+ left = rect.left + scrollLeft - tooltipRect.width - 8;
191
+ break;
192
+ case 'right':
193
+ top = rect.top + scrollTop + (rect.height / 2) - (tooltipRect.height / 2);
194
+ left = rect.right + scrollLeft + 8;
195
+ break;
196
+ }
197
+
198
+ // Prevent overflow
199
+ const viewportWidth = window.innerWidth;
200
+ const viewportHeight = window.innerHeight;
201
+ const padding = 8;
202
+
203
+ if (left < padding) {
204
+ left = padding;
205
+ } else if (left + tooltipRect.width > viewportWidth - padding) {
206
+ left = viewportWidth - tooltipRect.width - padding;
207
+ }
208
+
209
+ if (top < scrollTop + padding) {
210
+ top = scrollTop + padding;
211
+ } else if (top + tooltipRect.height > scrollTop + viewportHeight - padding) {
212
+ top = scrollTop + viewportHeight - tooltipRect.height - padding;
213
+ }
214
+
215
+ // Use single style assignment with transform for better performance
216
+ tooltip.style.cssText = `position: absolute; top: 0; left: 0; transform: translate(${left}px, ${top}px);`;
217
+ },
218
+
219
+ /**
220
+ * Show tooltip programmatically
221
+ * @param {HTMLElement|string} element - Target element or selector
222
+ */
223
+ show: function (element) {
224
+ const el = typeof element === 'string' ? document.querySelector(element) : element;
225
+ if (el && this.tooltips.has(el)) {
226
+ const { tooltip } = this.tooltips.get(el);
227
+ this.showTooltip(el, tooltip);
228
+ }
229
+ },
230
+
231
+ /**
232
+ * Hide tooltip programmatically
233
+ * @param {HTMLElement|string} element - Target element or selector
234
+ */
235
+ hide: function (element) {
236
+ const el = typeof element === 'string' ? document.querySelector(element) : element;
237
+ if (el && this.tooltips.has(el)) {
238
+ const { tooltip } = this.tooltips.get(el);
239
+ this.hideTooltip(el, tooltip);
240
+ }
241
+ },
242
+
243
+ /**
244
+ * Update tooltip content
245
+ * @param {HTMLElement|string} element - Target element or selector
246
+ * @param {string} content - New content
247
+ * @param {boolean} isHtml - Whether content is HTML
248
+ */
249
+ update: function (element, content, isHtml = false) {
250
+ const el = typeof element === 'string' ? document.querySelector(element) : element;
251
+ if (el && this.tooltips.has(el)) {
252
+ const { tooltip } = this.tooltips.get(el);
253
+ if (isHtml) {
254
+ tooltip.innerHTML = this.sanitizeHtml(content);
255
+ tooltip.classList.add('vd-tooltip-html');
256
+ } else {
257
+ tooltip.textContent = content;
258
+ tooltip.classList.remove('vd-tooltip-html');
259
+ }
260
+ }
261
+ },
262
+
263
+ /**
264
+ * Destroy a tooltip instance and clean up
265
+ * @param {HTMLElement} element - Element with tooltip
266
+ */
267
+ destroy: function (element) {
268
+ const data = this.tooltips.get(element);
269
+ if (!data) return;
270
+
271
+ // Clear any pending timer
272
+ const timer = this.delayTimers.get(element);
273
+ if (timer) {
274
+ clearTimeout(timer);
275
+ this.delayTimers.delete(element);
276
+ }
277
+
278
+ data.cleanup.forEach(fn => fn());
279
+
280
+ // Remove tooltip element from DOM
281
+ if (data.tooltip && data.tooltip.parentNode) {
282
+ data.tooltip.parentNode.removeChild(data.tooltip);
283
+ }
284
+
285
+ this.tooltips.delete(element);
286
+ },
287
+
288
+ /**
289
+ * Destroy all tooltip instances
290
+ */
291
+ destroyAll: function () {
292
+ this.tooltips.forEach((data, element) => {
293
+ this.destroy(element);
294
+ });
295
+ }
296
+ };
297
+
298
+ // Register with Vanduo framework if available
299
+ if (typeof window.Vanduo !== 'undefined') {
300
+ window.Vanduo.register('tooltips', Tooltips);
301
+ }
302
+
303
+ // Expose globally
304
+ window.VanduoTooltips = Tooltips;
305
+
306
+ })();
package/js/index.js ADDED
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Vanduo Framework - Bundle Entry Point
3
+ * This file imports all framework components for bundling.
4
+ *
5
+ * All component files are side-effect modules that:
6
+ * 1. Define their component object
7
+ * 2. Register with window.Vanduo via Vanduo.register()
8
+ * 3. Expose a convenience global (e.g. window.VanduoTooltips)
9
+ *
10
+ * The IIFE build uses `globalName: 'VanduoBundle'` so that esbuild's
11
+ * wrapper variable does NOT shadow the real `window.Vanduo` that the
12
+ * side-effect scripts create. After the bundle executes, `window.Vanduo`
13
+ * is the fully-populated framework object.
14
+ *
15
+ * For ESM/CJS consumers we re-export `window.Vanduo` as the default
16
+ * and named export so `import { Vanduo }` and `const { Vanduo } = require()`
17
+ * both work.
18
+ */
19
+
20
+ // Utilities (must load first — helpers defines `ready()`, `safeStorageGet()` etc.)
21
+ import './utils/helpers.js';
22
+ import './utils/lifecycle.js';
23
+
24
+ // Core framework object (creates window.Vanduo)
25
+ import './vanduo.js';
26
+
27
+ // Components (each registers itself with window.Vanduo)
28
+ import './components/code-snippet.js';
29
+ import './components/collapsible.js';
30
+ import './components/dropdown.js';
31
+ import './components/font-switcher.js';
32
+ import './components/grid.js';
33
+ import './components/image-box.js';
34
+ import './components/modals.js';
35
+ import './components/navbar.js';
36
+ import './components/pagination.js';
37
+ import './components/parallax.js';
38
+ import './components/preloader.js';
39
+ import './components/select.js';
40
+ import './components/sidenav.js';
41
+ import './components/tabs.js';
42
+ import './components/theme-customizer.js';
43
+ import './components/theme-switcher.js';
44
+ import './components/toast.js';
45
+ import './components/tooltips.js';
46
+ import './components/doc-search.js';
47
+ import './components/draggable.js';
48
+
49
+ // Re-export for ESM / CJS consumers
50
+ const Vanduo = window.Vanduo;
51
+ export { Vanduo };
52
+ export default Vanduo;
@@ -0,0 +1,306 @@
1
+ /**
2
+ * Vanduo Framework - Utility Helpers
3
+ * Common utility functions used across the framework
4
+ */
5
+
6
+ /**
7
+ * Check if element exists
8
+ * @param {string|HTMLElement} selector - CSS selector or element
9
+ * @returns {HTMLElement|null}
10
+ */
11
+ function $(selector) {
12
+ if (typeof selector === 'string') {
13
+ return document.querySelector(selector);
14
+ }
15
+ return selector;
16
+ }
17
+
18
+ /**
19
+ * Get all elements matching selector
20
+ * @param {string} selector - CSS selector
21
+ * @returns {NodeList}
22
+ */
23
+ function $$(selector) {
24
+ return document.querySelectorAll(selector);
25
+ }
26
+
27
+ /**
28
+ * Wait for DOM to be ready
29
+ * @param {Function} callback - Function to execute when DOM is ready
30
+ */
31
+ function ready(callback) {
32
+ if (document.readyState === 'loading') {
33
+ document.addEventListener('DOMContentLoaded', callback);
34
+ } else {
35
+ callback();
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Safely get a value from localStorage
41
+ * @param {string} key - Storage key
42
+ * @param {string|null} fallback - Fallback when storage is unavailable
43
+ * @returns {string|null}
44
+ */
45
+ function safeStorageGet(key, fallback = null) {
46
+ try {
47
+ const value = localStorage.getItem(key);
48
+ return value !== null ? value : fallback;
49
+ } catch (_e) {
50
+ return fallback;
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Safely set a value in localStorage
56
+ * @param {string} key - Storage key
57
+ * @param {string} value - Value to store
58
+ * @returns {boolean}
59
+ */
60
+ function safeStorageSet(key, value) {
61
+ try {
62
+ localStorage.setItem(key, value);
63
+ return true;
64
+ } catch (_e) {
65
+ return false;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Add event listener with delegation support
71
+ * @param {string|HTMLElement} target - Target element or selector
72
+ * @param {string} event - Event type
73
+ * @param {string|Function} handlerOrSelector - Event handler or selector for delegation
74
+ * @param {Function} handler - Event handler (if using delegation)
75
+ */
76
+ function on(target, event, handlerOrSelector, handler) {
77
+ const element = typeof target === 'string' ? $(target) : target;
78
+
79
+ if (!element) return;
80
+
81
+ if (typeof handlerOrSelector === 'function') {
82
+ // Direct event binding
83
+ element.addEventListener(event, handlerOrSelector);
84
+ } else {
85
+ // Event delegation
86
+ element.addEventListener(event, function (e) {
87
+ const delegateTarget = e.target.closest(handlerOrSelector);
88
+ if (delegateTarget && element.contains(delegateTarget)) {
89
+ handler.call(delegateTarget, e);
90
+ }
91
+ });
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Remove event listener
97
+ * @param {string|HTMLElement} target - Target element or selector
98
+ * @param {string} event - Event type
99
+ * @param {Function} handler - Event handler
100
+ */
101
+ function off(target, event, handler) {
102
+ const element = typeof target === 'string' ? $(target) : target;
103
+ if (element) {
104
+ element.removeEventListener(event, handler);
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Toggle class on element
110
+ * @param {string|HTMLElement} selector - CSS selector or element
111
+ * @param {string} className - Class name to toggle
112
+ */
113
+ function toggleClass(selector, className) {
114
+ const element = typeof selector === 'string' ? $(selector) : selector;
115
+ if (element) {
116
+ element.classList.toggle(className);
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Add class to element
122
+ * @param {string|HTMLElement} selector - CSS selector or element
123
+ * @param {string} className - Class name to add
124
+ */
125
+ function addClass(selector, className) {
126
+ const element = typeof selector === 'string' ? $(selector) : selector;
127
+ if (element) {
128
+ element.classList.add(className);
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Remove class from element
134
+ * @param {string|HTMLElement} selector - CSS selector or element
135
+ * @param {string} className - Class name to remove
136
+ */
137
+ function removeClass(selector, className) {
138
+ const element = typeof selector === 'string' ? $(selector) : selector;
139
+ if (element) {
140
+ element.classList.remove(className);
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Check if element has class
146
+ * @param {string|HTMLElement} selector - CSS selector or element
147
+ * @param {string} className - Class name to check
148
+ * @returns {boolean}
149
+ */
150
+ function hasClass(selector, className) {
151
+ const element = typeof selector === 'string' ? $(selector) : selector;
152
+ return element ? element.classList.contains(className) : false;
153
+ }
154
+
155
+ /**
156
+ * Get or set data attribute
157
+ * @param {HTMLElement} element - Element
158
+ * @param {string} name - Data attribute name (without data- prefix)
159
+ * @param {string} value - Value to set (optional)
160
+ * @returns {string|undefined}
161
+ */
162
+ function data(element, name, value) {
163
+ if (value !== undefined) {
164
+ element.setAttribute(`data-${name}`, value);
165
+ return value;
166
+ }
167
+ return element.getAttribute(`data-${name}`);
168
+ }
169
+
170
+ /**
171
+ * Debounce function
172
+ * @param {Function} func - Function to debounce
173
+ * @param {number} wait - Wait time in milliseconds
174
+ * @returns {Function}
175
+ */
176
+ function debounce(func, wait) {
177
+ let timeout;
178
+ return function executedFunction(...args) {
179
+ const later = () => {
180
+ clearTimeout(timeout);
181
+ func(...args);
182
+ };
183
+ clearTimeout(timeout);
184
+ timeout = setTimeout(later, wait);
185
+ };
186
+ }
187
+
188
+ /**
189
+ * Throttle function
190
+ * @param {Function} func - Function to throttle
191
+ * @param {number} limit - Time limit in milliseconds
192
+ * @returns {Function}
193
+ */
194
+ function throttle(func, limit) {
195
+ let inThrottle;
196
+ return function (...args) {
197
+ if (!inThrottle) {
198
+ func.apply(this, args);
199
+ inThrottle = true;
200
+ setTimeout(() => inThrottle = false, limit);
201
+ }
202
+ };
203
+ }
204
+
205
+ /**
206
+ * Check if element is visible
207
+ * @param {HTMLElement} element - Element to check
208
+ * @returns {boolean}
209
+ */
210
+ function isVisible(element) {
211
+ if (!element) return false;
212
+ const style = window.getComputedStyle(element);
213
+ return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0';
214
+ }
215
+
216
+ /**
217
+ * Get element position relative to viewport
218
+ * @param {HTMLElement} element - Element
219
+ * @returns {Object} - Object with top, left, right, bottom, width, height
220
+ */
221
+ function getPosition(element) {
222
+ if (!element) return null;
223
+ const rect = element.getBoundingClientRect();
224
+ return {
225
+ top: rect.top,
226
+ left: rect.left,
227
+ right: rect.right,
228
+ bottom: rect.bottom,
229
+ width: rect.width,
230
+ height: rect.height
231
+ };
232
+ }
233
+
234
+ /**
235
+ * Escape HTML special characters to prevent injection
236
+ * @param {string} str - String to escape
237
+ * @returns {string} Escaped string safe for insertion into HTML
238
+ */
239
+ function escapeHtml(str) {
240
+ if (!str) return '';
241
+ var div = document.createElement('div');
242
+ div.appendChild(document.createTextNode(str));
243
+ return div.innerHTML;
244
+ }
245
+
246
+ /**
247
+ * Basic HTML sanitizer (whitelist-based) — runs in the browser without external libs.
248
+ * Keeps a small set of tags and strips disallowed tags and attributes. Safe for
249
+ * simple rich text (use server-side or DOMPurify for stronger guarantees).
250
+ * @param {string} input
251
+ * @returns {string} sanitized HTML
252
+ */
253
+ function sanitizeHtml(input) {
254
+ if (!input) return '';
255
+ var doc = new DOMParser().parseFromString(input, 'text/html');
256
+ var allowed = ['B', 'STRONG', 'I', 'EM', 'BR', 'A', 'SPAN', 'U', 'SVG', 'PATH', 'LINE', 'CIRCLE', 'POLYLINE', 'RECT', 'G'];
257
+
258
+ var sanitizeNode = function (node) {
259
+ var children = Array.from(node.childNodes);
260
+ children.forEach(function (child) {
261
+ if (child.nodeType === Node.TEXT_NODE) return;
262
+
263
+ if (!allowed.includes(child.nodeName)) {
264
+ var text = document.createTextNode(child.textContent);
265
+ node.replaceChild(text, child);
266
+ return;
267
+ }
268
+
269
+ if (child.nodeName === 'A') {
270
+ var href = child.getAttribute('href') || '';
271
+ try {
272
+ var url = new URL(href, location.href);
273
+ if (!['http:', 'https:', 'mailto:'].includes(url.protocol)) {
274
+ child.removeAttribute('href');
275
+ }
276
+ } catch (_e) {
277
+ child.removeAttribute('href');
278
+ }
279
+ child.removeAttribute('target');
280
+ child.removeAttribute('rel');
281
+ } else if (child.nodeName === 'SVG' || child.closest && child.closest('svg')) {
282
+ // Allow safe SVG presentation attributes only
283
+ var safeSvgAttrs = ['xmlns', 'width', 'height', 'viewBox', 'fill', 'stroke', 'stroke-width',
284
+ 'stroke-linecap', 'stroke-linejoin', 'd', 'cx', 'cy', 'r', 'x1', 'y1', 'x2', 'y2', 'points',
285
+ 'transform', 'class'];
286
+ var attrs = Array.from(child.attributes || []);
287
+ attrs.forEach(function (a) {
288
+ if (!safeSvgAttrs.includes(a.name)) {
289
+ child.removeAttribute(a.name);
290
+ }
291
+ });
292
+ } else {
293
+ var otherAttrs = Array.from(child.attributes || []);
294
+ otherAttrs.forEach(function (a) { child.removeAttribute(a.name); });
295
+ }
296
+
297
+ sanitizeNode(child);
298
+ });
299
+ };
300
+
301
+ sanitizeNode(doc.body);
302
+ return doc.body.innerHTML;
303
+ }
304
+
305
+
306
+