@vanduo-oss/framework 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -12
- package/css/components/music-player.css +578 -0
- package/css/components/navbar.css +5 -0
- package/css/vanduo.css +1 -0
- package/dist/build-info.json +3 -3
- package/dist/vanduo.cjs.js +729 -44
- package/dist/vanduo.cjs.js.map +3 -3
- package/dist/vanduo.cjs.min.js +5 -5
- package/dist/vanduo.cjs.min.js.map +4 -4
- package/dist/vanduo.css +512 -1
- package/dist/vanduo.css.map +1 -1
- package/dist/vanduo.esm.js +729 -44
- package/dist/vanduo.esm.js.map +3 -3
- package/dist/vanduo.esm.min.js +5 -5
- package/dist/vanduo.esm.min.js.map +4 -4
- package/dist/vanduo.js +729 -44
- package/dist/vanduo.js.map +3 -3
- package/dist/vanduo.min.css +2 -2
- package/dist/vanduo.min.css.map +1 -1
- package/dist/vanduo.min.js +5 -5
- package/dist/vanduo.min.js.map +4 -4
- package/js/components/code-snippet.js +5 -4
- package/js/components/dropdown.js +9 -9
- package/js/components/image-box.js +7 -1
- package/js/components/modals.js +18 -10
- package/js/components/music-player.js +848 -0
- package/js/components/navbar.js +3 -3
- package/js/components/select.js +15 -13
- package/js/components/suggest.js +14 -1
- package/js/components/theme-customizer.js +0 -1
- package/js/components/validate.js +14 -3
- package/js/index.js +1 -0
- package/js/utils/helpers.js +7 -3
- package/package.json +2 -2
|
@@ -282,19 +282,20 @@
|
|
|
282
282
|
const codeElement = activePane.querySelector('code') || activePane;
|
|
283
283
|
const code = codeElement.textContent;
|
|
284
284
|
|
|
285
|
+
let copySuccess;
|
|
285
286
|
try {
|
|
286
287
|
await navigator.clipboard.writeText(code);
|
|
287
|
-
|
|
288
|
+
copySuccess = true;
|
|
288
289
|
} catch (_err) {
|
|
289
290
|
// Fallback for older browsers
|
|
290
|
-
|
|
291
|
-
this.showCopyFeedback(copyBtn, success);
|
|
291
|
+
copySuccess = this.fallbackCopy(code);
|
|
292
292
|
}
|
|
293
|
+
this.showCopyFeedback(copyBtn, copySuccess);
|
|
293
294
|
|
|
294
295
|
// Dispatch event
|
|
295
296
|
const event = new CustomEvent('codesnippet:copy', {
|
|
296
297
|
bubbles: true,
|
|
297
|
-
detail: { snippet, code, success:
|
|
298
|
+
detail: { snippet, code, success: copySuccess }
|
|
298
299
|
});
|
|
299
300
|
snippet.dispatchEvent(event);
|
|
300
301
|
},
|
|
@@ -12,9 +12,6 @@
|
|
|
12
12
|
const Dropdown = {
|
|
13
13
|
// Store initialized dropdowns and their cleanup functions
|
|
14
14
|
instances: new Map(),
|
|
15
|
-
// Typeahead state
|
|
16
|
-
_typeaheadBuffer: '',
|
|
17
|
-
_typeaheadTimer: null,
|
|
18
15
|
|
|
19
16
|
/**
|
|
20
17
|
* Initialize dropdown components
|
|
@@ -95,7 +92,7 @@
|
|
|
95
92
|
cleanupFunctions.push(() => item.removeEventListener('keydown', itemKeydownHandler));
|
|
96
93
|
});
|
|
97
94
|
|
|
98
|
-
this.instances.set(dropdown, { toggle, menu, cleanup: cleanupFunctions });
|
|
95
|
+
this.instances.set(dropdown, { toggle, menu, cleanup: cleanupFunctions, typeaheadBuffer: '', typeaheadTimer: null });
|
|
99
96
|
},
|
|
100
97
|
|
|
101
98
|
/**
|
|
@@ -249,18 +246,21 @@
|
|
|
249
246
|
default:
|
|
250
247
|
// Typeahead: jump to matching item when typing printable characters
|
|
251
248
|
if (isOpen && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
252
|
-
|
|
253
|
-
|
|
249
|
+
// Per-instance typeahead state to avoid cross-instance corruption
|
|
250
|
+
const instance = this.instances.get(dropdown);
|
|
251
|
+
if (!instance) break;
|
|
252
|
+
clearTimeout(instance.typeaheadTimer);
|
|
253
|
+
instance.typeaheadBuffer += e.key.toLowerCase();
|
|
254
254
|
|
|
255
255
|
const match = items.find(item =>
|
|
256
|
-
item.textContent.trim().toLowerCase().startsWith(
|
|
256
|
+
item.textContent.trim().toLowerCase().startsWith(instance.typeaheadBuffer)
|
|
257
257
|
);
|
|
258
258
|
if (match) {
|
|
259
259
|
match.focus();
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
-
|
|
263
|
-
|
|
262
|
+
instance.typeaheadTimer = setTimeout(() => {
|
|
263
|
+
instance.typeaheadBuffer = '';
|
|
264
264
|
}, 500);
|
|
265
265
|
}
|
|
266
266
|
break;
|
|
@@ -274,9 +274,10 @@
|
|
|
274
274
|
// Handle image load
|
|
275
275
|
if (!this.img.complete) {
|
|
276
276
|
this.img.style.opacity = '0';
|
|
277
|
-
this.
|
|
277
|
+
this._imgLoadHandler = () => {
|
|
278
278
|
this.img.style.opacity = '';
|
|
279
279
|
};
|
|
280
|
+
this.img.addEventListener('load', this._imgLoadHandler, { once: true });
|
|
280
281
|
}
|
|
281
282
|
},
|
|
282
283
|
|
|
@@ -305,6 +306,11 @@
|
|
|
305
306
|
// Clear image after transition
|
|
306
307
|
setTimeout(() => {
|
|
307
308
|
if (!this.isOpen) {
|
|
309
|
+
// Clean up load handler if still pending
|
|
310
|
+
if (this._imgLoadHandler) {
|
|
311
|
+
this.img.removeEventListener('load', this._imgLoadHandler);
|
|
312
|
+
this._imgLoadHandler = null;
|
|
313
|
+
}
|
|
308
314
|
this.img.src = '';
|
|
309
315
|
this.img.alt = '';
|
|
310
316
|
}
|
package/js/components/modals.js
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
// Store trigger cleanup functions
|
|
18
18
|
_triggerCleanups: [],
|
|
19
|
+
// Shared ESC key handler (installed once)
|
|
20
|
+
_sharedEscHandler: null,
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
23
|
* Initialize modals
|
|
@@ -99,17 +101,18 @@
|
|
|
99
101
|
backdrop.addEventListener('click', backdropClickHandler);
|
|
100
102
|
cleanupFunctions.push(() => backdrop.removeEventListener('click', backdropClickHandler));
|
|
101
103
|
|
|
102
|
-
// ESC key handler
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
// ESC key handler — use a single shared handler instead of one-per-modal
|
|
105
|
+
if (!this._sharedEscHandler) {
|
|
106
|
+
this._sharedEscHandler = (e) => {
|
|
107
|
+
if (e.key === 'Escape' && this.openModals.length > 0) {
|
|
108
|
+
const topModal = this.openModals[this.openModals.length - 1];
|
|
109
|
+
if (topModal.dataset.keyboard !== 'false') {
|
|
110
|
+
this.close(topModal);
|
|
111
|
+
}
|
|
108
112
|
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
cleanupFunctions.push(() => document.removeEventListener('keydown', escKeyHandler));
|
|
113
|
+
};
|
|
114
|
+
document.addEventListener('keydown', this._sharedEscHandler);
|
|
115
|
+
}
|
|
113
116
|
|
|
114
117
|
this.modals.set(modal, { backdrop, dialog, trapHandler: null, cleanup: cleanupFunctions });
|
|
115
118
|
},
|
|
@@ -352,6 +355,11 @@
|
|
|
352
355
|
// Clean up trigger listeners
|
|
353
356
|
this._triggerCleanups.forEach(fn => fn());
|
|
354
357
|
this._triggerCleanups = [];
|
|
358
|
+
// Remove shared ESC handler
|
|
359
|
+
if (this._sharedEscHandler) {
|
|
360
|
+
document.removeEventListener('keydown', this._sharedEscHandler);
|
|
361
|
+
this._sharedEscHandler = null;
|
|
362
|
+
}
|
|
355
363
|
}
|
|
356
364
|
};
|
|
357
365
|
|