@dazecoop/liquid-glass 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +260 -2
- package/lib/index.cjs +1184 -0
- package/lib/index.d.cts +156 -0
- package/lib/index.d.ts +156 -0
- package/lib/index.js +1184 -0
- package/lib/liquid-glass.css +390 -0
- package/lib/liquid-glass.global.js +14 -0
- package/lib/react.cjs +238 -0
- package/lib/react.d.cts +91 -0
- package/lib/react.d.ts +91 -0
- package/lib/react.js +238 -0
- package/package.json +81 -4
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
/* Liquid Glass UI components: the switch, segmented switch, tab bar and button from
|
|
2
|
+
components.js. Plain CSS, no build step needed.
|
|
3
|
+
|
|
4
|
+
Every colour is a custom property, so a theme is a handful of overrides. The dark look is
|
|
5
|
+
the default; put data-theme="light" on any ancestor for the light one (the components'
|
|
6
|
+
JS reads the same attribute to pick their glass). --lg-surface is the colour right behind
|
|
7
|
+
a component: the glass's copy is ringed with it, so it never bends see-through pixels. */
|
|
8
|
+
|
|
9
|
+
:where(:root, [data-theme="dark"]) {
|
|
10
|
+
--lg-surface: #000000;
|
|
11
|
+
--lg-label: #ffffff;
|
|
12
|
+
--lg-label-2: rgb(235 235 245 / 0.6);
|
|
13
|
+
--lg-fill: rgb(118 118 128 / 0.24);
|
|
14
|
+
--lg-off: #39393d;
|
|
15
|
+
--lg-on: #30d158;
|
|
16
|
+
--lg-accent: #0a84ff;
|
|
17
|
+
--lg-platter: rgb(30 30 33 / 0.62);
|
|
18
|
+
--lg-platter-solid: #1c1c1f;
|
|
19
|
+
--lg-pill: rgb(0 0 0 / 0.5);
|
|
20
|
+
--lg-lens-shadow: 0 7px 18px -2px rgb(0 0 0 / 0.5);
|
|
21
|
+
--lg-button-shadow: 0 6px 16px -4px rgb(0 0 0 / 0.3);
|
|
22
|
+
}
|
|
23
|
+
:where([data-theme="light"]) {
|
|
24
|
+
--lg-surface: #f2f2f7;
|
|
25
|
+
--lg-label: #000000;
|
|
26
|
+
--lg-label-2: rgb(60 60 67 / 0.6);
|
|
27
|
+
--lg-fill: rgb(120 120 128 / 0.16);
|
|
28
|
+
--lg-off: #e9e9eb;
|
|
29
|
+
--lg-on: #34c759;
|
|
30
|
+
--lg-accent: #007aff;
|
|
31
|
+
--lg-platter: rgb(255 255 255 / 0.72);
|
|
32
|
+
--lg-platter-solid: #f7f7f8;
|
|
33
|
+
--lg-pill: rgb(0 0 0 / 0.07);
|
|
34
|
+
--lg-lens-shadow: 0 0 0 0.5px rgb(0 0 0 / 0.1), 0 7px 18px rgb(0 0 0 / 0.12);
|
|
35
|
+
--lg-button-shadow: 0 0 0 0.5px rgb(0 0 0 / 0.08), 0 6px 16px -4px rgb(0 0 0 / 0.18);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* ---------- Switches ----------
|
|
39
|
+
Each lens sits over the thumb and under the labels, and refracts .refract: a bar in the
|
|
40
|
+
track's colour, hidden on the page and shown only in the lens's copy, ringed there by a
|
|
41
|
+
wide solid shadow in the surface's colour */
|
|
42
|
+
.lg-switch,
|
|
43
|
+
.lg-segment {
|
|
44
|
+
--p: 0;
|
|
45
|
+
position: relative;
|
|
46
|
+
display: inline-grid;
|
|
47
|
+
grid-template-columns: 1fr 1fr;
|
|
48
|
+
border: 0;
|
|
49
|
+
border-radius: 999px;
|
|
50
|
+
cursor: pointer;
|
|
51
|
+
touch-action: pan-y;
|
|
52
|
+
user-select: none;
|
|
53
|
+
-webkit-user-select: none;
|
|
54
|
+
-webkit-tap-highlight-color: transparent;
|
|
55
|
+
}
|
|
56
|
+
.lg-switch:focus-visible,
|
|
57
|
+
.lg-segment button:focus-visible {
|
|
58
|
+
outline: 2px solid var(--lg-accent);
|
|
59
|
+
outline-offset: 2px;
|
|
60
|
+
}
|
|
61
|
+
.lg-switch .thumb,
|
|
62
|
+
.lg-segment .thumb {
|
|
63
|
+
position: absolute;
|
|
64
|
+
border-radius: 999px;
|
|
65
|
+
transition: translate 0.35s cubic-bezier(0.32, 0.72, 0, 1);
|
|
66
|
+
}
|
|
67
|
+
.lg-switch.glassy .thumb,
|
|
68
|
+
.lg-segment.glassy .thumb {
|
|
69
|
+
transition: opacity 0.18s;
|
|
70
|
+
}
|
|
71
|
+
.lg-switch.lifted .thumb,
|
|
72
|
+
.lg-segment.lifted .thumb {
|
|
73
|
+
opacity: 0;
|
|
74
|
+
}
|
|
75
|
+
.lg-switch .refract,
|
|
76
|
+
.lg-segment .refract {
|
|
77
|
+
position: absolute;
|
|
78
|
+
inset: 12.5% auto 12.5% 0;
|
|
79
|
+
border-radius: 999px;
|
|
80
|
+
opacity: 0;
|
|
81
|
+
pointer-events: none;
|
|
82
|
+
}
|
|
83
|
+
.lg-switch .refract[data-glass-copy],
|
|
84
|
+
.lg-segment .refract[data-glass-copy] {
|
|
85
|
+
opacity: 1;
|
|
86
|
+
box-shadow: 0 0 0 80px var(--lg-surface);
|
|
87
|
+
}
|
|
88
|
+
.lg-switch [data-glass],
|
|
89
|
+
.lg-segment [data-glass] {
|
|
90
|
+
z-index: 2 !important;
|
|
91
|
+
background: var(--lg-surface);
|
|
92
|
+
box-shadow: var(--lg-lens-shadow) !important;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* The plain switch: 64 × 28, a white thumb as tall as the track, the track turning green
|
|
96
|
+
as the thumb crosses */
|
|
97
|
+
.lg-switch {
|
|
98
|
+
width: 64px;
|
|
99
|
+
height: 28px;
|
|
100
|
+
padding: 0;
|
|
101
|
+
background: color-mix(in srgb, var(--lg-off), var(--lg-on) calc(var(--p) * 100%));
|
|
102
|
+
}
|
|
103
|
+
.lg-switch .thumb {
|
|
104
|
+
inset: 0 auto 0 0;
|
|
105
|
+
width: 40px;
|
|
106
|
+
background: #fff;
|
|
107
|
+
box-shadow:
|
|
108
|
+
0 1px 3px rgb(0 0 0 / 0.2),
|
|
109
|
+
0 3px 8px rgb(0 0 0 / 0.12);
|
|
110
|
+
}
|
|
111
|
+
.lg-switch .refract {
|
|
112
|
+
background: color-mix(in srgb, var(--lg-off), var(--lg-on) calc(var(--p) * 100%));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* With words: a label each side, the thumb in the accent */
|
|
116
|
+
.lg-segment {
|
|
117
|
+
padding: 3px;
|
|
118
|
+
background: var(--lg-fill);
|
|
119
|
+
}
|
|
120
|
+
.lg-segment .thumb {
|
|
121
|
+
inset: 3px auto 3px 3px;
|
|
122
|
+
width: calc(50% - 3px);
|
|
123
|
+
background: var(--lg-accent);
|
|
124
|
+
}
|
|
125
|
+
.lg-segment .refract {
|
|
126
|
+
background: var(--lg-accent);
|
|
127
|
+
}
|
|
128
|
+
.lg-segment button {
|
|
129
|
+
position: relative;
|
|
130
|
+
z-index: 3;
|
|
131
|
+
min-width: 84px;
|
|
132
|
+
padding: 6px 14px;
|
|
133
|
+
border: 0;
|
|
134
|
+
border-radius: 999px;
|
|
135
|
+
background: none;
|
|
136
|
+
color: var(--lg-label-2);
|
|
137
|
+
font: inherit;
|
|
138
|
+
font-size: 14px;
|
|
139
|
+
font-weight: 600;
|
|
140
|
+
letter-spacing: -0.01em;
|
|
141
|
+
cursor: pointer;
|
|
142
|
+
transition: color 0.3s;
|
|
143
|
+
}
|
|
144
|
+
.lg-segment button[aria-checked="true"] {
|
|
145
|
+
color: #fff;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Pressed: a soft white glow under the finger, inside the lens */
|
|
149
|
+
[data-glass] > .lg-glow {
|
|
150
|
+
position: absolute;
|
|
151
|
+
width: 48px;
|
|
152
|
+
height: 48px;
|
|
153
|
+
margin: -24px 0 0 -24px;
|
|
154
|
+
border-radius: 50%;
|
|
155
|
+
background: radial-gradient(closest-side, rgb(255 255 255 / 0.7), rgb(255 255 255 / 0));
|
|
156
|
+
opacity: 0;
|
|
157
|
+
transition: opacity 0.3s;
|
|
158
|
+
pointer-events: none;
|
|
159
|
+
}
|
|
160
|
+
.pressing > [data-glass] > .lg-glow {
|
|
161
|
+
opacity: 0.45;
|
|
162
|
+
transition-duration: 0.12s;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* ---------- Tab bar ----------
|
|
166
|
+
A platter of regular glass. Position it yourself (fixed to the bottom of the screen, say);
|
|
167
|
+
it only needs to be positioned, which it is by default. */
|
|
168
|
+
.lg-tabs {
|
|
169
|
+
position: relative;
|
|
170
|
+
isolation: isolate;
|
|
171
|
+
display: grid;
|
|
172
|
+
grid-auto-columns: 1fr;
|
|
173
|
+
grid-auto-flow: column;
|
|
174
|
+
padding: 4px;
|
|
175
|
+
color: var(--lg-label);
|
|
176
|
+
touch-action: pan-y;
|
|
177
|
+
user-select: none;
|
|
178
|
+
-webkit-user-select: none;
|
|
179
|
+
}
|
|
180
|
+
.lg-tabs::before {
|
|
181
|
+
content: "";
|
|
182
|
+
position: absolute;
|
|
183
|
+
inset: 0;
|
|
184
|
+
z-index: -1;
|
|
185
|
+
border-radius: 999px;
|
|
186
|
+
background: var(--lg-platter);
|
|
187
|
+
backdrop-filter: blur(14px) saturate(1.6);
|
|
188
|
+
box-shadow:
|
|
189
|
+
inset 0 0.5px 0 rgb(255 255 255 / 0.22),
|
|
190
|
+
inset 0 0 0 0.5px rgb(255 255 255 / 0.1),
|
|
191
|
+
0 0 0 0.5px rgb(0 0 0 / 0.06),
|
|
192
|
+
0 10px 30px rgb(0 0 0 / 0.25);
|
|
193
|
+
transition: scale 0.3s cubic-bezier(0.3, 1.25, 0.5, 1);
|
|
194
|
+
}
|
|
195
|
+
/* Pressed, the bar swells 3.55%, as UIKit's does */
|
|
196
|
+
.lg-tabs.pressing::before {
|
|
197
|
+
scale: 1.0355;
|
|
198
|
+
}
|
|
199
|
+
.lg-tabs > :is(a, button) {
|
|
200
|
+
display: grid;
|
|
201
|
+
justify-items: center;
|
|
202
|
+
gap: 1px;
|
|
203
|
+
padding: 7px 0 6px;
|
|
204
|
+
border: 0;
|
|
205
|
+
border-radius: 999px;
|
|
206
|
+
background: none;
|
|
207
|
+
color: inherit;
|
|
208
|
+
font: inherit;
|
|
209
|
+
font-size: 10px;
|
|
210
|
+
font-weight: 600;
|
|
211
|
+
letter-spacing: 0.01em;
|
|
212
|
+
text-decoration: none;
|
|
213
|
+
cursor: pointer;
|
|
214
|
+
-webkit-tap-highlight-color: transparent;
|
|
215
|
+
}
|
|
216
|
+
.lg-tabs > :is(a, button):focus-visible {
|
|
217
|
+
outline: 2px solid var(--lg-accent);
|
|
218
|
+
outline-offset: -2px;
|
|
219
|
+
}
|
|
220
|
+
.lg-tabs svg {
|
|
221
|
+
width: 25px;
|
|
222
|
+
height: 25px;
|
|
223
|
+
fill: none;
|
|
224
|
+
stroke: currentColor;
|
|
225
|
+
stroke-width: 1.7;
|
|
226
|
+
stroke-linecap: round;
|
|
227
|
+
stroke-linejoin: round;
|
|
228
|
+
}
|
|
229
|
+
.lg-tabs [aria-current] {
|
|
230
|
+
color: var(--lg-accent);
|
|
231
|
+
}
|
|
232
|
+
.lg-tabs [aria-current] svg {
|
|
233
|
+
fill: color-mix(in srgb, var(--lg-accent), transparent 80%);
|
|
234
|
+
}
|
|
235
|
+
/* UIKit's resting selection: a pill behind the current tab, hidden under the lens */
|
|
236
|
+
.lg-tabs .lg-pill {
|
|
237
|
+
position: absolute;
|
|
238
|
+
z-index: -1;
|
|
239
|
+
border-radius: 999px;
|
|
240
|
+
background: var(--lg-pill);
|
|
241
|
+
pointer-events: none;
|
|
242
|
+
transition:
|
|
243
|
+
opacity 0.2s,
|
|
244
|
+
left 0.35s cubic-bezier(0.3, 1.2, 0.5, 1),
|
|
245
|
+
width 0.35s cubic-bezier(0.3, 1.2, 0.5, 1);
|
|
246
|
+
}
|
|
247
|
+
.lg-tabs .lg-pill[hidden],
|
|
248
|
+
.lg-tabs.lensing > .lg-pill {
|
|
249
|
+
display: block;
|
|
250
|
+
opacity: 0;
|
|
251
|
+
}
|
|
252
|
+
.lg-tabs .lg-glowbox {
|
|
253
|
+
position: absolute;
|
|
254
|
+
inset: 0;
|
|
255
|
+
z-index: -1;
|
|
256
|
+
overflow: hidden;
|
|
257
|
+
border-radius: 999px;
|
|
258
|
+
pointer-events: none;
|
|
259
|
+
}
|
|
260
|
+
.lg-tabs .lg-glow {
|
|
261
|
+
position: absolute;
|
|
262
|
+
width: 72px;
|
|
263
|
+
height: 72px;
|
|
264
|
+
margin: -36px 0 0 -36px;
|
|
265
|
+
border-radius: 50%;
|
|
266
|
+
background: radial-gradient(closest-side, rgb(255 255 255 / 0.5), rgb(255 255 255 / 0));
|
|
267
|
+
opacity: 0;
|
|
268
|
+
transition: opacity 0.25s;
|
|
269
|
+
}
|
|
270
|
+
.lg-tabs.pressing .lg-glow {
|
|
271
|
+
opacity: 0.6;
|
|
272
|
+
}
|
|
273
|
+
.lg-tabs [data-glass] {
|
|
274
|
+
background: var(--lg-surface);
|
|
275
|
+
box-shadow: var(--lg-lens-shadow) !important;
|
|
276
|
+
}
|
|
277
|
+
/* Its copy of the bar, under the lens: in place, the platter solid (the copy can't frost),
|
|
278
|
+
every tab in the accent, no pill or glow */
|
|
279
|
+
.lg-tabs[data-glass-copy] {
|
|
280
|
+
translate: none;
|
|
281
|
+
}
|
|
282
|
+
.lg-tabs[data-glass-copy]::before {
|
|
283
|
+
background: var(--lg-platter-solid);
|
|
284
|
+
box-shadow: 0 0 0 80px var(--lg-surface);
|
|
285
|
+
}
|
|
286
|
+
.lg-tabs[data-glass-copy] > :is(a, button) {
|
|
287
|
+
color: var(--lg-accent);
|
|
288
|
+
}
|
|
289
|
+
.lg-tabs[data-glass-copy] .lg-pill,
|
|
290
|
+
.lg-tabs[data-glass-copy] .lg-glowbox {
|
|
291
|
+
display: none;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/* ---------- Button ----------
|
|
295
|
+
Clear glass over whatever is behind it, lighting up in --tint (the accent by default).
|
|
296
|
+
Its lens lives in the button's parent, which should be positioned. */
|
|
297
|
+
.lg-button {
|
|
298
|
+
--s: 1;
|
|
299
|
+
--lit: 0;
|
|
300
|
+
--tint: var(--lg-accent);
|
|
301
|
+
position: relative;
|
|
302
|
+
z-index: 3;
|
|
303
|
+
isolation: isolate;
|
|
304
|
+
display: inline-flex;
|
|
305
|
+
align-items: center;
|
|
306
|
+
justify-content: center;
|
|
307
|
+
gap: 6px;
|
|
308
|
+
min-width: 48px;
|
|
309
|
+
height: 48px;
|
|
310
|
+
padding: 0 16px;
|
|
311
|
+
border: 0;
|
|
312
|
+
border-radius: 999px;
|
|
313
|
+
background: none;
|
|
314
|
+
/* The label turns white as the tint fills in */
|
|
315
|
+
color: color-mix(in srgb, var(--lg-label), #fff calc(var(--lit) * 100%));
|
|
316
|
+
font: inherit;
|
|
317
|
+
font-size: 16px;
|
|
318
|
+
font-weight: 600;
|
|
319
|
+
letter-spacing: -0.01em;
|
|
320
|
+
cursor: pointer;
|
|
321
|
+
touch-action: manipulation;
|
|
322
|
+
-webkit-tap-highlight-color: transparent;
|
|
323
|
+
}
|
|
324
|
+
.lg-button:focus-visible {
|
|
325
|
+
outline: none;
|
|
326
|
+
}
|
|
327
|
+
/* The content grows with the lens, as one piece */
|
|
328
|
+
.lg-content {
|
|
329
|
+
display: flex;
|
|
330
|
+
align-items: center;
|
|
331
|
+
gap: 6px;
|
|
332
|
+
scale: var(--s);
|
|
333
|
+
}
|
|
334
|
+
.lg-button svg {
|
|
335
|
+
width: 22px;
|
|
336
|
+
height: 22px;
|
|
337
|
+
fill: none;
|
|
338
|
+
stroke: currentColor;
|
|
339
|
+
stroke-width: 2.4;
|
|
340
|
+
stroke-linecap: round;
|
|
341
|
+
stroke-linejoin: round;
|
|
342
|
+
}
|
|
343
|
+
/* The tint (lighting up as the lens grows, a little lighter below, catching the light on its
|
|
344
|
+
rim) and the glow, together clipped to the button */
|
|
345
|
+
.lg-button .lg-clip {
|
|
346
|
+
position: absolute;
|
|
347
|
+
inset: 0;
|
|
348
|
+
z-index: -1;
|
|
349
|
+
overflow: hidden;
|
|
350
|
+
border-radius: inherit;
|
|
351
|
+
scale: var(--s);
|
|
352
|
+
pointer-events: none;
|
|
353
|
+
}
|
|
354
|
+
.lg-button .lg-clip::before {
|
|
355
|
+
content: "";
|
|
356
|
+
position: absolute;
|
|
357
|
+
inset: 0;
|
|
358
|
+
border-radius: inherit;
|
|
359
|
+
background: linear-gradient(to bottom, var(--tint), color-mix(in srgb, var(--tint), #fff 14%));
|
|
360
|
+
box-shadow:
|
|
361
|
+
inset 1.5px 1.5px 1px -0.5px rgb(255 255 255 / 0.55),
|
|
362
|
+
inset -1px -1px 1px -0.5px rgb(255 255 255 / 0.3);
|
|
363
|
+
opacity: calc(var(--lit) * var(--lit-max, 1));
|
|
364
|
+
transition: filter 0.2s;
|
|
365
|
+
}
|
|
366
|
+
.lg-button.pressing .lg-clip::before {
|
|
367
|
+
filter: brightness(1.1);
|
|
368
|
+
}
|
|
369
|
+
.lg-button .lg-glow {
|
|
370
|
+
position: absolute;
|
|
371
|
+
width: 56px;
|
|
372
|
+
height: 56px;
|
|
373
|
+
margin: -28px 0 0 -28px;
|
|
374
|
+
border-radius: 50%;
|
|
375
|
+
background: radial-gradient(closest-side, rgb(255 255 255 / 0.7), rgb(255 255 255 / 0));
|
|
376
|
+
opacity: 0;
|
|
377
|
+
transition: opacity 0.3s;
|
|
378
|
+
}
|
|
379
|
+
.lg-button.hovering .lg-glow {
|
|
380
|
+
opacity: 0.2;
|
|
381
|
+
}
|
|
382
|
+
.lg-button.pressing .lg-glow {
|
|
383
|
+
opacity: 0.45;
|
|
384
|
+
transition-duration: 0.12s;
|
|
385
|
+
}
|
|
386
|
+
/* The button's lens, in its parent */
|
|
387
|
+
.lg-button-lens {
|
|
388
|
+
background: var(--lg-surface);
|
|
389
|
+
box-shadow: var(--lg-button-shadow) !important;
|
|
390
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
var LiquidGlass=(function(){"use strict";const kt="http://www.w3.org/2000/svg",Lt=[.2126,.7152,.0722],Q=.94,Ct=1-Math.sqrt(1-Q*Q),ht={stiffness:440,damping:42},Et=`
|
|
2
|
+
[data-glass-copy], [data-glass-copy] *,
|
|
3
|
+
[data-glass-copy]::before, [data-glass-copy]::after,
|
|
4
|
+
[data-glass-copy] *::before, [data-glass-copy] *::after {
|
|
5
|
+
backdrop-filter: none !important;
|
|
6
|
+
-webkit-backdrop-filter: none !important;
|
|
7
|
+
will-change: auto !important;
|
|
8
|
+
animation: none !important;
|
|
9
|
+
transition: none !important;
|
|
10
|
+
}`;let St=0;class z{constructor(t,{stiffness:s=260,damping:e=26}={},i=null){this.wake=i,this.value=t,this.target=t,this.velocity=0,this.stiffness=s,this.damping=e}get target(){return this._target}set target(t){this._target=t,this.wake?.()}step(t){const s=(this.target-this.value)*this.stiffness-this.velocity*this.damping;this.velocity+=s*t,this.value+=this.velocity*t}snap(t){this.value=this.target=t,this.velocity=0}get settled(){return Math.abs(this.target-this.value)<.01&&Math.abs(this.velocity)<.01}}function at(l,t,s,e,i){const n=Math.abs(l)-s+i,r=Math.abs(t)-e+i;return Math.hypot(Math.max(n,0),Math.max(r,0))+Math.min(Math.max(n,r),0)-i}function Z(l,t){const s=[];for(let e=t;e&&e!==l;e=e.parentElement)s.unshift([...e.parentElement.children].indexOf(e));return s}function tt(l,t){let s=l;for(const e of t)s=s?.children[e];return s}const lt=l=>(l.nodeType===1?l:l.parentElement)?.closest("[data-glass]"),Rt=l=>(l.nodeType===1?l:l.parentElement)?.closest("[data-glass-skip]");class E{static presets={clear:{refraction:40,frost:2,chroma:.4,zoom:1.08,bevel:35,specular:.35,fresnel:.3,brightness:0,saturation:0,tint:"",rim:1},frosted:{refraction:24,frost:14,chroma:.15,zoom:1,bevel:30,specular:.5,fresnel:.5,brightness:.08,saturation:.3,tint:"rgb(255 255 255 / 0.08)",rim:1},dark:{refraction:32,frost:6,chroma:.2,zoom:1.04,bevel:35,specular:.3,fresnel:.2,brightness:-.18,saturation:-.2,tint:"rgb(0 0 0 / 0.25)",rim:.6},prism:{refraction:80,frost:0,chroma:1,zoom:1.12,bevel:50,specular:.25,fresnel:.4,brightness:.02,saturation:.5,tint:"",rim:.8},dome:{refraction:50,frost:0,chroma:.2,zoom:1.35,bevel:160,specular:.7,fresnel:.6,brightness:0,saturation:.1,tint:"",rim:1}};constructor(t,{anchor:s=null,container:e=null,button:i=!1,throttle:n=0,...r}={}){this.source=t,this.anchor=s,this.container=s?null:e,this.button=!!(i&&s),this.throttle=n,this.syncedAt=-1/0,this.params={width:280,height:180,radius:64,...E.presets.clear,...r},this.reducedMotion=matchMedia("(prefers-reduced-motion: reduce)").matches;const h=()=>this.#s();this.x=new z(innerWidth/2,void 0,h),this.y=new z(innerHeight/2,void 0,h),this.scale=new z(1,{stiffness:320,damping:18},h),this.stretchX=new z(1,{stiffness:180,damping:14}),this.stretchY=new z(1,{stiffness:180,damping:14}),this.springs=[this.x,this.y,this.scale,this.stretchX,this.stretchY],this.hovered=new z(0,ht,h),this.pressed=new z(0,ht,h),this.scrolled=new Set,this.popScale=1,this.#p(),this.#d(),this.shapeDirty=!0,this.copyDirty=!0,this.placeDirty=!0,this.#s()}#t=!1;#i=0;#n=new AbortController;#r=[];#a=[];#s(){this.#t||this.destroyed||(this.#t=!0,this.el&&!this.anchor&&(this.el.style.willChange="transform"),this.last=performance.now(),this.#i=requestAnimationFrame(t=>this.#c(t)))}#o(t,s,e){this.#a.push([t,s,t.style[s]]),t.style[s]=e}get#e(){return!!(this.anchor||this.visible||this.pop)}#p(){const t=`liquid-glass-${++St}`;if(this.svg=document.createElementNS(kt,"svg"),this.svg.setAttribute("aria-hidden","true"),this.svg.style.cssText="position:absolute;width:0;height:0;overflow:hidden",this.svg.innerHTML=`<style>${Et}</style><filter id="${t}" x="0" y="0" width="100%" height="100%" color-interpolation-filters="sRGB" />`,this.filter=this.svg.querySelector("filter"),this.el=document.createElement("div"),this.el.dataset.glass="",this.el.setAttribute("aria-hidden","true"),this.el.style.cssText="pointer-events:none;overflow:hidden;box-shadow:0 12px 34px -6px rgb(0 0 0 / 0.35)",this.anchor)getComputedStyle(this.anchor).position==="static"&&this.#o(this.anchor,"position","relative"),this.#o(this.anchor,"isolation","isolate"),this.el.style.cssText+=";position:absolute;inset:0;z-index:-1;border-radius:inherit";else{const e=this.container?"absolute;z-index:1":"fixed;z-index:2147483000";this.el.style.cssText+=`;position:${e};left:0;top:0;opacity:0;transition:opacity .3s`,this.container&&getComputedStyle(this.container).position==="static"&&this.#o(this.container,"position","relative")}this.frost=document.createElement("div"),this.frost.style.cssText="position:absolute",this.view=document.createElement("div"),this.view.style.cssText=`position:absolute;inset:0;overflow:hidden;filter:url(#${t})`,this.copyHost=document.createElement("div"),this.copyHost.inert=!0,this.copyHost.style.cssText="position:absolute;left:0;top:0",this.rim=document.createElement("canvas"),this.rim.style.cssText="position:absolute;inset:0;width:100%;height:100%",this.view.append(this.copyHost),this.frost.append(this.view),this.el.append(this.frost,this.rim),document.body.append(this.svg),(this.anchor??this.container??document.body).prepend(this.el);const{signal:s}=this.#n;if(this.anchor){const e=new ResizeObserver(()=>{this.set({width:this.anchor.offsetWidth,height:this.anchor.offsetHeight,radius:parseFloat(getComputedStyle(this.anchor).borderTopLeftRadius)||0})});e.observe(this.anchor),this.#r.push(e)}if(this.button){const e=this.anchor,i=()=>this.pressed.target=0;e.addEventListener("pointerenter",()=>this.hovered.target=1,{signal:s}),e.addEventListener("pointerleave",()=>{this.hovered.target=0,i()},{signal:s}),e.addEventListener("pointerdown",()=>this.pressed.target=1,{signal:s}),e.addEventListener("pointerup",i,{signal:s}),e.addEventListener("pointercancel",i,{signal:s}),e.addEventListener("keydown",n=>{(n.key===" "||n.key==="Enter")&&(this.pressed.target=1)},{signal:s}),e.addEventListener("keyup",i,{signal:s}),e.addEventListener("blur",i,{signal:s})}}#d(){const{signal:t}=this.#n,s=()=>{this.copyDirty=!0,this.#e&&this.#s()},e=new MutationObserver(i=>{const n=r=>!lt(r.target)&&!Rt(r.target);i.some(n)&&s()});e.observe(this.source,{subtree:!0,childList:!0,attributes:!0,characterData:!0}),this.#r.push(e),this.source.addEventListener("input",s,{capture:!0,signal:t}),this.source.addEventListener("scroll",i=>{lt(i.target)||(this.scrolled.add(i.target),s())},{capture:!0,signal:t}),addEventListener("resize",s,{signal:t}),addEventListener("scroll",()=>{this.#e&&(this.rectDirty=!0,this.#s())},{passive:!0,signal:t})}#f(){const t=this.source.cloneNode(!0);t.dataset.glassCopy="";const s="input, textarea, select, canvas",e=t.querySelectorAll(s);this.source.querySelectorAll(s).forEach((r,h)=>{const a=e[h];"value"in r&&r.type!=="file"&&(a.value=r.value),"checked"in r&&(a.checked=r.checked),r instanceof HTMLCanvasElement&&a.getContext("2d")?.drawImage(r,0,0)});const i=[];for(const r of this.scrolled)this.source.contains(r)?i.push([tt(t,Z(this.source,r)),r.scrollLeft,r.scrollTop]):this.scrolled.delete(r);this.anchor&&this.source.contains(this.anchor)?tt(t,Z(this.source,this.anchor))?.remove():this.source.contains(this.el)&&tt(t,Z(this.source,this.el))?.remove();for(const r of t.querySelectorAll("[data-glass]"))for(const h of r.querySelectorAll("[data-glass]"))h.remove();t.removeAttribute("id");for(const r of t.querySelectorAll("[id]"))r.removeAttribute("id");const n=this.source.getBoundingClientRect();this.sourceRect=n,this.containerRect=this.container?.getBoundingClientRect(),Object.assign(t.style,{position:"absolute",inset:"auto",left:"0",top:"0",margin:"0",transform:"none",translate:"none",rotate:"none",scale:"none",width:`${n.width}px`,height:`${n.height}px`}),this.copyHost.replaceChildren(t);for(const[r,h,a]of i)r?.scrollTo(h,a);this.copyDirty=!1}#u(){const{width:t,height:s,radius:e,bevel:i,refraction:n,frost:r,chroma:h,zoom:a,specular:o,fresnel:c,tint:u,rim:x}=this.params,m=this.params.body??1,p=Math.min(devicePixelRatio||1,2),g=t/2,b=s/2,G=Math.min(e,g,b),O=Math.max(Math.min(i,g,b),1),q=(d,f)=>at(d,f,g,b,G),it=(d,f,v)=>{const y=q(d+v,f)-q(d-v,f),R=q(d,f+v)-q(d,f-v),A=Math.hypot(y,R)||1;return[y/A,R/A]},w=a,F=g/w,B=b/w,mt=O/w,Y=n/w,X=(d,f)=>at(d,f,F,B,G/w),S=p*Math.max(w,1),M=Math.ceil(r*1.5/w);this.inner={x:F+M,y:B+M};const nt=Y*2+Math.sign(Y||1),rt=(this.params.falloff??0)/w,$=document.createElement("canvas");$.width=Math.ceil((F+M)*2*S),$.height=Math.ceil((B+M)*2*S);const yt=$.getContext("2d"),U=yt.createImageData($.width,$.height);for(let d=0;d<$.height;d++)for(let f=0;f<$.width;f++){const v=(f+.5)/S-F-M,y=(d+.5)/S-B-M;let R=0,A=0;const L=X(v,y),I=rt>0;if(L<0&&(I?L>-rt*6:L>-mt)&&n!==0){let D;if(I)D=Math.exp(L/rt)*Y;else{const J=(1+L/mt)*Q;D=(1-Math.sqrt(1-J*J))/Ct*Y}const N=X(v+.5/S,y)-X(v-.5/S,y),j=X(v,y+.5/S)-X(v,y-.5/S),K=Math.hypot(N,j)||1;R=N/K*D,A=j/K*D}const C=(d*$.width+f)*4;U.data[C]=(.5-R/nt)*255,U.data[C+1]=(.5-A/nt)*255,U.data[C+3]=255}yt.putImageData(U,0,0);const k=this.rim;k.width=Math.ceil(t*p),k.height=Math.ceil(s*p);const bt=k.getContext("2d"),W=bt.createImageData(k.width,k.height),vt=(this.params.lightAngle??-123.5)*Math.PI/180,xt=Math.cos(vt),wt=Math.sin(vt),Mt=Math.sqrt(1-.5**2),At=[xt*Mt,wt*Mt,.5+1],Dt=Math.hypot(...At),[Ht,Ft,Bt]=At.map(d=>d/Dt);for(let d=0;d<k.height;d++){const f=(d+.5)/p-b,v=(1-(f+b)/s)*.05;for(let y=0;y<k.width;y++){const R=(y+.5)/p-g,A=q(R,f);if(A>0)continue;let L=(.06+v)*m;if(A>-O){const C=1+A/O,[D,N]=it(R,f,1/p),j=D*xt+N*wt,K=C**3*(.55*Math.max(j,0)+.22*Math.max(-j,0)),J=(1-Math.min(-A/1.5,1))*.35;L+=(K+J)*x;const $t=Math.sqrt(1-C*C),Gt=Math.max(D*C*Ht+N*C*Ft+$t*Bt,0)**48;L+=o*Gt*1.4+c*(1-$t)**3*.5}const I=(d*k.width+y)*4;W.data[I]=W.data[I+1]=W.data[I+2]=255,W.data[I+3]=Math.min(L,1)*255}}bt.putImageData(W,0,0),k.style.background=u;const Pt=$.toDataURL(),ot=h*.35,V=(d,f)=>`<feDisplacementMap in="SourceGraphic" in2="map" scale="${nt*d}" xChannelSelector="R" yChannelSelector="G" result="${f}" />`;this.filterBase=`<feImage href="${Pt}" result="map" preserveAspectRatio="none" />`+(ot>.001?V(1+ot,"dr")+V(1,"dg")+V(1-ot,"db")+`<feColorMatrix in="dr" values="1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" result="r" />
|
|
11
|
+
<feColorMatrix in="dg" values="0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0" result="g" />
|
|
12
|
+
<feColorMatrix in="db" values="0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0" result="b" />
|
|
13
|
+
<feComposite in="r" in2="g" operator="arithmetic" k2="1" k3="1" result="rg" />
|
|
14
|
+
<feComposite in="rg" in2="b" operator="arithmetic" k2="1" k3="1" />`:V(1,"out")),this.#l(),Object.assign(this.frost.style,{left:`${g-F-M}px`,top:`${b-B-M}px`,width:`${(F+M)*2}px`,height:`${(B+M)*2}px`,transform:w===1?"":`scale(${w})`}),this.#h(),this.anchor||(this.el.style.width=`${t}px`,this.el.style.height=`${s}px`,this.el.style.borderRadius=`${G}px`),this.shapeDirty=!1}#g(){const{brightness:t,saturation:s}=this.params;if(!t&&!s)return"";const e=1+s;return`<feColorMatrix type="matrix" values="${[0,1,2].map(n=>[...Lt.map((r,h)=>r*(1-e)+(n===h?e:0)),0,t].map(r=>+r.toFixed(4)).join(" ")).join(" ")} 0 0 0 1 0" />`}#l(){this.filter.innerHTML=this.filterBase+this.#g(),this.displacers=[...this.filter.querySelectorAll("feDisplacementMap")].map(t=>[t,Number(t.getAttribute("scale"))])}#h(){const{frost:t}=this.params,s=t>0?[`blur(${t/2/this.params.zoom}px)`]:[];if(this.button){const e=this.hovered.value,i=this.pressed.value;for(const[n,r]of this.displacers)n.setAttribute("scale",r*(1-.6*i));s.push(`brightness(${1+.12*e-.04*i})`),this.el.style.boxShadow=`0 ${12-8*i}px ${34-18*i}px -6px rgb(0 0 0 / ${.35+.15*i})`}this.frost.style.filter=s.join(" ")||"none"}#m(){const{width:t,height:s}=this.params,e=this.sourceRect,i=devicePixelRatio||1,n=a=>Math.round(a*i)/i;let r,h;if(this.anchor)({left:r,top:h}=this.anchor.getBoundingClientRect());else{const a=this.scale.value*this.popScale;r=n(this.x.value-t/2),h=n(this.y.value-s/2),this.el.style.transform=`translate(${r}px, ${h}px) scale(${a*this.stretchX.value}, ${a*this.stretchY.value})`,this.container&&(r+=this.containerRect.left,h+=this.containerRect.top)}this.copyHost.style.left=`${n(this.inner.x+e.left-r-t/2)}px`,this.copyHost.style.top=`${n(this.inner.y+e.top-h-s/2)}px`}moveTo(t,s){if(this.container){const{left:e,top:i}=this.container.getBoundingClientRect();t-=e,s-=i}this.reducedMotion?(this.x.snap(t),this.y.snap(s)):(this.x.target=t,this.y.target=s),this.show(!0,{pop:!0})}press(t){this.scale.target=t?1.12:1,this.reducedMotion&&this.scale.snap(this.scale.target)}set(t){const s=Object.keys(t).every(e=>e==="brightness"||e==="saturation");Object.assign(this.params,t),s&&!this.shapeDirty&&this.filterBase?(this.#l(),this.#h()):(this.shapeDirty=!0,this.#s())}destroy(){if(!this.destroyed){this.destroyed=!0,cancelAnimationFrame(this.#i),this.#t=!1,this.#n.abort(),this.#r.forEach(t=>t.disconnect()),this.el.remove(),this.svg.remove();for(const[t,s,e]of this.#a.reverse())t.style[s]=e;this.scrolled.clear()}}show(t,{pop:s=!1}={}){if(t!==this.visible){if(this.visible=t,t&&(this.rectDirty=!0),this.#s(),this.el.getAnimations().forEach(e=>e.cancel()),!s||this.reducedMotion){this.pop=null,this.popScale=1,this.el.style.transition="opacity .3s",this.el.style.opacity=t?"1":"0";return}this.el.style.transition="none",this.pop={visible:t,start:performance.now(),burst:!t}}}#y(t){const{visible:s,start:e}=this.pop,i=t-e;let n,r;if(s){const h=Math.min(i/460,1);n=.6+.4*(1+2.2*(h-1)**3+1.2*(h-1)**2),r=Math.min(i/140,1)}else{const h=Math.min(i/110,1);n=1+.14*h*h,r=i<110?1:0,i>=110&&this.pop.burst&&(this.pop.burst=!1,this.#b())}this.popScale=n,this.el.style.opacity=String(r),i>=(s?460:110)&&(this.pop=null,this.popScale=1)}#b(){const t=this.el.getBoundingClientRect();if(!t.width)return;const s=t.left+t.width/2,e=t.top+t.height/2,i=this.container??document.body,n=this.container?this.container.getBoundingClientRect():{left:0,top:0},r=this.container?"absolute":"fixed",h=this.container?2:2147483e3,a=(c,u,x)=>{const m=document.createElement("div");m.setAttribute("aria-hidden","true"),m.style.cssText=`position:${r};pointer-events:none;z-index:${h};${c}`,i.append(m),m.animate(u,{fill:"forwards",...x}).finished.then(()=>m.remove())};a(`left:${t.left-n.left}px;top:${t.top-n.top}px;width:${t.width}px;height:${t.height}px;border-radius:${this.el.style.borderRadius};box-shadow:inset 0 0 0 1px rgb(255 255 255 / .8)`,[{opacity:.8,transform:"scale(1)"},{opacity:0,transform:"scale(1.18)"}],{duration:200,easing:"cubic-bezier(.2,.7,.3,1)"});const o=22;for(let c=0;c<o;c++){const u=c/o*Math.PI*2+(Math.random()-.5)*.5,x=Math.cos(u),m=Math.sin(u),p=3+Math.random()*4,g=18+Math.random()*46,b=14+Math.random()*22,G=s+x*t.width/2-n.left-p/2,O=e+m*t.height/2-n.top-p/2,q=`translate(${x*g*.75}px, ${m*g*.75}px)`,it=`translate(${x*g}px, ${m*g+b}px) scale(.4)`;a(`left:${G}px;top:${O}px;width:${p}px;height:${p}px;border-radius:50%;background:radial-gradient(circle at 35% 35%, rgb(255 255 255 / .95), rgb(255 255 255 / .35) 65%, rgb(255 255 255 / .1))`,[{opacity:1,transform:"translate(0, 0)",easing:"cubic-bezier(.1,.8,.3,1)"},{opacity:.9,transform:q,offset:.35,easing:"cubic-bezier(.4,0,.8,.6)"},{opacity:0,transform:it}],{duration:520+Math.random()*260})}}#c(t){const s=Math.min((t-this.last)/1e3,.03333333333333333);this.last=t;let e=!1;if(!this.anchor){if(!this.reducedMotion){const a=Math.max(Math.abs(this.x.velocity)-150,0),o=Math.max(Math.abs(this.y.velocity)-150,0),c=this.params.stretch??1;this.stretchX.target=1+c*(Math.min(a/5e3,.22)-Math.min(o/9e3,.1)),this.stretchY.target=1+c*(Math.min(o/5e3,.22)-Math.min(a/9e3,.1))}e=this.springs.some(a=>!a.settled);for(const a of this.springs)a.step(s),a.settled&&a.snap(a.target);this.pop&&(this.#y(t),e=!0)}if(this.button&&(!this.hovered.settled||!this.pressed.settled)){for(const a of[this.hovered,this.pressed])this.reducedMotion?a.snap(a.target):a.step(s);this.#h()}const i=this.copyDirty&&this.#e&&t-this.syncedAt>=this.throttle,n=i||this.shapeDirty||this.rectDirty;i?(this.#f(),this.syncedAt=t):this.rectDirty&&(this.sourceRect=this.source.getBoundingClientRect(),this.containerRect=this.container?.getBoundingClientRect()),this.rectDirty=!1,this.shapeDirty&&this.params.width>=1&&this.params.height>=1&&this.#u(),this.inner&&this.sourceRect&&(e||n||this.placeDirty)&&this.#m(),this.placeDirty=e;const r=this.button&&(!this.hovered.settled||!this.pressed.settled),h=this.shapeDirty||this.rectDirty||this.placeDirty||this.copyDirty&&this.#e||!this.inner;e||r||h?this.#i=requestAnimationFrame(a=>this.#c(a)):(this.#t=!1,this.el.style.willChange="auto")}}const T=(l,t,s)=>Math.min(Math.max(l,t),s),ct={stiffness:632,damping:50.3},zt={stiffness:241,damping:26.4},Tt={stiffness:576,damping:36},pt=220,P=(l,t=zt)=>{l.scale.stiffness=ct.stiffness,l.scale.damping=ct.damping,l.x.stiffness=l.y.stiffness=t.stiffness,l.x.damping=l.y.damping=t.damping},dt=l=>Math.abs(l.x.value-l.x.target)<=.5&&Math.abs(l.x.velocity)<=30,H={frost:0,fresnel:.15,saturation:0,tint:"",rim:.45,body:0,falloff:0,lightAngle:-123.5},_={switch:{dark:{...H,refraction:25,bevel:6,chroma:.21,zoom:1.12,specular:.25,brightness:.03},light:{...H,refraction:20,bevel:5,chroma:.12,zoom:1.1,specular:.55,brightness:0}},tabs:{dark:{...H,refraction:28,bevel:5,falloff:4.5,chroma:.1,zoom:1.1,specular:.25,brightness:.03,lightAngle:-140},light:{...H,refraction:28,bevel:5,falloff:4.5,chroma:.1,zoom:1.1,specular:.55,brightness:0,lightAngle:-140}},button:{dark:{...H,frost:1,refraction:28,bevel:5,chroma:.12,zoom:1.08,specular:.25,brightness:.03,rim:.6,body:.12},light:{...H,frost:1,refraction:28,bevel:5,chroma:.12,zoom:1.08,specular:.55,brightness:0,rim:.6,body:.12}}},st=l=>(t,s,e)=>t.addEventListener(s,e,{signal:l}),et=l=>l.closest("[data-theme]")?.dataset.theme==="light"?"light":"dark";function qt(l){const t=document.createElement("span");return t.className="lg-glow",l.el.append(t),s=>{const e=l.el.getBoundingClientRect(),i=e.width/l.el.offsetWidth;t.style.left=`${(s.clientX-e.left)/i}px`,t.style.top=`${(s.clientY-e.top)/i}px`}}class ft{constructor(t,{grow:s=1.55,press:e=1.08,lean:i=4,look:n=_.switch[et(t)]}={}){this.root=t,this.grow=s,this.press=e,this.lean=i,this.look=n,this.thumb=t.querySelector(":scope > .thumb"),this.bar=t.querySelector(":scope > .refract"),this.options=[...t.querySelectorAll(":scope > [data-opt]")],this.isSwitch=t.getAttribute("role")==="switch",this.value=this.isSwitch?+(t.getAttribute("aria-checked")==="true"):Math.max(0,this.options.findIndex(r=>r.getAttribute("aria-checked")==="true")),this.up=!1,this.hovered=!1,this.pressed=!1,this.drag=null,this.frame=0,this.abort=new AbortController,this.lens=new E(this.bar,{container:t,...n}),P(this.lens),this.glowAt=qt(this.lens),t.classList.add("glassy"),this.size(),this.lens.scale.snap(1/(s*e)),this.put(this.centre(this.value)),this.#t()}centre(t){const s=this.options[t];return s.offsetLeft+s.offsetWidth/2}put(t){this.at=t,this.thumb.style.translate=`${t-this.thumb.offsetLeft-this.thumb.offsetWidth/2}px`,this.root.style.setProperty("--p",T((t-this.centre(0))/(this.centre(1)-this.centre(0)),0,1))}size(){const t=this.thumb.offsetWidth,s=this.thumb.offsetHeight,e=s*this.grow,i=t*this.grow,n=Math.min(i,(i+t+e-s)/2);this.shift=(i-n)/2,this.lens.set({width:n*this.press,height:e*this.press,radius:e*this.press/2});const r=e-this.bar.offsetHeight*this.look.zoom,h=(n-r)/2/this.look.zoom,a=this.lensAt(this.centre(0))-h;this.bar.style.left=`${a}px`,this.bar.style.width=`${this.lensAt(this.centre(1))+h-a}px`}lensAt(t){const s=this.centre(0),e=this.centre(1)-s;return t+this.shift*(2*T((t-s)/e,0,1)-1)}thumbAt(t){const s=this.centre(0),e=2*this.shift/(this.centre(1)-s);return T((t+this.shift+e*s)/(1+e),s,this.centre(1))}select(t){t!==this.value&&(this.value=t,this.isSwitch?this.root.setAttribute("aria-checked",String(t===1)):this.options.forEach((s,e)=>s.setAttribute("aria-checked",String(t===e))),this.lift(),this.lens.x.target=this.lensAt(this.centre(t)),this.root.dispatchEvent(new CustomEvent("change",{detail:{value:t},bubbles:!0})))}lift(){const t=this.lens;this.up||(this.up=!0,t.x.snap(this.lensAt(this.at)),t.y.snap(this.thumb.offsetTop+this.thumb.offsetHeight/2),t.show(!0),t.el.style.transition="opacity 0.18s",this.root.classList.add("lifted")),t.scale.target=this.pressed?1:1/this.press,cancelAnimationFrame(this.frame);let s=0;const e=i=>{this.put(this.thumbAt(t.x.value)),this.drag||this.hovered||this.pressed||!dt(t)?s=0:s||(s=i),s&&i-s>pt?this.drop():this.frame=requestAnimationFrame(e)};this.frame=requestAnimationFrame(e)}drop(){this.up&&(this.up=!1,this.lens.scale.target=1/(this.grow*this.press),this.lens.show(!1),this.lens.el.style.transition="opacity 0.3s 0.08s",this.root.classList.remove("lifted"))}setPressed(t,s){this.pressed=t,this.root.classList.toggle("pressing",t),t&&this.glowAt(s),(t||this.up)&&this.lift()}destroy(){cancelAnimationFrame(this.frame),this.abort.abort(),this.lens.destroy(),this.root.classList.remove("glassy","lifted","pressing"),this.root.style.removeProperty("--p"),this.thumb.style.translate="",this.bar.style.left=this.bar.style.width=""}#t(){const t=this.root,s=st(this.abort.signal);let e=!1;s(t,"click",()=>{e?e=!1:this.select(1-this.value)}),s(t,"pointerenter",i=>{i.pointerType==="mouse"&&(this.hovered=!0,this.lift())}),s(t,"pointerleave",()=>{this.hovered=!1,this.drag?this.drag.moved||(this.drag=null,this.setPressed(!1)):this.lens.x.target=this.lensAt(this.centre(this.value))}),s(t,"pointerdown",i=>{i.button===0&&(this.drag={id:i.pointerId,x:i.clientX,from:this.at,moved:!1},this.setPressed(!0,i))}),s(t,"pointermove",i=>{const n=this.drag,r=this.lens;if(this.pressed&&this.glowAt(i),!n){if(!this.hovered)return;const a=this.centre(this.value),o=i.clientX-t.getBoundingClientRect().left-t.clientLeft,c=this.options[this.value].offsetWidth/2;r.x.target=this.lensAt(a)+T((o-a)/c,-1,1)*this.lean;return}const h=i.clientX-n.x;!n.moved&&Math.abs(h)>4&&(n.moved=!0,t.setPointerCapture(n.id)),n.moved&&(r.x.target=this.lensAt(T(n.from+h,this.centre(0),this.centre(1))))}),s(t,"pointerup",()=>{const i=this.drag;if(this.drag=null,this.setPressed(!1),!i?.moved)return;e=!0,setTimeout(()=>e=!1);const n=this.lens,h=this.thumbAt(n.x.value+n.x.velocity*.12)>(this.centre(0)+this.centre(1))/2?1:0;h===this.value?(this.lift(),n.x.target=this.lensAt(this.centre(h))):this.select(h)}),s(t,"pointercancel",()=>{this.drag=null,this.setPressed(!1),this.lens.x.target=this.lensAt(this.centre(this.value))}),s(window,"resize",()=>{this.size(),this.up||this.put(this.centre(this.value))})}}class ut{constructor(t,{grow:s=1.3,press:e=1.1,look:i=_.tabs[et(t)]}={}){this.root=t,this.grow=s,this.press=e,this.tabs=[...t.querySelectorAll(":scope > a, :scope > button")],this.pill=t.querySelector(":scope > .lg-pill"),this.glow=t.querySelector(".lg-glow"),this.pressedId=null,this.frame=0,this.abort=new AbortController,this.lens=new E(t,{container:t,...i}),P(this.lens),this.placePill(),this.#t()}get current(){return this.tabs.find(t=>t.hasAttribute("aria-current"))??null}centre(t){return t.offsetLeft+t.offsetWidth/2}choose(t){t!==this.current&&(this.tabs.forEach(s=>s.removeAttribute("aria-current")),t.setAttribute("aria-current","page"),this.placePill(),this.root.dispatchEvent(new CustomEvent("change",{detail:{tab:t},bubbles:!0})))}placePill(){const t=this.current;this.pill.hidden=!t,t&&Object.assign(this.pill.style,{left:`${t.offsetLeft}px`,top:`${t.offsetTop}px`,width:`${t.offsetWidth}px`,height:`${t.offsetHeight}px`})}lift(t){const s=this.lens;cancelAnimationFrame(this.frame);const e=(this.root.offsetHeight+8)*this.press;s.set({width:(t.offsetWidth+16)*this.press,height:e,radius:e/2});const i=this.centre(t),n=t.offsetTop+t.offsetHeight/2;s.visible||(s.x.snap(i),s.y.snap(n),s.scale.snap(1/(this.grow*this.press)),s.show(!0),s.el.style.transition="opacity 0.18s",this.root.classList.add("lensing")),this.bulge(),s.x.target=i,s.y.target=n}bulge(){this.lens.scale.target=this.pressedId===null?1/this.press:1}drop(){this.root.classList.remove("lensing"),this.lens.scale.target=1/(this.grow*this.press),this.lens.show(!1),this.lens.el.style.transition="opacity 0.3s 0.08s"}settle(t){const s=this.lens;cancelAnimationFrame(this.frame);let e=0;const i=n=>{dt(s)?e||(e=n):e=0,e&&n-e>t?this.drop():this.frame=requestAnimationFrame(i)};this.frame=requestAnimationFrame(i)}destroy(){cancelAnimationFrame(this.frame),this.abort.abort(),this.lens.destroy(),this.root.classList.remove("lensing","pressing")}#t(){const t=this.root,s=st(this.abort.signal),e=o=>o.clientX-t.getBoundingClientRect().left-t.clientLeft,i=o=>T(o,this.centre(this.tabs[0]),this.centre(this.tabs.at(-1))),n=o=>this.tabs.reduce((c,u)=>Math.abs(this.centre(u)-o)<Math.abs(this.centre(c)-o)?u:c),r=o=>{const c=t.getBoundingClientRect();this.glow.style.left=`${o.clientX-c.left}px`,this.glow.style.top=`${o.clientY-c.top}px`};this.tabs.forEach(o=>s(o,"click",()=>this.choose(o))),s(t,"pointerover",o=>{const c=o.target.closest("a, button");o.pointerType==="mouse"&&this.pressedId===null&&c&&this.lift(c)}),s(t,"pointerleave",o=>{o.pointerType==="mouse"&&this.pressedId===null&&this.settle(0)});let h=null;s(t,"pointerdown",o=>{if(o.button!==0)return;o.preventDefault(),t.setPointerCapture(o.pointerId),this.pressedId=o.pointerId;const c=o.target.closest("a, button")??n(e(o));h={x:o.clientX,tab:c,dragging:!1},this.lift(c),P(this.lens,Tt),r(o),t.classList.add("pressing")}),s(t,"pointermove",o=>{if(o.pointerId!==this.pressedId)return;r(o);const c=o.clientX-h.x;!h.dragging&&Math.abs(c)>6&&(h.dragging=!0),h.dragging&&(this.lens.x.target=i(this.centre(h.tab)+c))});const a=(o,c)=>{if(o.pointerId!==this.pressedId)return;if(this.pressedId=null,t.classList.remove("pressing"),P(this.lens),this.bulge(),!c)return this.settle(0);const u=h.dragging?n(this.lens.x.target):h.tab;this.lens.x.target=this.centre(u),u.click(),o.pointerType==="mouse"&&t.matches(":hover")||this.settle(pt)};s(t,"pointerup",o=>a(o,!0)),s(t,"pointercancel",o=>a(o,!1)),s(window,"resize",()=>this.placePill())}}class gt{constructor(t,{container:s=t.parentElement,source:e=s.querySelector(":scope > [data-glass-source]"),grow:i=1.1,press:n=1.08,lit:r=.95,hang:h=120,look:a=_.button[et(t)]}={}){if(!e)throw new Error("GlassButton: no source to refract. Give the button's parent a [data-glass-source] child, or pass { source }.");this.btn=t,this.container=s,this.grow=i,this.press=n,this.hang=h,this.hovered=!1,this.pressed=!1,this.up=!1,this.frame=0,this.abort=new AbortController;const o=st(this.abort.signal);t.style.setProperty("--lit-max",r);const c=document.createElement("span");c.className="lg-clip",c.setAttribute("aria-hidden","true");const u=document.createElement("span");u.className="lg-glow",c.append(u),t.prepend(c),this.clip=c,this.lens=new E(e,{container:s,...a}),this.lens.el.classList.add("lg-button-lens"),P(this.lens),this.size(),this.lens.scale.snap(1/this.full),this.lens.show(!0),this.track();const x=p=>{const g=t.getBoundingClientRect(),b=g.width/t.offsetWidth;u.style.left=`${(p.clientX-g.left)/b}px`,u.style.top=`${(p.clientY-g.top)/b}px`};o(t,"pointerenter",p=>{p.pointerType==="mouse"&&(this.hovered=!0,x(p),t.classList.add("hovering"),this.lift())}),o(t,"pointerleave",()=>{this.hovered=!1,t.classList.remove("hovering"),this.pressed=!1,this.lift(),this.settle()}),o(t,"pointerdown",p=>{p.button===0&&(this.pressed=!0,x(p),this.lift())}),o(t,"pointermove",p=>(this.pressed||this.hovered)&&x(p));const m=()=>{this.pressed&&(this.pressed=!1,this.lift(),this.settle())};o(t,"pointerup",m),o(t,"pointercancel",m),o(t,"focus",()=>t.matches(":focus-visible")&&this.lift()),o(t,"blur",()=>this.settle(0)),o(t,"keydown",p=>{p.key!==" "&&p.key!=="Enter"||(this.pressed=!0,this.lift())}),o(t,"keyup",()=>{this.pressed=!1,this.lift()}),o(window,"resize",()=>this.size())}destroy(){cancelAnimationFrame(this.frame),cancelAnimationFrame(this.settling),this.abort.abort(),this.lens.destroy(),this.clip.remove(),this.btn.classList.remove("hovering","pressing");for(const t of["--lit-max","--s","--lit"])this.btn.style.removeProperty(t)}get full(){return this.grow*this.press}size(){const t=this.btn.getBoundingClientRect(),s=this.container.getBoundingClientRect(),e=this.btn.offsetWidth*this.full,i=this.btn.offsetHeight*this.full;this.lens.set({width:e,height:i,radius:i/2}),this.lens.x.snap(t.left+t.width/2-s.left-this.container.clientLeft),this.lens.y.snap(t.top+t.height/2-s.top-this.container.clientTop)}lift(){cancelAnimationFrame(this.settling),this.up=!0,this.lens.scale.target=this.pressed?1:1/this.press,this.btn.classList.toggle("pressing",this.pressed),this.track()}settle(t=this.hang){if(!this.up||this.hovered||this.pressed||this.btn.matches(":focus-visible"))return;cancelAnimationFrame(this.settling);const s=performance.now(),e=i=>{this.hovered||this.pressed||(i-s<t?this.settling=requestAnimationFrame(e):(this.up=!1,this.lens.scale.target=1/this.full,this.track()))};this.settling=requestAnimationFrame(e)}track(){cancelAnimationFrame(this.frame);const t=()=>{const s=this.lens.scale.value*this.full;this.btn.style.setProperty("--s",s.toFixed(4)),this.btn.style.setProperty("--lit",T((s-1)/(this.grow-1||1),0,1).toFixed(4)),this.lens.scale.settled||(this.frame=requestAnimationFrame(t))};this.frame=requestAnimationFrame(t)}}function It(l=document){return[...[...l.querySelectorAll("[data-glass-switch]")].map(t=>new ft(t)),...[...l.querySelectorAll("[data-glass-tabs]")].map(t=>new ut(t)),...[...l.querySelectorAll("[data-glass-button]")].map(t=>new gt(t))]}return Object.assign(E,{LiquidGlass:E,GlassSwitch:ft,GlassTabBar:ut,GlassButton:gt,LOOKS:_,initComponents:It}),E})();
|