@kupola/kupola 1.2.0 → 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.
- package/LICENSE +21 -0
- package/README.md +330 -286
- package/adapters/axios.d.ts +34 -0
- package/adapters/axios.js +122 -0
- package/adapters/navios-http.d.ts +110 -0
- package/adapters/navios-http.js +151 -0
- package/css/components-ext.css +157 -0
- package/css/components.css +69 -0
- package/css/theme-dark.css +5 -5
- package/css/utilities.css +119 -1
- package/dist/css/accessibility.css +119 -0
- package/dist/css/animations.css +224 -0
- package/dist/css/brand-themes.css +300 -0
- package/dist/css/colors_and_type.css +441 -0
- package/dist/css/components-ext.css +4165 -0
- package/dist/css/components.css +1483 -0
- package/dist/css/kupola.css +1 -12
- package/dist/css/kupola.min.css +1 -0
- package/dist/css/responsive.css +697 -0
- package/dist/css/scaffold.css +145 -0
- package/dist/css/states.css +316 -0
- package/dist/css/theme-dark.css +296 -0
- package/dist/css/theme-light.css +296 -0
- package/dist/css/utilities.css +171 -0
- package/dist/icons.svg +284 -0
- package/dist/kupola.cjs.js +17409 -257
- package/dist/kupola.cjs.js.map +1 -1
- package/dist/kupola.css +9000 -0
- package/dist/kupola.esm.js +17183 -7597
- package/dist/kupola.esm.js.map +1 -1
- package/dist/kupola.min.css +1 -0
- package/dist/kupola.min.js +2 -0
- package/dist/kupola.min.js.map +1 -0
- package/dist/kupola.umd.js +17415 -257
- package/dist/kupola.umd.js.map +1 -1
- package/dist/plugins/vite-plugin-kupola.js +120 -0
- package/dist/types/kupola.d.ts +421 -323
- package/js/calendar.js +334 -25
- package/js/carousel.js +182 -48
- package/js/collapse.js +148 -34
- package/js/color-picker.js +416 -108
- package/js/component.js +8 -19
- package/js/countdown.js +9 -8
- package/js/data-bind.js +73 -16
- package/js/datepicker.js +488 -110
- package/js/depends.js +710 -0
- package/js/dialog.js +4 -2
- package/js/drawer.js +172 -8
- package/js/dropdown.js +272 -17
- package/js/dynamic-tags.js +156 -40
- package/js/fileupload.js +9 -8
- package/js/form.js +280 -254
- package/js/global-events.js +281 -188
- package/js/heatmap.js +10 -7
- package/js/i18n.js +18 -10
- package/js/icons.js +141 -161
- package/js/image-preview.js +146 -2
- package/js/initializer.js +151 -71
- package/js/kupola-core.js +123 -45
- package/js/kupola-lifecycle.js +13 -11
- package/js/message.js +8 -1
- package/js/modal.js +207 -59
- package/js/notification.js +8 -1
- package/js/numberinput.js +9 -8
- package/js/pagination.js +263 -0
- package/js/registry.js +29 -12
- package/js/select.js +482 -27
- package/js/slide-captcha.js +11 -2
- package/js/slider.js +442 -25
- package/js/statcard.js +9 -7
- package/js/table.js +1210 -0
- package/js/tag.js +268 -14
- package/js/theme-standalone.js +88 -0
- package/js/theme.js +14 -43
- package/js/timepicker.js +335 -66
- package/js/tooltip.js +317 -86
- package/js/utils.js +6 -2
- package/js/validation.js +6 -2
- package/js/virtual-list.js +11 -7
- package/js/web-components.js +288 -0
- package/package.json +81 -67
- package/plugins/vite-plugin-kupola.js +120 -0
- package/scripts/build-css.cjs +113 -0
- package/scripts/build-svg-sprite.cjs +107 -0
- package/types/kupola.d.ts +421 -323
- package/CHANGELOG.md +0 -130
- package/INTEGRATION.md +0 -440
- package/PROJECT_SUMMARY.md +0 -312
- package/SKILL.md +0 -572
- package/dist/utils/utils/Kupola.cs +0 -77
- package/dist/utils/utils/Kupola.java +0 -104
- package/dist/utils/utils/kupola.go +0 -120
- package/dist/utils/utils/kupola.js +0 -63
- package/dist/utils/utils/kupola.py +0 -1392
- package/dist/utils/utils/kupola.rb +0 -69
- package/js/composition-api.js +0 -458
- package/js/error-handler.js +0 -181
- package/js/http.js +0 -419
- package/js/kupola-devtools.js +0 -598
- package/js/performance.js +0 -250
- package/js/router.js +0 -396
- package/js/security.js +0 -189
- package/js/test-utils.js +0 -251
- package/templates/base.html +0 -30
- package/templates/base_dashboard.html +0 -99
- package/utils/Kupola.cs +0 -77
- package/utils/Kupola.java +0 -104
- package/utils/kupola.go +0 -120
- package/utils/kupola.js +0 -63
- package/utils/kupola.py +0 -1392
- package/utils/kupola.rb +0 -69
package/js/image-preview.js
CHANGED
|
@@ -7,6 +7,12 @@ class ImagePreview {
|
|
|
7
7
|
this.keyHandler = this.handleKeydown.bind(this);
|
|
8
8
|
this.clickHandler = this.handleOverlayClick.bind(this);
|
|
9
9
|
|
|
10
|
+
this.zoom = 1;
|
|
11
|
+
this.rotation = 0;
|
|
12
|
+
this.zoomStep = options.zoomStep || 0.2;
|
|
13
|
+
this.minZoom = options.minZoom || 0.5;
|
|
14
|
+
this.maxZoom = options.maxZoom || 3;
|
|
15
|
+
|
|
10
16
|
this.init();
|
|
11
17
|
}
|
|
12
18
|
|
|
@@ -36,6 +42,38 @@ class ImagePreview {
|
|
|
36
42
|
</svg>
|
|
37
43
|
</button>
|
|
38
44
|
</div>
|
|
45
|
+
<div class="ds-image-preview__toolbar">
|
|
46
|
+
<button class="ds-image-preview__toolbar-btn" type="button" aria-label="Zoom in" data-action="zoom-in">
|
|
47
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
48
|
+
<line x1="12" y1="5" x2="12" y2="19"/>
|
|
49
|
+
<line x1="5" y1="12" x2="19" y2="12"/>
|
|
50
|
+
</svg>
|
|
51
|
+
</button>
|
|
52
|
+
<button class="ds-image-preview__toolbar-btn" type="button" aria-label="Zoom out" data-action="zoom-out">
|
|
53
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
54
|
+
<line x1="5" y1="12" x2="19" y2="12"/>
|
|
55
|
+
</svg>
|
|
56
|
+
</button>
|
|
57
|
+
<button class="ds-image-preview__toolbar-btn" type="button" aria-label="Reset zoom" data-action="zoom-reset">
|
|
58
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
59
|
+
<path d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0z"/>
|
|
60
|
+
<line x1="12" y1="5" x2="12" y2="19"/>
|
|
61
|
+
<line x1="5" y1="12" x2="19" y2="12"/>
|
|
62
|
+
</svg>
|
|
63
|
+
</button>
|
|
64
|
+
<button class="ds-image-preview__toolbar-btn" type="button" aria-label="Rotate left" data-action="rotate-left">
|
|
65
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
66
|
+
<polyline points="1 4 1 10 7 10"/>
|
|
67
|
+
<path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/>
|
|
68
|
+
</svg>
|
|
69
|
+
</button>
|
|
70
|
+
<button class="ds-image-preview__toolbar-btn" type="button" aria-label="Rotate right" data-action="rotate-right">
|
|
71
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
72
|
+
<polyline points="23 4 23 10 17 10"/>
|
|
73
|
+
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
|
|
74
|
+
</svg>
|
|
75
|
+
</button>
|
|
76
|
+
</div>
|
|
39
77
|
<div class="ds-image-preview__content">
|
|
40
78
|
<img src="" alt="" />
|
|
41
79
|
</div>
|
|
@@ -62,6 +100,82 @@ class ImagePreview {
|
|
|
62
100
|
closeBtn.addEventListener('click', this.closeHandler);
|
|
63
101
|
prevBtn.addEventListener('click', this._prevHandler);
|
|
64
102
|
nextBtn.addEventListener('click', this._nextHandler);
|
|
103
|
+
|
|
104
|
+
const toolbarBtns = this.overlay.querySelectorAll('.ds-image-preview__toolbar-btn');
|
|
105
|
+
toolbarBtns.forEach(btn => {
|
|
106
|
+
btn.addEventListener('click', (e) => {
|
|
107
|
+
const action = btn.getAttribute('data-action');
|
|
108
|
+
this.handleToolbarAction(action);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const content = this.overlay.querySelector('.ds-image-preview__content');
|
|
113
|
+
content.addEventListener('wheel', (e) => {
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
if (e.deltaY < 0) {
|
|
116
|
+
this.zoomIn();
|
|
117
|
+
} else {
|
|
118
|
+
this.zoomOut();
|
|
119
|
+
}
|
|
120
|
+
}, { passive: false });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
handleToolbarAction(action) {
|
|
124
|
+
switch (action) {
|
|
125
|
+
case 'zoom-in':
|
|
126
|
+
this.zoomIn();
|
|
127
|
+
break;
|
|
128
|
+
case 'zoom-out':
|
|
129
|
+
this.zoomOut();
|
|
130
|
+
break;
|
|
131
|
+
case 'zoom-reset':
|
|
132
|
+
this.resetZoom();
|
|
133
|
+
break;
|
|
134
|
+
case 'rotate-left':
|
|
135
|
+
this.rotate(-90);
|
|
136
|
+
break;
|
|
137
|
+
case 'rotate-right':
|
|
138
|
+
this.rotate(90);
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
zoomIn() {
|
|
144
|
+
this.zoom = Math.min(this.maxZoom, this.zoom + this.zoomStep);
|
|
145
|
+
this.updateTransform();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
zoomOut() {
|
|
149
|
+
this.zoom = Math.max(this.minZoom, this.zoom - this.zoomStep);
|
|
150
|
+
this.updateTransform();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
resetZoom() {
|
|
154
|
+
this.zoom = 1;
|
|
155
|
+
this.rotation = 0;
|
|
156
|
+
this.updateTransform();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
rotate(degrees) {
|
|
160
|
+
this.rotation += degrees;
|
|
161
|
+
this.updateTransform();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
setRotation(degrees) {
|
|
165
|
+
this.rotation = degrees;
|
|
166
|
+
this.updateTransform();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
setZoom(zoom) {
|
|
170
|
+
this.zoom = Math.max(this.minZoom, Math.min(this.maxZoom, zoom));
|
|
171
|
+
this.updateTransform();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
updateTransform() {
|
|
175
|
+
const img = this.overlay.querySelector('.ds-image-preview__content img');
|
|
176
|
+
if (img) {
|
|
177
|
+
img.style.transform = `scale(${this.zoom}) rotate(${this.rotation}deg)`;
|
|
178
|
+
}
|
|
65
179
|
}
|
|
66
180
|
|
|
67
181
|
handleKeydown(e) {
|
|
@@ -77,6 +191,28 @@ class ImagePreview {
|
|
|
77
191
|
case 'ArrowRight':
|
|
78
192
|
this.next();
|
|
79
193
|
break;
|
|
194
|
+
case '+':
|
|
195
|
+
case '=':
|
|
196
|
+
e.preventDefault();
|
|
197
|
+
this.zoomIn();
|
|
198
|
+
break;
|
|
199
|
+
case '-':
|
|
200
|
+
case '_':
|
|
201
|
+
e.preventDefault();
|
|
202
|
+
this.zoomOut();
|
|
203
|
+
break;
|
|
204
|
+
case '0':
|
|
205
|
+
e.preventDefault();
|
|
206
|
+
this.resetZoom();
|
|
207
|
+
break;
|
|
208
|
+
case '[':
|
|
209
|
+
e.preventDefault();
|
|
210
|
+
this.rotate(-90);
|
|
211
|
+
break;
|
|
212
|
+
case ']':
|
|
213
|
+
e.preventDefault();
|
|
214
|
+
this.rotate(90);
|
|
215
|
+
break;
|
|
80
216
|
}
|
|
81
217
|
}
|
|
82
218
|
|
|
@@ -90,6 +226,7 @@ class ImagePreview {
|
|
|
90
226
|
this.images = images;
|
|
91
227
|
this.currentIndex = Math.min(Math.max(index, 0), images.length - 1);
|
|
92
228
|
|
|
229
|
+
this.resetZoom();
|
|
93
230
|
this.render();
|
|
94
231
|
this.overlay.classList.add('is-visible');
|
|
95
232
|
|
|
@@ -109,6 +246,7 @@ class ImagePreview {
|
|
|
109
246
|
prev() {
|
|
110
247
|
if (this.currentIndex > 0) {
|
|
111
248
|
this.currentIndex--;
|
|
249
|
+
this.resetZoom();
|
|
112
250
|
this.render();
|
|
113
251
|
}
|
|
114
252
|
}
|
|
@@ -116,6 +254,7 @@ class ImagePreview {
|
|
|
116
254
|
next() {
|
|
117
255
|
if (this.currentIndex < this.images.length - 1) {
|
|
118
256
|
this.currentIndex++;
|
|
257
|
+
this.resetZoom();
|
|
119
258
|
this.render();
|
|
120
259
|
}
|
|
121
260
|
}
|
|
@@ -123,6 +262,7 @@ class ImagePreview {
|
|
|
123
262
|
goTo(index) {
|
|
124
263
|
if (index >= 0 && index < this.images.length) {
|
|
125
264
|
this.currentIndex = index;
|
|
265
|
+
this.resetZoom();
|
|
126
266
|
this.render();
|
|
127
267
|
}
|
|
128
268
|
}
|
|
@@ -213,6 +353,10 @@ function showImagePreview(images, index = 0) {
|
|
|
213
353
|
imagePreviewInstance.show(images, index);
|
|
214
354
|
}
|
|
215
355
|
|
|
216
|
-
|
|
217
|
-
|
|
356
|
+
export { ImagePreview, initImagePreview, showImagePreview };
|
|
357
|
+
|
|
358
|
+
if (typeof window !== 'undefined') {
|
|
359
|
+
window.ImagePreview = ImagePreview;
|
|
360
|
+
window.initImagePreview = initImagePreview;
|
|
361
|
+
window.showImagePreview = showImagePreview;
|
|
218
362
|
}
|
package/js/initializer.js
CHANGED
|
@@ -1,15 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component Initializer Registry — auto-discovery and lifecycle management for declarative components.
|
|
3
|
+
* Scans the DOM for `data-*` attributes or CSS classes, calls init functions,
|
|
4
|
+
* and uses MutationObserver for dynamically added elements.
|
|
5
|
+
*/
|
|
1
6
|
class ComponentInitializerRegistry {
|
|
2
7
|
constructor() {
|
|
3
8
|
this.initializers = new Map();
|
|
4
9
|
this.cleanupFunctions = new Map();
|
|
5
10
|
this.processedElements = new WeakSet();
|
|
11
|
+
// Single source of truth for selectors
|
|
12
|
+
this._dataAttrs = ['data-component'];
|
|
13
|
+
this._cssClasses = [];
|
|
14
|
+
this._cachedSelector = null;
|
|
6
15
|
}
|
|
7
16
|
|
|
8
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Register a component initializer.
|
|
19
|
+
* @param {string} name - Component name
|
|
20
|
+
* @param {Function} initFn - Init function called with the DOM element
|
|
21
|
+
* @param {Function} [cleanupFn] - Optional cleanup/destroy function
|
|
22
|
+
* @param {Object} [options] - { dataAttribute, cssClass } for auto-discovery
|
|
23
|
+
*/
|
|
24
|
+
register(name, initFn, cleanupFn = null, options = {}) {
|
|
9
25
|
this.initializers.set(name, initFn);
|
|
10
26
|
if (cleanupFn) {
|
|
11
27
|
this.cleanupFunctions.set(name, cleanupFn);
|
|
12
28
|
}
|
|
29
|
+
// Auto-register selectors
|
|
30
|
+
if (options.dataAttribute && !this._dataAttrs.includes(options.dataAttribute)) {
|
|
31
|
+
this._dataAttrs.push(options.dataAttribute);
|
|
32
|
+
this._cachedSelector = null;
|
|
33
|
+
}
|
|
34
|
+
if (options.cssClass && !this._cssClasses.includes(options.cssClass)) {
|
|
35
|
+
this._cssClasses.push(options.cssClass);
|
|
36
|
+
this._cachedSelector = null;
|
|
37
|
+
}
|
|
13
38
|
}
|
|
14
39
|
|
|
15
40
|
unregister(name) {
|
|
@@ -25,20 +50,26 @@ class ComponentInitializerRegistry {
|
|
|
25
50
|
return this.initializers.get(name);
|
|
26
51
|
}
|
|
27
52
|
|
|
53
|
+
_buildSelector() {
|
|
54
|
+
if (this._cachedSelector !== null) return this._cachedSelector;
|
|
55
|
+
const parts = this._dataAttrs.map(a => `[${a}]`);
|
|
56
|
+
for (const cls of this._cssClasses) {
|
|
57
|
+
parts.push(`.${cls}`);
|
|
58
|
+
}
|
|
59
|
+
this._cachedSelector = parts.join(', ');
|
|
60
|
+
return this._cachedSelector;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Initialize a single DOM element if it matches a registered component. */
|
|
28
64
|
async initialize(element) {
|
|
29
65
|
if (this.processedElements.has(element)) return;
|
|
30
66
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
'data-modal', 'data-dialog', 'data-color-picker', 'data-calendar',
|
|
34
|
-
'data-slide-captcha', 'data-heatmap'];
|
|
35
|
-
|
|
36
|
-
for (const attr of dataAttrs) {
|
|
67
|
+
// Check data attributes
|
|
68
|
+
for (const attr of this._dataAttrs) {
|
|
37
69
|
const value = element.getAttribute(attr);
|
|
38
|
-
if (value) {
|
|
70
|
+
if (value !== null) {
|
|
39
71
|
const name = value || attr.replace('data-', '');
|
|
40
72
|
const initFn = this.initializers.get(name) || this.initializers.get(attr.replace('data-', ''));
|
|
41
|
-
|
|
42
73
|
if (initFn) {
|
|
43
74
|
try {
|
|
44
75
|
await initFn(element);
|
|
@@ -51,46 +82,35 @@ class ComponentInitializerRegistry {
|
|
|
51
82
|
}
|
|
52
83
|
}
|
|
53
84
|
|
|
85
|
+
// Check CSS classes
|
|
54
86
|
const className = element.className;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (initFn) {
|
|
70
|
-
try {
|
|
71
|
-
await initFn(element);
|
|
72
|
-
this.processedElements.add(element);
|
|
73
|
-
} catch (error) {
|
|
74
|
-
console.error(`[ComponentInitializerRegistry] Error initializing "${name}":`, error);
|
|
87
|
+
if (typeof className === 'string') {
|
|
88
|
+
for (const cls of this._cssClasses) {
|
|
89
|
+
if (className.includes(cls)) {
|
|
90
|
+
const name = cls.replace('ds-', '');
|
|
91
|
+
const initFn = this.initializers.get(name) || this.initializers.get(cls);
|
|
92
|
+
if (initFn) {
|
|
93
|
+
try {
|
|
94
|
+
await initFn(element);
|
|
95
|
+
this.processedElements.add(element);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error(`[ComponentInitializerRegistry] Error initializing "${name}":`, error);
|
|
98
|
+
}
|
|
99
|
+
return;
|
|
75
100
|
}
|
|
76
|
-
return;
|
|
77
101
|
}
|
|
78
102
|
}
|
|
79
103
|
}
|
|
80
104
|
}
|
|
81
105
|
|
|
106
|
+
/** Run cleanup/destroy for a single DOM element. */
|
|
82
107
|
cleanup(element) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
'data-modal', 'data-dialog', 'data-color-picker', 'data-calendar',
|
|
86
|
-
'data-slide-captcha', 'data-heatmap'];
|
|
87
|
-
|
|
88
|
-
for (const attr of dataAttrs) {
|
|
108
|
+
// Check data attributes
|
|
109
|
+
for (const attr of this._dataAttrs) {
|
|
89
110
|
const value = element.getAttribute(attr);
|
|
90
|
-
if (value) {
|
|
111
|
+
if (value !== null) {
|
|
91
112
|
const name = value || attr.replace('data-', '');
|
|
92
113
|
const cleanupFn = this.cleanupFunctions.get(name) || this.cleanupFunctions.get(attr.replace('data-', ''));
|
|
93
|
-
|
|
94
114
|
if (cleanupFn) {
|
|
95
115
|
try {
|
|
96
116
|
cleanupFn(element);
|
|
@@ -103,45 +123,33 @@ class ComponentInitializerRegistry {
|
|
|
103
123
|
}
|
|
104
124
|
}
|
|
105
125
|
|
|
126
|
+
// Check CSS classes
|
|
106
127
|
const className = element.className;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (cleanupFn) {
|
|
122
|
-
try {
|
|
123
|
-
cleanupFn(element);
|
|
124
|
-
} catch (error) {
|
|
125
|
-
console.error(`[ComponentInitializerRegistry] Error cleaning up "${name}":`, error);
|
|
128
|
+
if (typeof className === 'string') {
|
|
129
|
+
for (const cls of this._cssClasses) {
|
|
130
|
+
if (className.includes(cls)) {
|
|
131
|
+
const name = cls.replace('ds-', '');
|
|
132
|
+
const cleanupFn = this.cleanupFunctions.get(name) || this.cleanupFunctions.get(cls);
|
|
133
|
+
if (cleanupFn) {
|
|
134
|
+
try {
|
|
135
|
+
cleanupFn(element);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error(`[ComponentInitializerRegistry] Error cleaning up "${name}":`, error);
|
|
138
|
+
}
|
|
139
|
+
this.processedElements.delete(element);
|
|
140
|
+
return;
|
|
126
141
|
}
|
|
127
|
-
this.processedElements.delete(element);
|
|
128
|
-
return;
|
|
129
142
|
}
|
|
130
143
|
}
|
|
131
144
|
}
|
|
132
145
|
}
|
|
133
146
|
|
|
147
|
+
/** Scan and initialize all matching components. Uses MutationObserver for dynamic content. */
|
|
134
148
|
async initializeAll(root = document) {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
'[data-drawer], [data-modal], [data-dialog], [data-color-picker], ' +
|
|
138
|
-
'[data-calendar], [data-slide-captcha], [data-heatmap], ' +
|
|
139
|
-
'.ds-dropdown, .ds-select, .ds-datepicker, .ds-timepicker, ' +
|
|
140
|
-
'.ds-slider, .ds-carousel, .ds-drawer, .ds-modal, .ds-dialog, ' +
|
|
141
|
-
'.ds-color-picker, .ds-calendar, .ds-slider-captcha, .ds-heatmap, ' +
|
|
142
|
-
'.ds-tooltip, .ds-tag, .ds-statcard, .ds-collapse, .ds-fileupload, ' +
|
|
143
|
-
'.ds-notification, .ds-message');
|
|
149
|
+
const selector = this._buildSelector();
|
|
150
|
+
if (!selector) return;
|
|
144
151
|
|
|
152
|
+
const elements = root.querySelectorAll(selector);
|
|
145
153
|
const promises = [];
|
|
146
154
|
elements.forEach(element => {
|
|
147
155
|
if (!this.processedElements.has(element)) {
|
|
@@ -152,11 +160,83 @@ class ComponentInitializerRegistry {
|
|
|
152
160
|
}
|
|
153
161
|
}
|
|
154
162
|
|
|
163
|
+
/** @type {ComponentInitializerRegistry} Global initializer instance */
|
|
155
164
|
const kupolaInitializer = new ComponentInitializerRegistry();
|
|
156
165
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
166
|
+
// Pre-register known component selectors
|
|
167
|
+
const knownComponents = [
|
|
168
|
+
{ attr: 'data-dropdown', cls: 'ds-dropdown' },
|
|
169
|
+
{ attr: 'data-select', cls: 'ds-select' },
|
|
170
|
+
{ attr: 'data-datepicker', cls: 'ds-datepicker' },
|
|
171
|
+
{ attr: 'data-timepicker', cls: 'ds-timepicker' },
|
|
172
|
+
{ attr: 'data-slider', cls: 'ds-slider' },
|
|
173
|
+
{ attr: 'data-carousel', cls: 'ds-carousel' },
|
|
174
|
+
{ attr: 'data-drawer', cls: 'ds-drawer' },
|
|
175
|
+
{ attr: 'data-modal', cls: 'ds-modal' },
|
|
176
|
+
{ attr: 'data-dialog', cls: 'ds-dialog' },
|
|
177
|
+
{ attr: 'data-color-picker', cls: 'ds-color-picker' },
|
|
178
|
+
{ attr: 'data-calendar', cls: 'ds-calendar' },
|
|
179
|
+
{ attr: 'data-slide-captcha', cls: 'ds-slide-captcha' },
|
|
180
|
+
{ attr: 'data-heatmap', cls: 'ds-heatmap' },
|
|
181
|
+
{ cls: 'ds-tooltip' },
|
|
182
|
+
{ cls: 'ds-tag' },
|
|
183
|
+
{ cls: 'ds-statcard' },
|
|
184
|
+
{ cls: 'ds-collapse' },
|
|
185
|
+
{ cls: 'ds-fileupload' },
|
|
186
|
+
{ cls: 'ds-notification' },
|
|
187
|
+
{ cls: 'ds-message' }
|
|
188
|
+
];
|
|
189
|
+
|
|
190
|
+
for (const comp of knownComponents) {
|
|
191
|
+
if (comp.attr && !kupolaInitializer._dataAttrs.includes(comp.attr)) {
|
|
192
|
+
kupolaInitializer._dataAttrs.push(comp.attr);
|
|
193
|
+
}
|
|
194
|
+
if (comp.cls && !kupolaInitializer._cssClasses.includes(comp.cls)) {
|
|
195
|
+
kupolaInitializer._cssClasses.push(comp.cls);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export { ComponentInitializerRegistry, kupolaInitializer };
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Kupola global namespace — convenience API for external consumers.
|
|
203
|
+
* Provides Kupola.refresh(root) to re-initialize dynamically inserted DOM.
|
|
204
|
+
*/
|
|
205
|
+
if (typeof window !== 'undefined') {
|
|
206
|
+
window.Kupola = window.Kupola || {};
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Re-scan and initialize components within a given scope.
|
|
210
|
+
* @param {Element|Document} [root=document] - Scope to scan
|
|
211
|
+
* @returns {Promise<void>}
|
|
212
|
+
*/
|
|
213
|
+
window.Kupola.refresh = async function refresh(root) {
|
|
214
|
+
return kupolaInitializer.initializeAll(root || document);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Initialize a single element or container.
|
|
219
|
+
* @param {Element} element
|
|
220
|
+
* @returns {Promise<void>}
|
|
221
|
+
*/
|
|
222
|
+
window.Kupola.init = async function init(element) {
|
|
223
|
+
return kupolaInitializer.initialize(element);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Cleanup a single element.
|
|
228
|
+
* @param {Element} element
|
|
229
|
+
*/
|
|
230
|
+
window.Kupola.cleanup = function cleanup(element) {
|
|
231
|
+
return kupolaInitializer.cleanup(element);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
/** Expose the initializer registry directly */
|
|
235
|
+
window.Kupola.initializer = kupolaInitializer;
|
|
236
|
+
window.Kupola.ComponentInitializerRegistry = ComponentInitializerRegistry;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (typeof window !== 'undefined') {
|
|
160
240
|
window.ComponentInitializerRegistry = ComponentInitializerRegistry;
|
|
161
241
|
window.kupolaInitializer = kupolaInitializer;
|
|
162
242
|
}
|