@vanduo-oss/framework 1.3.2 → 1.3.4
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 +23 -4
- package/css/components/cards.css +8 -0
- package/css/components/draggable.css +3 -1
- package/css/components/dropdown.css +8 -0
- package/css/components/fab.css +14 -0
- package/css/components/modals.css +13 -0
- package/css/components/navbar.css +30 -0
- package/css/components/toast.css +8 -0
- package/css/components/tooltips.css +29 -0
- package/css/core/tokens.css +37 -0
- package/css/core/vd-aliases.css +13 -0
- package/css/effects/glass.css +154 -0
- package/css/vanduo.css +1 -0
- package/dist/build-info.json +3 -3
- package/dist/vanduo.cjs.js +206 -19
- package/dist/vanduo.cjs.js.map +3 -3
- package/dist/vanduo.cjs.min.js +4 -4
- package/dist/vanduo.cjs.min.js.map +4 -4
- package/dist/vanduo.css +264 -2
- package/dist/vanduo.css.map +1 -1
- package/dist/vanduo.esm.js +206 -19
- package/dist/vanduo.esm.js.map +3 -3
- package/dist/vanduo.esm.min.js +4 -4
- package/dist/vanduo.esm.min.js.map +4 -4
- package/dist/vanduo.js +206 -19
- 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 +4 -4
- package/dist/vanduo.min.js.map +4 -4
- package/js/components/draggable.js +103 -12
- package/js/components/glass.js +87 -0
- package/js/components/navbar.js +44 -3
- package/js/components/theme-customizer.js +22 -9
- package/js/components/theme-switcher.js +4 -0
- package/js/index.js +3 -0
- package/package.json +1 -1
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
touchState: null,
|
|
19
19
|
// Feedback element
|
|
20
20
|
feedbackElement: null,
|
|
21
|
+
// Shared selector used by init and touch reorder
|
|
22
|
+
containerSelector: '.vd-draggable-container, .vd-draggable-container-vertical',
|
|
21
23
|
|
|
22
24
|
/**
|
|
23
25
|
* Initialize draggable components
|
|
@@ -32,7 +34,7 @@
|
|
|
32
34
|
this.initDraggable(element);
|
|
33
35
|
});
|
|
34
36
|
|
|
35
|
-
const containers = document.querySelectorAll(
|
|
37
|
+
const containers = document.querySelectorAll(this.containerSelector);
|
|
36
38
|
containers.forEach(container => {
|
|
37
39
|
if (!this.instances.has(container)) {
|
|
38
40
|
this.initContainer(container);
|
|
@@ -369,10 +371,16 @@
|
|
|
369
371
|
// Don't prevent default here — it blocks scrolling.
|
|
370
372
|
// We only prevent default in touchmove once drag threshold is reached.
|
|
371
373
|
const touch = e.touches[0];
|
|
374
|
+
const rect = element.getBoundingClientRect();
|
|
372
375
|
this.touchState = {
|
|
373
376
|
element: element,
|
|
374
377
|
startX: touch.clientX,
|
|
375
378
|
startY: touch.clientY,
|
|
379
|
+
lastX: touch.clientX,
|
|
380
|
+
lastY: touch.clientY,
|
|
381
|
+
// Keep preview anchored to the original grab point.
|
|
382
|
+
offsetX: touch.clientX - rect.left,
|
|
383
|
+
offsetY: touch.clientY - rect.top,
|
|
376
384
|
startTime: Date.now(),
|
|
377
385
|
isDragging: false
|
|
378
386
|
};
|
|
@@ -387,6 +395,8 @@
|
|
|
387
395
|
if (!this.touchState) return;
|
|
388
396
|
|
|
389
397
|
const touch = e.touches[0];
|
|
398
|
+
this.touchState.lastX = touch.clientX;
|
|
399
|
+
this.touchState.lastY = touch.clientY;
|
|
390
400
|
const deltaX = touch.clientX - this.touchState.startX;
|
|
391
401
|
const deltaY = touch.clientY - this.touchState.startY;
|
|
392
402
|
|
|
@@ -405,7 +415,10 @@
|
|
|
405
415
|
element: element,
|
|
406
416
|
initialPosition: { x: this.touchState.startX, y: this.touchState.startY },
|
|
407
417
|
initialBounds: element.getBoundingClientRect(),
|
|
408
|
-
data: this.getData(element)
|
|
418
|
+
data: this.getData(element),
|
|
419
|
+
// Preserve where inside the element the drag started for accurate ghost positioning.
|
|
420
|
+
offsetX: this.touchState.offsetX,
|
|
421
|
+
offsetY: this.touchState.offsetY
|
|
409
422
|
};
|
|
410
423
|
|
|
411
424
|
// Dispatch event
|
|
@@ -434,8 +447,10 @@
|
|
|
434
447
|
}
|
|
435
448
|
}));
|
|
436
449
|
|
|
450
|
+
this.updateTouchDropZone(touch.clientX, touch.clientY);
|
|
451
|
+
|
|
437
452
|
// Reorder for touch
|
|
438
|
-
const container = element.closest(
|
|
453
|
+
const container = element.closest(this.containerSelector);
|
|
439
454
|
if (container && container.contains(element)) {
|
|
440
455
|
this.handleReorder(container, element, touch.clientX, touch.clientY);
|
|
441
456
|
}
|
|
@@ -451,6 +466,18 @@
|
|
|
451
466
|
handleTouchEnd: function (e, element) {
|
|
452
467
|
if (this.touchState && this.touchState.isDragging) {
|
|
453
468
|
if (e.cancelable) e.preventDefault();
|
|
469
|
+
const endTouch = e.changedTouches?.[0];
|
|
470
|
+
const endPosition = {
|
|
471
|
+
x: endTouch?.clientX ?? this.touchState.lastX ?? this.touchState.startX,
|
|
472
|
+
y: endTouch?.clientY ?? this.touchState.lastY ?? this.touchState.startY
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
const dropZone = this.resolveDropZoneAtPoint(endPosition.x, endPosition.y) || this.touchState.overZone;
|
|
476
|
+
if (dropZone) {
|
|
477
|
+
this.dispatchDrop(dropZone, endPosition);
|
|
478
|
+
} else if (this.touchState.overZone) {
|
|
479
|
+
this.touchState.overZone.classList.remove('is-drag-over');
|
|
480
|
+
}
|
|
454
481
|
|
|
455
482
|
element.classList.remove('is-dragging');
|
|
456
483
|
element.classList.add('is-dropped');
|
|
@@ -463,7 +490,6 @@
|
|
|
463
490
|
}
|
|
464
491
|
|
|
465
492
|
// Dispatch event
|
|
466
|
-
const endTouch = e.changedTouches[0];
|
|
467
493
|
const data = this.currentDrag?.data || this.getData(element);
|
|
468
494
|
const startX = this.touchState?.startX || 0;
|
|
469
495
|
const startY = this.touchState?.startY || 0;
|
|
@@ -473,10 +499,10 @@
|
|
|
473
499
|
detail: {
|
|
474
500
|
element: element,
|
|
475
501
|
data: data,
|
|
476
|
-
position:
|
|
502
|
+
position: endPosition,
|
|
477
503
|
delta: {
|
|
478
|
-
x:
|
|
479
|
-
y:
|
|
504
|
+
x: endPosition.x - startX,
|
|
505
|
+
y: endPosition.y - startY
|
|
480
506
|
}
|
|
481
507
|
}
|
|
482
508
|
}));
|
|
@@ -523,16 +549,79 @@
|
|
|
523
549
|
*/
|
|
524
550
|
handleDrop: function (e, zone) {
|
|
525
551
|
e.preventDefault();
|
|
526
|
-
zone.
|
|
552
|
+
this.dispatchDrop(zone, { x: e.clientX, y: e.clientY });
|
|
553
|
+
},
|
|
527
554
|
|
|
528
|
-
|
|
555
|
+
/**
|
|
556
|
+
* Resolve a drop zone from viewport coordinates
|
|
557
|
+
* @param {number} x
|
|
558
|
+
* @param {number} y
|
|
559
|
+
* @returns {HTMLElement|null}
|
|
560
|
+
*/
|
|
561
|
+
resolveDropZoneAtPoint: function (x, y) {
|
|
562
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
|
|
563
|
+
|
|
564
|
+
// Prefer the full stacking list so overlays/top elements don't hide real drop targets.
|
|
565
|
+
if (typeof document.elementsFromPoint === 'function') {
|
|
566
|
+
const stacked = document.elementsFromPoint(x, y);
|
|
567
|
+
for (const element of stacked) {
|
|
568
|
+
const zone = element.closest('.vd-drop-zone');
|
|
569
|
+
if (zone) return zone;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const target = document.elementFromPoint(x, y);
|
|
574
|
+
const targetZone = target ? target.closest('.vd-drop-zone') : null;
|
|
575
|
+
if (targetZone) return targetZone;
|
|
576
|
+
|
|
577
|
+
// Last-resort fallback for mobile emulation edge cases.
|
|
578
|
+
const zones = document.querySelectorAll('.vd-drop-zone');
|
|
579
|
+
for (const zone of zones) {
|
|
580
|
+
const rect = zone.getBoundingClientRect();
|
|
581
|
+
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
|
|
582
|
+
return zone;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return null;
|
|
587
|
+
},
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Track and update active drop-zone hover state on touch devices
|
|
591
|
+
* @param {number} x
|
|
592
|
+
* @param {number} y
|
|
593
|
+
*/
|
|
594
|
+
updateTouchDropZone: function (x, y) {
|
|
595
|
+
if (!this.touchState) return;
|
|
596
|
+
|
|
597
|
+
const nextZone = this.resolveDropZoneAtPoint(x, y);
|
|
598
|
+
const prevZone = this.touchState.overZone || null;
|
|
599
|
+
|
|
600
|
+
if (prevZone && prevZone !== nextZone) {
|
|
601
|
+
prevZone.classList.remove('is-drag-over');
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (nextZone && nextZone !== prevZone) {
|
|
605
|
+
nextZone.classList.add('is-drag-over');
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
this.touchState.overZone = nextZone || null;
|
|
609
|
+
},
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Dispatch a normalized drop event for mouse and touch flows
|
|
613
|
+
* @param {HTMLElement} zone
|
|
614
|
+
* @param {{x:number, y:number}} position
|
|
615
|
+
*/
|
|
616
|
+
dispatchDrop: function (zone, position) {
|
|
617
|
+
zone.classList.remove('is-drag-over');
|
|
529
618
|
zone.dispatchEvent(new CustomEvent('draggable:drop', {
|
|
530
619
|
bubbles: true,
|
|
531
620
|
detail: {
|
|
532
621
|
zone: zone,
|
|
533
622
|
element: this.currentDrag?.element,
|
|
534
623
|
data: this.currentDrag?.data,
|
|
535
|
-
position:
|
|
624
|
+
position: position
|
|
536
625
|
}
|
|
537
626
|
}));
|
|
538
627
|
},
|
|
@@ -650,9 +739,11 @@
|
|
|
650
739
|
this.feedbackElement.appendChild(clone);
|
|
651
740
|
|
|
652
741
|
// Set styles
|
|
742
|
+
const offsetX = this.currentDrag.offsetX ?? 20;
|
|
743
|
+
const offsetY = this.currentDrag.offsetY ?? 20;
|
|
653
744
|
Object.assign(this.feedbackElement.style, {
|
|
654
|
-
left: (x -
|
|
655
|
-
top: (y -
|
|
745
|
+
left: (x - offsetX) + 'px',
|
|
746
|
+
top: (y - offsetY) + 'px',
|
|
656
747
|
width: rect.width + 'px',
|
|
657
748
|
height: rect.height + 'px'
|
|
658
749
|
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vanduo Framework - Glass Scroll Activation
|
|
3
|
+
* Generic scroll-aware glass activation via IntersectionObserver.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* Add `data-glass-scroll` to any element carrying a `.vd-glass*` class.
|
|
7
|
+
* By default the previous sibling is used as the sentinel.
|
|
8
|
+
* Point to a custom sentinel via `data-glass-sentinel="<CSS selector>"`.
|
|
9
|
+
*
|
|
10
|
+
* Behaviour:
|
|
11
|
+
* - Adds `.is-glass-active` when the sentinel leaves the viewport (scrolled past).
|
|
12
|
+
* - Removes `.is-glass-active` when the sentinel re-enters the viewport.
|
|
13
|
+
*/
|
|
14
|
+
(function () {
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const GlassScroll = {
|
|
18
|
+
/** @type {Map<Element, IntersectionObserver>} */
|
|
19
|
+
observers: new Map(),
|
|
20
|
+
|
|
21
|
+
init: function () {
|
|
22
|
+
document.querySelectorAll('[data-glass-scroll]').forEach(el => {
|
|
23
|
+
if (this.observers.has(el)) return;
|
|
24
|
+
this.initElement(el);
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Wire up a single scroll-activated glass element.
|
|
30
|
+
* @param {HTMLElement} el
|
|
31
|
+
*/
|
|
32
|
+
initElement: function (el) {
|
|
33
|
+
const sentinelSelector = el.dataset.glassSentinel;
|
|
34
|
+
let sentinel;
|
|
35
|
+
|
|
36
|
+
if (sentinelSelector) {
|
|
37
|
+
sentinel = document.querySelector(sentinelSelector);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!sentinel) {
|
|
41
|
+
// Fall back to the previous sibling element
|
|
42
|
+
sentinel = el.previousElementSibling;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!sentinel) {
|
|
46
|
+
// No sentinel available — activate immediately so glass is always shown
|
|
47
|
+
el.classList.add('is-glass-active');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const observer = new IntersectionObserver(
|
|
52
|
+
(entries) => {
|
|
53
|
+
entries.forEach(entry => {
|
|
54
|
+
// Active when sentinel is NOT intersecting (scrolled past it)
|
|
55
|
+
el.classList.toggle('is-glass-active', !entry.isIntersecting);
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
{ threshold: 0, rootMargin: '0px' }
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
observer.observe(sentinel);
|
|
62
|
+
this.observers.set(el, observer);
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Disconnect and remove a single element's observer.
|
|
67
|
+
* @param {HTMLElement} el
|
|
68
|
+
*/
|
|
69
|
+
destroy: function (el) {
|
|
70
|
+
const observer = this.observers.get(el);
|
|
71
|
+
if (observer) {
|
|
72
|
+
observer.disconnect();
|
|
73
|
+
this.observers.delete(el);
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
destroyAll: function () {
|
|
78
|
+
this.observers.forEach((observer, el) => this.destroy(el));
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
if (typeof window.Vanduo !== 'undefined') {
|
|
83
|
+
window.Vanduo.register('glassScroll', GlassScroll);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
window.VanduoGlassScroll = GlassScroll;
|
|
87
|
+
})();
|
package/js/components/navbar.js
CHANGED
|
@@ -41,6 +41,37 @@
|
|
|
41
41
|
});
|
|
42
42
|
},
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Initialize scroll-aware glass/transparent behaviour for a navbar.
|
|
46
|
+
* Adds/removes `.vd-navbar-scrolled` when the page scrolls past a threshold.
|
|
47
|
+
* Threshold: `data-scroll-threshold` attribute (px) or the navbar's own height.
|
|
48
|
+
* @param {HTMLElement} navbar - Navbar element
|
|
49
|
+
* @returns {Function|null} Cleanup function, or null if not applicable
|
|
50
|
+
*/
|
|
51
|
+
initScrollWatcher: function (navbar) {
|
|
52
|
+
const isGlass = navbar.classList.contains('vd-navbar-glass');
|
|
53
|
+
const isTransparent = navbar.classList.contains('vd-navbar-transparent');
|
|
54
|
+
|
|
55
|
+
if (!isGlass && !isTransparent) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const getThreshold = () => {
|
|
60
|
+
const attr = parseInt(navbar.dataset.scrollThreshold, 10);
|
|
61
|
+
return isNaN(attr) ? (navbar.offsetHeight || 60) : attr;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const onScroll = () => {
|
|
65
|
+
const scrolled = window.scrollY > getThreshold();
|
|
66
|
+
navbar.classList.toggle('vd-navbar-scrolled', scrolled);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
onScroll(); // set initial state without waiting for first scroll
|
|
70
|
+
window.addEventListener('scroll', onScroll, { passive: true });
|
|
71
|
+
|
|
72
|
+
return () => window.removeEventListener('scroll', onScroll);
|
|
73
|
+
},
|
|
74
|
+
|
|
44
75
|
/**
|
|
45
76
|
* Initialize a single navbar
|
|
46
77
|
* @param {HTMLElement} navbar - Navbar element
|
|
@@ -50,13 +81,23 @@
|
|
|
50
81
|
const menu = navbar.querySelector('.vd-navbar-menu');
|
|
51
82
|
const overlay = navbar.querySelector('.vd-navbar-overlay') || this.createOverlay(navbar);
|
|
52
83
|
|
|
84
|
+
// Store cleanup functions for this navbar instance
|
|
85
|
+
const cleanupFunctions = [];
|
|
86
|
+
|
|
87
|
+
// Wire up scroll-aware glass/transparent behaviour regardless of mobile menu
|
|
88
|
+
const scrollWatcherCleanup = this.initScrollWatcher(navbar);
|
|
89
|
+
if (scrollWatcherCleanup) {
|
|
90
|
+
cleanupFunctions.push(scrollWatcherCleanup);
|
|
91
|
+
}
|
|
92
|
+
|
|
53
93
|
if (!toggle || !menu) {
|
|
94
|
+
// Still store the instance so scroll-watcher cleanup is tracked
|
|
95
|
+
if (cleanupFunctions.length) {
|
|
96
|
+
this.instances.set(navbar, { toggle: null, menu: null, overlay: null, cleanup: cleanupFunctions });
|
|
97
|
+
}
|
|
54
98
|
return;
|
|
55
99
|
}
|
|
56
100
|
|
|
57
|
-
// Store cleanup functions for this navbar instance
|
|
58
|
-
const cleanupFunctions = [];
|
|
59
|
-
|
|
60
101
|
// Toggle menu on button click
|
|
61
102
|
const toggleClickHandler = (e) => {
|
|
62
103
|
e.preventDefault();
|
|
@@ -147,6 +147,7 @@
|
|
|
147
147
|
loadPreferences: function () {
|
|
148
148
|
this.state.theme = this.getStorageValue(this.STORAGE_KEYS.THEME, this.DEFAULTS.THEME);
|
|
149
149
|
this.state.primary = this.getStorageValue(this.STORAGE_KEYS.PRIMARY, this.getDefaultPrimary(this.state.theme));
|
|
150
|
+
this._normalizeDefaultPrimaryIfStaleWithStoredTheme();
|
|
150
151
|
this.state.neutral = this.getStorageValue(this.STORAGE_KEYS.NEUTRAL, this.DEFAULTS.NEUTRAL);
|
|
151
152
|
this.state.radius = this.getStorageValue(this.STORAGE_KEYS.RADIUS, this.DEFAULTS.RADIUS);
|
|
152
153
|
this.state.font = this.getStorageValue(this.STORAGE_KEYS.FONT, this.DEFAULTS.FONT);
|
|
@@ -254,15 +255,12 @@
|
|
|
254
255
|
// Prevent circular updates
|
|
255
256
|
this._isApplying = true;
|
|
256
257
|
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const newDefault = this.getDefaultPrimary(mode);
|
|
264
|
-
if (newDefault !== this.state.primary) {
|
|
265
|
-
this.applyPrimary(newDefault);
|
|
258
|
+
// Re-align black/amber when they don't match the effective primary for the target mode.
|
|
259
|
+
// Covers theme toggles and stale localStorage (e.g. amber saved while theme is light).
|
|
260
|
+
if (this.isUsingDefaultPrimary()) {
|
|
261
|
+
const expected = this.getDefaultPrimary(mode);
|
|
262
|
+
if (this.state.primary !== expected) {
|
|
263
|
+
this.applyPrimary(expected);
|
|
266
264
|
}
|
|
267
265
|
}
|
|
268
266
|
|
|
@@ -581,6 +579,21 @@
|
|
|
581
579
|
this.state.primary === this.DEFAULTS.PRIMARY_DARK;
|
|
582
580
|
},
|
|
583
581
|
|
|
582
|
+
/**
|
|
583
|
+
* When primary is still one of the auto-default palette keys (black/amber) but
|
|
584
|
+
* localStorage was written under a different theme (or OS changed in system mode),
|
|
585
|
+
* align in-memory state before applyAllPreferences runs — avoids amber+light / black+dark drift.
|
|
586
|
+
*/
|
|
587
|
+
_normalizeDefaultPrimaryIfStaleWithStoredTheme: function () {
|
|
588
|
+
if (!this.isUsingDefaultPrimary()) {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
const expected = this.getDefaultPrimary(this.state.theme);
|
|
592
|
+
if (this.state.primary !== expected) {
|
|
593
|
+
this.state.primary = expected;
|
|
594
|
+
}
|
|
595
|
+
},
|
|
596
|
+
|
|
584
597
|
bindEvents: function () {
|
|
585
598
|
// Trigger click - bind to any trigger button
|
|
586
599
|
if (this.elements.trigger) {
|
|
@@ -103,6 +103,10 @@
|
|
|
103
103
|
if (this.state.preference === 'system') {
|
|
104
104
|
// Re-apply (effectively just to ensure consistency, though removing attribute usually suffices)
|
|
105
105
|
this.applyTheme();
|
|
106
|
+
// Keep default primary (black/amber) aligned when OS scheme changes while in system mode
|
|
107
|
+
if (window.ThemeCustomizer && typeof window.ThemeCustomizer.applyTheme === 'function' && !window.ThemeCustomizer._isApplying) {
|
|
108
|
+
window.ThemeCustomizer.applyTheme('system');
|
|
109
|
+
}
|
|
106
110
|
}
|
|
107
111
|
};
|
|
108
112
|
this._mediaQuery.addEventListener('change', this._onMediaChange);
|
package/js/index.js
CHANGED
|
@@ -47,6 +47,9 @@ import './components/doc-search.js';
|
|
|
47
47
|
import './components/draggable.js';
|
|
48
48
|
import './components/lazy-load.js';
|
|
49
49
|
|
|
50
|
+
// Effects (glass scroll activation)
|
|
51
|
+
import './components/glass.js';
|
|
52
|
+
|
|
50
53
|
// Phase 10 (v1.2.7) components
|
|
51
54
|
import './components/flow.js';
|
|
52
55
|
import './components/bubble.js';
|