@vanduo-oss/framework 1.3.3 → 1.3.5
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 +0 -14
- package/README.md +33 -166
- package/css/components/cards.css +8 -0
- 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 +37 -8
- package/css/core/tokens.css +37 -0
- package/css/core/vd-aliases.css +13 -0
- package/css/effects/glass.css +154 -0
- package/css/effects/morph.css +259 -0
- package/css/vanduo.css +2 -0
- package/dist/build-info.json +3 -3
- package/dist/vanduo.cjs.js +207 -3
- 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 +501 -9
- package/dist/vanduo.css.map +1 -1
- package/dist/vanduo.esm.js +207 -3
- 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 +207 -3
- 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/glass.js +87 -0
- package/js/components/morph.js +137 -0
- package/js/components/navbar.js +44 -3
- package/js/index.js +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vanduo Framework - Glass Effects
|
|
3
|
+
* Frosted glass utility surfaces and modifiers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
:root {
|
|
7
|
+
--vd-glass-float-shadow: 0 14px 42px rgba(0, 0, 0, 0.2);
|
|
8
|
+
--vd-glass-float-translate-y: -2px;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.vd-glass,
|
|
12
|
+
.vd-glass-md {
|
|
13
|
+
position: relative;
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
background: var(--vd-glass-bg-light);
|
|
16
|
+
border: 1px solid var(--vd-glass-border-light);
|
|
17
|
+
box-shadow: var(--vd-glass-shadow);
|
|
18
|
+
backdrop-filter: blur(var(--vd-glass-blur)) saturate(var(--vd-glass-saturate));
|
|
19
|
+
-webkit-backdrop-filter: blur(var(--vd-glass-blur)) saturate(var(--vd-glass-saturate));
|
|
20
|
+
isolation: isolate;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.vd-glass::before,
|
|
24
|
+
.vd-glass-md::before {
|
|
25
|
+
content: "";
|
|
26
|
+
position: absolute;
|
|
27
|
+
inset: 0;
|
|
28
|
+
background: linear-gradient(
|
|
29
|
+
to bottom,
|
|
30
|
+
rgba(255, 255, 255, var(--vd-glass-highlight-alpha)) 0%,
|
|
31
|
+
rgba(255, 255, 255, 0) 45%
|
|
32
|
+
);
|
|
33
|
+
pointer-events: none;
|
|
34
|
+
z-index: 1;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.vd-glass::after,
|
|
38
|
+
.vd-glass-md::after {
|
|
39
|
+
content: "";
|
|
40
|
+
position: absolute;
|
|
41
|
+
inset: 0;
|
|
42
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140' viewBox='0 0 140 140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.15' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.25'/%3E%3C/svg%3E");
|
|
43
|
+
opacity: var(--vd-glass-noise-opacity);
|
|
44
|
+
pointer-events: none;
|
|
45
|
+
z-index: 2;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.vd-glass > *,
|
|
49
|
+
.vd-glass-md > * {
|
|
50
|
+
position: relative;
|
|
51
|
+
z-index: 3;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.vd-glass-sm {
|
|
55
|
+
--vd-glass-blur: 6px;
|
|
56
|
+
--vd-glass-saturate: 1.4;
|
|
57
|
+
--vd-glass-bg-opacity: 0.55;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.vd-glass-lg {
|
|
61
|
+
--vd-glass-blur: 20px;
|
|
62
|
+
--vd-glass-saturate: 2;
|
|
63
|
+
--vd-glass-bg-opacity: 0.72;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.vd-glass-xl {
|
|
67
|
+
--vd-glass-blur: 28px;
|
|
68
|
+
--vd-glass-saturate: 2.2;
|
|
69
|
+
--vd-glass-bg-opacity: 0.78;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.vd-glass-tinted {
|
|
73
|
+
background: linear-gradient(
|
|
74
|
+
135deg,
|
|
75
|
+
var(--vd-glass-tint, var(--vd-color-primary-alpha-10)) 0%,
|
|
76
|
+
transparent 70%
|
|
77
|
+
),
|
|
78
|
+
var(--vd-glass-bg-light);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.vd-glass-floating {
|
|
82
|
+
transition: transform var(--vd-transition-normal), box-shadow var(--vd-transition-normal);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.vd-glass-floating:hover,
|
|
86
|
+
.vd-glass-floating:focus-within {
|
|
87
|
+
transform: translateY(var(--vd-glass-float-translate-y));
|
|
88
|
+
box-shadow: var(--vd-glass-float-shadow);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.vd-glass-contrast {
|
|
92
|
+
--vd-glass-bg-opacity: 0.82;
|
|
93
|
+
--vd-glass-border-alpha: 0.26;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Scroll-activated glass: inactive until .is-glass-active is set by glass.js */
|
|
97
|
+
[data-glass-scroll] {
|
|
98
|
+
transition:
|
|
99
|
+
background var(--vd-transition-normal, 0.25s ease),
|
|
100
|
+
border-color var(--vd-transition-normal, 0.25s ease),
|
|
101
|
+
box-shadow var(--vd-transition-normal, 0.25s ease),
|
|
102
|
+
backdrop-filter var(--vd-transition-normal, 0.25s ease);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
[data-glass-scroll]:not(.is-glass-active) {
|
|
106
|
+
background: transparent !important;
|
|
107
|
+
border-color: transparent !important;
|
|
108
|
+
box-shadow: none !important;
|
|
109
|
+
backdrop-filter: none !important;
|
|
110
|
+
-webkit-backdrop-filter: none !important;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
[data-glass-scroll]:not(.is-glass-active)::before,
|
|
114
|
+
[data-glass-scroll]:not(.is-glass-active)::after {
|
|
115
|
+
opacity: 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
|
|
119
|
+
.vd-glass,
|
|
120
|
+
.vd-glass-md {
|
|
121
|
+
background: var(--vd-bg-primary);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
126
|
+
.vd-glass,
|
|
127
|
+
.vd-glass-sm,
|
|
128
|
+
.vd-glass-md,
|
|
129
|
+
.vd-glass-lg,
|
|
130
|
+
.vd-glass-xl {
|
|
131
|
+
backdrop-filter: none;
|
|
132
|
+
-webkit-backdrop-filter: none;
|
|
133
|
+
background: var(--vd-bg-primary);
|
|
134
|
+
border-color: var(--vd-border-color);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.vd-glass::before,
|
|
138
|
+
.vd-glass::after,
|
|
139
|
+
.vd-glass-md::before,
|
|
140
|
+
.vd-glass-md::after {
|
|
141
|
+
display: none;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@media (prefers-reduced-motion: reduce) {
|
|
146
|
+
.vd-glass-floating {
|
|
147
|
+
transition: none;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.vd-glass-floating:hover,
|
|
151
|
+
.vd-glass-floating:focus-within {
|
|
152
|
+
transform: none;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vanduo Framework - Water Morph Effect
|
|
3
|
+
* Liquid wave content-swap animation on click
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* Add `.vd-morph` (or `data-vd-morph`) to any element.
|
|
7
|
+
* Inside it, place:
|
|
8
|
+
* .vd-morph-wave — radial wave layer (auto-created by JS if missing)
|
|
9
|
+
* .vd-morph-shine — light-sweep layer (auto-created by JS if missing)
|
|
10
|
+
* .vd-morph-content.vd-morph-current — visible state
|
|
11
|
+
* .vd-morph-content.vd-morph-next — hidden next state
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
:root {
|
|
15
|
+
--morph-duration: 0.75s;
|
|
16
|
+
--morph-easing: cubic-bezier(0.2, 0.8, 0.35, 1);
|
|
17
|
+
--morph-blur-peak: 11px;
|
|
18
|
+
--morph-wave-color: var(--color-primary, #3b82f6);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* ========== Base ========== */
|
|
22
|
+
|
|
23
|
+
.vd-morph,
|
|
24
|
+
[data-vd-morph] {
|
|
25
|
+
position: relative;
|
|
26
|
+
overflow: hidden;
|
|
27
|
+
cursor: pointer;
|
|
28
|
+
-webkit-tap-highlight-color: transparent;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* ========== Wave Layer ========== */
|
|
32
|
+
|
|
33
|
+
.vd-morph-wave {
|
|
34
|
+
position: absolute;
|
|
35
|
+
border-radius: 50%;
|
|
36
|
+
background:
|
|
37
|
+
radial-gradient(circle at 32% 28%,
|
|
38
|
+
color-mix(in srgb, #fff 55%, transparent) 0%,
|
|
39
|
+
color-mix(in srgb, #fff 18%, transparent) 18%,
|
|
40
|
+
transparent 38%),
|
|
41
|
+
radial-gradient(circle at 68% 70%,
|
|
42
|
+
color-mix(in srgb, #fff 30%, transparent) 0%,
|
|
43
|
+
transparent 30%),
|
|
44
|
+
radial-gradient(circle at 50% 50%,
|
|
45
|
+
color-mix(in srgb, var(--morph-wave-color) 96%, #a8d8ff) 0%,
|
|
46
|
+
color-mix(in srgb, var(--morph-wave-color) 82%, #7ec8ff) 35%,
|
|
47
|
+
color-mix(in srgb, var(--morph-wave-color) 70%, #5ba4f5) 65%,
|
|
48
|
+
color-mix(in srgb, var(--morph-wave-color) 90%, #4478f0) 100%);
|
|
49
|
+
pointer-events: none;
|
|
50
|
+
opacity: 0;
|
|
51
|
+
transform: translate(-50%, -50%);
|
|
52
|
+
will-change: width, height, opacity, filter;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.vd-morph.is-morphing .vd-morph-wave {
|
|
56
|
+
animation: vd-morph-expand var(--morph-duration) var(--morph-easing) forwards;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.vd-morph.is-morphing .vd-morph-wave::after {
|
|
60
|
+
content: '';
|
|
61
|
+
position: absolute;
|
|
62
|
+
top: 50%;
|
|
63
|
+
left: 50%;
|
|
64
|
+
width: 0;
|
|
65
|
+
height: 0;
|
|
66
|
+
border-radius: 50%;
|
|
67
|
+
background:
|
|
68
|
+
radial-gradient(circle at 50% 50%,
|
|
69
|
+
color-mix(in srgb, var(--morph-wave-color) 70%, #b0e0ff) 0%,
|
|
70
|
+
color-mix(in srgb, var(--morph-wave-color) 50%, #88c0ff) 45%,
|
|
71
|
+
transparent 80%);
|
|
72
|
+
transform: translate(-50%, -50%);
|
|
73
|
+
opacity: 0;
|
|
74
|
+
animation: vd-morph-expand2 var(--morph-duration) var(--morph-easing) 0.08s forwards;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@keyframes vd-morph-expand {
|
|
78
|
+
0% {
|
|
79
|
+
width: 0;
|
|
80
|
+
height: 0;
|
|
81
|
+
opacity: 0;
|
|
82
|
+
filter: blur(0px) saturate(2);
|
|
83
|
+
}
|
|
84
|
+
6% {
|
|
85
|
+
opacity: 1;
|
|
86
|
+
filter: blur(1px) saturate(2.2);
|
|
87
|
+
}
|
|
88
|
+
35% {
|
|
89
|
+
opacity: 0.92;
|
|
90
|
+
filter: blur(6px) saturate(1.7);
|
|
91
|
+
}
|
|
92
|
+
68% {
|
|
93
|
+
width: 360%;
|
|
94
|
+
height: 360%;
|
|
95
|
+
opacity: 0.6;
|
|
96
|
+
filter: blur(var(--morph-blur-peak)) saturate(1.3);
|
|
97
|
+
}
|
|
98
|
+
100% {
|
|
99
|
+
width: 360%;
|
|
100
|
+
height: 360%;
|
|
101
|
+
opacity: 0;
|
|
102
|
+
filter: blur(7px) saturate(1);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@keyframes vd-morph-expand2 {
|
|
107
|
+
0% {
|
|
108
|
+
width: 0;
|
|
109
|
+
height: 0;
|
|
110
|
+
opacity: 0;
|
|
111
|
+
filter: blur(0px);
|
|
112
|
+
}
|
|
113
|
+
10% {
|
|
114
|
+
opacity: 0.7;
|
|
115
|
+
filter: blur(3px);
|
|
116
|
+
}
|
|
117
|
+
60% {
|
|
118
|
+
width: 280%;
|
|
119
|
+
height: 280%;
|
|
120
|
+
opacity: 0.38;
|
|
121
|
+
filter: blur(14px);
|
|
122
|
+
}
|
|
123
|
+
100% {
|
|
124
|
+
width: 280%;
|
|
125
|
+
height: 280%;
|
|
126
|
+
opacity: 0;
|
|
127
|
+
filter: blur(10px);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* ========== Shine Sweep ========== */
|
|
132
|
+
|
|
133
|
+
.vd-morph-shine {
|
|
134
|
+
position: absolute;
|
|
135
|
+
top: 0;
|
|
136
|
+
left: -110%;
|
|
137
|
+
width: 80%;
|
|
138
|
+
height: 100%;
|
|
139
|
+
background: linear-gradient(
|
|
140
|
+
105deg,
|
|
141
|
+
transparent 0%,
|
|
142
|
+
color-mix(in srgb, #fff 10%, transparent) 35%,
|
|
143
|
+
color-mix(in srgb, #fff 52%, transparent) 50%,
|
|
144
|
+
color-mix(in srgb, #fff 10%, transparent) 65%,
|
|
145
|
+
transparent 100%
|
|
146
|
+
);
|
|
147
|
+
pointer-events: none;
|
|
148
|
+
opacity: 0;
|
|
149
|
+
transform: skewX(-12deg);
|
|
150
|
+
will-change: left, opacity;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.vd-morph.is-morphing .vd-morph-shine {
|
|
154
|
+
animation: vd-morph-shine 0.72s cubic-bezier(0.3, 0.65, 0.5, 1) 0.04s forwards;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
@keyframes vd-morph-shine {
|
|
158
|
+
0% {
|
|
159
|
+
left: -110%;
|
|
160
|
+
opacity: 0;
|
|
161
|
+
}
|
|
162
|
+
15% {
|
|
163
|
+
opacity: 0.9;
|
|
164
|
+
}
|
|
165
|
+
72% {
|
|
166
|
+
opacity: 0.4;
|
|
167
|
+
}
|
|
168
|
+
100% {
|
|
169
|
+
left: 130%;
|
|
170
|
+
opacity: 0;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* ========== Completion Bounce ========== */
|
|
175
|
+
|
|
176
|
+
.vd-morph.morph-done {
|
|
177
|
+
animation: vd-morph-bounce 0.32s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
@keyframes vd-morph-bounce {
|
|
181
|
+
0% { transform: scale(0.97); }
|
|
182
|
+
60% { transform: scale(1.025); }
|
|
183
|
+
100% { transform: scale(1); }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/* ========== Content Layers ========== */
|
|
187
|
+
|
|
188
|
+
.vd-morph-content {
|
|
189
|
+
position: absolute;
|
|
190
|
+
inset: 0;
|
|
191
|
+
display: flex;
|
|
192
|
+
align-items: center;
|
|
193
|
+
justify-content: center;
|
|
194
|
+
gap: 0.45rem;
|
|
195
|
+
pointer-events: none;
|
|
196
|
+
will-change: opacity, transform, filter;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.vd-morph-current {
|
|
200
|
+
opacity: 1;
|
|
201
|
+
transform: scale(1) translateY(0);
|
|
202
|
+
filter: blur(0px);
|
|
203
|
+
z-index: 2;
|
|
204
|
+
transition:
|
|
205
|
+
opacity 0.28s cubic-bezier(0.4, 0, 0.6, 1),
|
|
206
|
+
transform 0.28s cubic-bezier(0.4, 0, 0.6, 1),
|
|
207
|
+
filter 0.28s ease;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.vd-morph-next {
|
|
211
|
+
opacity: 0;
|
|
212
|
+
transform: scale(0.86) translateY(4px);
|
|
213
|
+
filter: blur(3px);
|
|
214
|
+
z-index: 1;
|
|
215
|
+
transition:
|
|
216
|
+
opacity 0.3s cubic-bezier(0, 0, 0.2, 1),
|
|
217
|
+
transform 0.3s cubic-bezier(0, 0, 0.2, 1),
|
|
218
|
+
filter 0.3s ease;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.vd-morph.is-morphing .vd-morph-current {
|
|
222
|
+
opacity: 0;
|
|
223
|
+
transform: scale(0.82) translateY(-4px);
|
|
224
|
+
filter: blur(4px);
|
|
225
|
+
transition-duration: 0.2s;
|
|
226
|
+
transition-delay: 0s;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.vd-morph.is-morphing .vd-morph-next {
|
|
230
|
+
opacity: 1;
|
|
231
|
+
transform: scale(1) translateY(0);
|
|
232
|
+
filter: blur(0px);
|
|
233
|
+
transition-duration: 0.3s;
|
|
234
|
+
transition-delay: 0.25s;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* ========== Size Variants ========== */
|
|
238
|
+
|
|
239
|
+
.vd-morph-sm {
|
|
240
|
+
--morph-duration: 0.5s;
|
|
241
|
+
--morph-blur-peak: 7px;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.vd-morph-lg {
|
|
245
|
+
--morph-duration: 1s;
|
|
246
|
+
--morph-blur-peak: 16px;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/* ========== Accessibility ========== */
|
|
250
|
+
|
|
251
|
+
@media (prefers-reduced-motion: reduce) {
|
|
252
|
+
.vd-morph,
|
|
253
|
+
.vd-morph-content,
|
|
254
|
+
.vd-morph-wave,
|
|
255
|
+
.vd-morph-shine {
|
|
256
|
+
transition: none !important;
|
|
257
|
+
animation: none !important;
|
|
258
|
+
}
|
|
259
|
+
}
|
package/css/vanduo.css
CHANGED
package/dist/build-info.json
CHANGED
package/dist/vanduo.cjs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Vanduo v1.3.
|
|
1
|
+
/*! Vanduo v1.3.5 | Built: 2026-04-15T18:39:53.955Z | git:3ca4f62 | development */
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -132,7 +132,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
132
132
|
// js/vanduo.js
|
|
133
133
|
(function() {
|
|
134
134
|
"use strict";
|
|
135
|
-
const VANDUO_VERSION = true ? "1.3.
|
|
135
|
+
const VANDUO_VERSION = true ? "1.3.5" : "0.0.0-dev";
|
|
136
136
|
const Vanduo2 = {
|
|
137
137
|
version: VANDUO_VERSION,
|
|
138
138
|
components: {},
|
|
@@ -2182,6 +2182,31 @@ module.exports = __toCommonJS(index_exports);
|
|
|
2182
2182
|
this.initNavbar(navbar);
|
|
2183
2183
|
});
|
|
2184
2184
|
},
|
|
2185
|
+
/**
|
|
2186
|
+
* Initialize scroll-aware glass/transparent behaviour for a navbar.
|
|
2187
|
+
* Adds/removes `.vd-navbar-scrolled` when the page scrolls past a threshold.
|
|
2188
|
+
* Threshold: `data-scroll-threshold` attribute (px) or the navbar's own height.
|
|
2189
|
+
* @param {HTMLElement} navbar - Navbar element
|
|
2190
|
+
* @returns {Function|null} Cleanup function, or null if not applicable
|
|
2191
|
+
*/
|
|
2192
|
+
initScrollWatcher: function(navbar) {
|
|
2193
|
+
const isGlass = navbar.classList.contains("vd-navbar-glass");
|
|
2194
|
+
const isTransparent = navbar.classList.contains("vd-navbar-transparent");
|
|
2195
|
+
if (!isGlass && !isTransparent) {
|
|
2196
|
+
return null;
|
|
2197
|
+
}
|
|
2198
|
+
const getThreshold = () => {
|
|
2199
|
+
const attr = parseInt(navbar.dataset.scrollThreshold, 10);
|
|
2200
|
+
return isNaN(attr) ? navbar.offsetHeight || 60 : attr;
|
|
2201
|
+
};
|
|
2202
|
+
const onScroll = () => {
|
|
2203
|
+
const scrolled = window.scrollY > getThreshold();
|
|
2204
|
+
navbar.classList.toggle("vd-navbar-scrolled", scrolled);
|
|
2205
|
+
};
|
|
2206
|
+
onScroll();
|
|
2207
|
+
window.addEventListener("scroll", onScroll, { passive: true });
|
|
2208
|
+
return () => window.removeEventListener("scroll", onScroll);
|
|
2209
|
+
},
|
|
2185
2210
|
/**
|
|
2186
2211
|
* Initialize a single navbar
|
|
2187
2212
|
* @param {HTMLElement} navbar - Navbar element
|
|
@@ -2190,10 +2215,17 @@ module.exports = __toCommonJS(index_exports);
|
|
|
2190
2215
|
const toggle = navbar.querySelector(".vd-navbar-toggle, .vd-navbar-burger");
|
|
2191
2216
|
const menu = navbar.querySelector(".vd-navbar-menu");
|
|
2192
2217
|
const overlay = navbar.querySelector(".vd-navbar-overlay") || this.createOverlay(navbar);
|
|
2218
|
+
const cleanupFunctions = [];
|
|
2219
|
+
const scrollWatcherCleanup = this.initScrollWatcher(navbar);
|
|
2220
|
+
if (scrollWatcherCleanup) {
|
|
2221
|
+
cleanupFunctions.push(scrollWatcherCleanup);
|
|
2222
|
+
}
|
|
2193
2223
|
if (!toggle || !menu) {
|
|
2224
|
+
if (cleanupFunctions.length) {
|
|
2225
|
+
this.instances.set(navbar, { toggle: null, menu: null, overlay: null, cleanup: cleanupFunctions });
|
|
2226
|
+
}
|
|
2194
2227
|
return;
|
|
2195
2228
|
}
|
|
2196
|
-
const cleanupFunctions = [];
|
|
2197
2229
|
const toggleClickHandler = (e) => {
|
|
2198
2230
|
e.preventDefault();
|
|
2199
2231
|
e.stopPropagation();
|
|
@@ -6504,6 +6536,178 @@ module.exports = __toCommonJS(index_exports);
|
|
|
6504
6536
|
window.VanduoLazyLoad = VanduoLazyLoad;
|
|
6505
6537
|
})();
|
|
6506
6538
|
|
|
6539
|
+
// js/components/glass.js
|
|
6540
|
+
(function() {
|
|
6541
|
+
"use strict";
|
|
6542
|
+
const GlassScroll = {
|
|
6543
|
+
/** @type {Map<Element, IntersectionObserver>} */
|
|
6544
|
+
observers: /* @__PURE__ */ new Map(),
|
|
6545
|
+
init: function() {
|
|
6546
|
+
document.querySelectorAll("[data-glass-scroll]").forEach((el) => {
|
|
6547
|
+
if (this.observers.has(el)) return;
|
|
6548
|
+
this.initElement(el);
|
|
6549
|
+
});
|
|
6550
|
+
},
|
|
6551
|
+
/**
|
|
6552
|
+
* Wire up a single scroll-activated glass element.
|
|
6553
|
+
* @param {HTMLElement} el
|
|
6554
|
+
*/
|
|
6555
|
+
initElement: function(el) {
|
|
6556
|
+
const sentinelSelector = el.dataset.glassSentinel;
|
|
6557
|
+
let sentinel;
|
|
6558
|
+
if (sentinelSelector) {
|
|
6559
|
+
sentinel = document.querySelector(sentinelSelector);
|
|
6560
|
+
}
|
|
6561
|
+
if (!sentinel) {
|
|
6562
|
+
sentinel = el.previousElementSibling;
|
|
6563
|
+
}
|
|
6564
|
+
if (!sentinel) {
|
|
6565
|
+
el.classList.add("is-glass-active");
|
|
6566
|
+
return;
|
|
6567
|
+
}
|
|
6568
|
+
const observer = new IntersectionObserver(
|
|
6569
|
+
(entries) => {
|
|
6570
|
+
entries.forEach((entry) => {
|
|
6571
|
+
el.classList.toggle("is-glass-active", !entry.isIntersecting);
|
|
6572
|
+
});
|
|
6573
|
+
},
|
|
6574
|
+
{ threshold: 0, rootMargin: "0px" }
|
|
6575
|
+
);
|
|
6576
|
+
observer.observe(sentinel);
|
|
6577
|
+
this.observers.set(el, observer);
|
|
6578
|
+
},
|
|
6579
|
+
/**
|
|
6580
|
+
* Disconnect and remove a single element's observer.
|
|
6581
|
+
* @param {HTMLElement} el
|
|
6582
|
+
*/
|
|
6583
|
+
destroy: function(el) {
|
|
6584
|
+
const observer = this.observers.get(el);
|
|
6585
|
+
if (observer) {
|
|
6586
|
+
observer.disconnect();
|
|
6587
|
+
this.observers.delete(el);
|
|
6588
|
+
}
|
|
6589
|
+
},
|
|
6590
|
+
destroyAll: function() {
|
|
6591
|
+
this.observers.forEach((observer, el) => this.destroy(el));
|
|
6592
|
+
}
|
|
6593
|
+
};
|
|
6594
|
+
if (typeof window.Vanduo !== "undefined") {
|
|
6595
|
+
window.Vanduo.register("glassScroll", GlassScroll);
|
|
6596
|
+
}
|
|
6597
|
+
window.VanduoGlassScroll = GlassScroll;
|
|
6598
|
+
})();
|
|
6599
|
+
|
|
6600
|
+
// js/components/morph.js
|
|
6601
|
+
(function() {
|
|
6602
|
+
"use strict";
|
|
6603
|
+
const MORPH_DURATION_MS = 750;
|
|
6604
|
+
const Morph = {
|
|
6605
|
+
instances: /* @__PURE__ */ new Map(),
|
|
6606
|
+
init: function() {
|
|
6607
|
+
const elements = document.querySelectorAll(".vd-morph, [data-vd-morph]");
|
|
6608
|
+
elements.forEach(function(el) {
|
|
6609
|
+
if (Morph.instances.has(el)) return;
|
|
6610
|
+
if (el.getAttribute("data-vd-morph") === "manual") return;
|
|
6611
|
+
Morph.initInstance(el);
|
|
6612
|
+
});
|
|
6613
|
+
},
|
|
6614
|
+
initInstance: function(el) {
|
|
6615
|
+
Morph._ensureLayers(el);
|
|
6616
|
+
const cleanup = [];
|
|
6617
|
+
let morphing = false;
|
|
6618
|
+
const handleClick = function(e) {
|
|
6619
|
+
if (morphing) return;
|
|
6620
|
+
Morph._runMorph(el, e, function() {
|
|
6621
|
+
morphing = false;
|
|
6622
|
+
});
|
|
6623
|
+
morphing = true;
|
|
6624
|
+
};
|
|
6625
|
+
el.addEventListener("click", handleClick);
|
|
6626
|
+
cleanup.push(function() {
|
|
6627
|
+
el.removeEventListener("click", handleClick);
|
|
6628
|
+
});
|
|
6629
|
+
this.instances.set(el, { cleanup });
|
|
6630
|
+
},
|
|
6631
|
+
morph: function(el) {
|
|
6632
|
+
if (!el) return;
|
|
6633
|
+
if (!this.instances.has(el)) this.initInstance(el);
|
|
6634
|
+
this._runMorph(el, null, null);
|
|
6635
|
+
},
|
|
6636
|
+
destroy: function(el) {
|
|
6637
|
+
const instance = this.instances.get(el);
|
|
6638
|
+
if (!instance) return;
|
|
6639
|
+
instance.cleanup.forEach(function(fn) {
|
|
6640
|
+
fn();
|
|
6641
|
+
});
|
|
6642
|
+
this.instances.delete(el);
|
|
6643
|
+
},
|
|
6644
|
+
destroyAll: function() {
|
|
6645
|
+
this.instances.forEach(function(_, el) {
|
|
6646
|
+
Morph.destroy(el);
|
|
6647
|
+
});
|
|
6648
|
+
},
|
|
6649
|
+
/* ── Internal helpers ── */
|
|
6650
|
+
_ensureLayers: function(el) {
|
|
6651
|
+
if (!el.querySelector(".vd-morph-wave")) {
|
|
6652
|
+
const wave = document.createElement("span");
|
|
6653
|
+
wave.className = "vd-morph-wave";
|
|
6654
|
+
wave.setAttribute("aria-hidden", "true");
|
|
6655
|
+
el.insertBefore(wave, el.firstChild);
|
|
6656
|
+
}
|
|
6657
|
+
if (!el.querySelector(".vd-morph-shine")) {
|
|
6658
|
+
const shine = document.createElement("span");
|
|
6659
|
+
shine.className = "vd-morph-shine";
|
|
6660
|
+
shine.setAttribute("aria-hidden", "true");
|
|
6661
|
+
const waveEl = el.querySelector(".vd-morph-wave");
|
|
6662
|
+
if (waveEl && waveEl.nextSibling) {
|
|
6663
|
+
el.insertBefore(shine, waveEl.nextSibling);
|
|
6664
|
+
} else {
|
|
6665
|
+
el.insertBefore(shine, el.firstChild);
|
|
6666
|
+
}
|
|
6667
|
+
}
|
|
6668
|
+
},
|
|
6669
|
+
_runMorph: function(el, pointerEvent, onComplete) {
|
|
6670
|
+
const wave = el.querySelector(".vd-morph-wave");
|
|
6671
|
+
if (wave) {
|
|
6672
|
+
const rect = el.getBoundingClientRect();
|
|
6673
|
+
const cx = rect.left + rect.width / 2;
|
|
6674
|
+
const cy = rect.top + rect.height / 2;
|
|
6675
|
+
const px = pointerEvent ? pointerEvent.clientX || cx : cx;
|
|
6676
|
+
const py = pointerEvent ? pointerEvent.clientY || cy : cy;
|
|
6677
|
+
wave.style.left = px - rect.left + "px";
|
|
6678
|
+
wave.style.top = py - rect.top + "px";
|
|
6679
|
+
}
|
|
6680
|
+
el.classList.add("is-morphing");
|
|
6681
|
+
let duration = MORPH_DURATION_MS;
|
|
6682
|
+
const custom = getComputedStyle(el).getPropertyValue("--morph-duration");
|
|
6683
|
+
if (custom) {
|
|
6684
|
+
const parsed = parseFloat(custom);
|
|
6685
|
+
if (!isNaN(parsed)) duration = parsed * (custom.indexOf("ms") !== -1 ? 1 : 1e3);
|
|
6686
|
+
}
|
|
6687
|
+
setTimeout(function() {
|
|
6688
|
+
el.classList.remove("is-morphing");
|
|
6689
|
+
const current = el.querySelector(".vd-morph-current");
|
|
6690
|
+
const next = el.querySelector(".vd-morph-next");
|
|
6691
|
+
if (current && next) {
|
|
6692
|
+
current.classList.remove("vd-morph-current");
|
|
6693
|
+
current.classList.add("vd-morph-next");
|
|
6694
|
+
next.classList.remove("vd-morph-next");
|
|
6695
|
+
next.classList.add("vd-morph-current");
|
|
6696
|
+
}
|
|
6697
|
+
el.classList.add("morph-done");
|
|
6698
|
+
setTimeout(function() {
|
|
6699
|
+
el.classList.remove("morph-done");
|
|
6700
|
+
}, 350);
|
|
6701
|
+
if (typeof onComplete === "function") onComplete();
|
|
6702
|
+
}, duration);
|
|
6703
|
+
}
|
|
6704
|
+
};
|
|
6705
|
+
if (typeof window.Vanduo !== "undefined") {
|
|
6706
|
+
window.Vanduo.register("morph", Morph);
|
|
6707
|
+
}
|
|
6708
|
+
window.VanduoMorph = Morph;
|
|
6709
|
+
})();
|
|
6710
|
+
|
|
6507
6711
|
// js/components/flow.js
|
|
6508
6712
|
(function() {
|
|
6509
6713
|
"use strict";
|