use-scroll-animate 2.4.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +2 -0
  3. package/README_ja.md +2 -0
  4. package/README_zh.md +2 -0
  5. package/dist/chunks/{base-CuvCgqLy.js → base-08OYzphB.js} +2 -2
  6. package/dist/chunks/{base-CuvCgqLy.js.map → base-08OYzphB.js.map} +1 -1
  7. package/dist/chunks/{base-CJ7XfidP.cjs → base-5DFCAnvH.cjs} +3 -1
  8. package/dist/chunks/{base-CJ7XfidP.cjs.map → base-5DFCAnvH.cjs.map} +1 -1
  9. package/dist/chunks/{spring-CGO9Jd9b.cjs → spring-Bi4qY0pL.cjs} +2 -2
  10. package/dist/chunks/{spring-CGO9Jd9b.cjs.map → spring-Bi4qY0pL.cjs.map} +1 -1
  11. package/dist/chunks/{spring-BKUU7-Xm.js → spring-CpeFkxNd.js} +2 -2
  12. package/dist/chunks/{spring-BKUU7-Xm.js.map → spring-CpeFkxNd.js.map} +1 -1
  13. package/dist/components/background.cjs +1 -1
  14. package/dist/components/background.js +2 -2
  15. package/dist/components/cards.cjs +2 -2
  16. package/dist/components/cards.js +3 -3
  17. package/dist/components/click.cjs +985 -0
  18. package/dist/components/click.cjs.map +1 -0
  19. package/dist/components/click.css +9 -0
  20. package/dist/components/click.d.cts +250 -0
  21. package/dist/components/click.d.ts +250 -0
  22. package/dist/components/click.js +967 -0
  23. package/dist/components/click.js.map +1 -0
  24. package/dist/components/feedback.cjs +2 -2
  25. package/dist/components/feedback.css +1 -1
  26. package/dist/components/feedback.js +3 -3
  27. package/dist/components/interaction.cjs +2 -2
  28. package/dist/components/interaction.css +1 -1
  29. package/dist/components/interaction.js +3 -3
  30. package/dist/components/physics.cjs +2 -2
  31. package/dist/components/physics.js +4 -4
  32. package/dist/components/reveal.cjs +1 -1
  33. package/dist/components/reveal.js +2 -2
  34. package/dist/components/text.cjs +1 -1
  35. package/dist/components/text.js +2 -2
  36. package/dist/components/transitions.cjs +1 -1
  37. package/dist/components/transitions.js +2 -2
  38. package/dist/components/ui.cjs +1082 -0
  39. package/dist/components/ui.cjs.map +1 -0
  40. package/dist/components/ui.css +14 -0
  41. package/dist/components/ui.d.cts +251 -0
  42. package/dist/components/ui.d.ts +251 -0
  43. package/dist/components/ui.js +1064 -0
  44. package/dist/components/ui.js.map +1 -0
  45. package/dist/components.cjs +44 -4
  46. package/dist/components.cjs.map +1 -1
  47. package/dist/components.css +21 -2
  48. package/dist/components.d.cts +427 -2
  49. package/dist/components.d.ts +427 -2
  50. package/dist/components.js +14 -3
  51. package/dist/components.js.map +1 -1
  52. package/dist/components.umd.js +2 -2
  53. package/dist/components.umd.js.map +1 -1
  54. package/docs/components.md +33 -0
  55. package/package.json +24 -2
@@ -0,0 +1,967 @@
1
+ import { p as prefersReducedMotion, d as defineElement, E as EASE_OUT, b as clamp, e as caf, n as now, r as raf } from '../chunks/base-08OYzphB.js';
2
+ export { a as configureComponents } from '../chunks/base-08OYzphB.js';
3
+ import { d as springEasing, c as createSpring } from '../chunks/spring-CpeFkxNd.js';
4
+
5
+ /**
6
+ * Click-effect helpers (v2.5): `burst()`, `confetti()`, `shake()`, `haptic()`.
7
+ * Particles live in one fixed, pointer-transparent layer and are removed when
8
+ * their animation ends. Under reduced motion particles are skipped and
9
+ * `shake()` only flashes an outline.
10
+ */
11
+ let layer = null;
12
+ function fxLayer() {
13
+ if (layer && layer.isConnected)
14
+ return layer;
15
+ layer = document.createElement('div');
16
+ layer.className = 'usa-fx-layer';
17
+ layer.setAttribute('aria-hidden', 'true');
18
+ layer.style.cssText = 'position:fixed;inset:0;pointer-events:none;z-index:2147483000;overflow:hidden;contain:strict';
19
+ document.body.appendChild(layer);
20
+ return layer;
21
+ }
22
+ const PALETTE = ['#7c5cff', '#22d3ee', '#f472b6', '#facc15', '#34d399', '#fb923c'];
23
+ const GLYPH = { star: '★', heart: '♥' };
24
+ function particle(x, y, size, color, shape) {
25
+ const p = document.createElement('span');
26
+ const glyph = GLYPH[shape] || (shape !== 'circle' && shape !== 'square' ? shape : '');
27
+ p.style.cssText = `position:absolute;left:${x}px;top:${y}px;width:${size}px;height:${size}px;margin:${-size / 2}px 0 0 ${-size / 2}px;will-change:transform,opacity;` +
28
+ (glyph ? `font-size:${size * 2}px;line-height:${size}px;text-align:center;color:${color}` : `background:${color};border-radius:${shape === 'square' ? '2px' : '50%'}`);
29
+ if (glyph)
30
+ p.textContent = glyph;
31
+ fxLayer().appendChild(p);
32
+ return p;
33
+ }
34
+ const done = (a, el) => {
35
+ if (a)
36
+ a.onfinish = () => el.remove();
37
+ else
38
+ el.remove();
39
+ };
40
+ /** Particles radiating from client point (x, y). Returns the number spawned. */
41
+ function burst(x, y, options = {}) {
42
+ if (typeof document === 'undefined' || prefersReducedMotion())
43
+ return 0;
44
+ const { count = 12, colors = PALETTE, distance = 48, size = 6, shape = 'circle', duration = 600 } = options;
45
+ for (let i = 0; i < count; i++) {
46
+ const angle = (i / count) * Math.PI * 2 + Math.random() * 0.4;
47
+ const d = distance * (0.7 + Math.random() * 0.5);
48
+ const p = particle(x, y, size, colors[i % colors.length], shape);
49
+ const a = typeof p.animate === 'function'
50
+ ? p.animate([
51
+ { transform: 'translate(0,0) scale(1)', opacity: 1 },
52
+ { transform: `translate(${Math.cos(angle) * d}px, ${Math.sin(angle) * d}px) scale(0.2)`, opacity: 0 },
53
+ ], { duration: duration * (0.8 + Math.random() * 0.4), easing: 'cubic-bezier(0.22, 1, 0.36, 1)', fill: 'forwards' })
54
+ : null;
55
+ done(a, p);
56
+ }
57
+ return count;
58
+ }
59
+ /** A confetti cannon (paper pieces with gravity, drift and spin). */
60
+ function confetti(options = {}) {
61
+ if (typeof document === 'undefined' || prefersReducedMotion())
62
+ return 0;
63
+ const W = window.innerWidth || 800;
64
+ const H = window.innerHeight || 600;
65
+ const { x = W / 2, y = H * 0.66, count = 80, spread = 70, velocity = 1, colors = PALETTE, duration = 1600 } = options;
66
+ for (let i = 0; i < count; i++) {
67
+ const angle = ((-90 + (Math.random() - 0.5) * spread) * Math.PI) / 180;
68
+ const speed = (260 + Math.random() * 320) * velocity;
69
+ const vx = Math.cos(angle) * speed;
70
+ const vy = Math.sin(angle) * speed;
71
+ const p = particle(x, y, 6 + Math.random() * 5, colors[i % colors.length], Math.random() > 0.5 ? 'square' : 'circle');
72
+ p.style.height = `${4 + Math.random() * 3}px`;
73
+ const frames = [];
74
+ const steps = 8;
75
+ const T = duration / 1000;
76
+ const spin = (Math.random() - 0.5) * 1440;
77
+ for (let s = 0; s <= steps; s++) {
78
+ const t = (s / steps) * T;
79
+ const g = 900;
80
+ frames.push({
81
+ transform: `translate(${(vx * t * 0.8).toFixed(1)}px, ${(vy * t + 0.5 * g * t * t).toFixed(1)}px) rotate(${((spin * s) / steps).toFixed(0)}deg) rotateX(${s * 120}deg)`,
82
+ opacity: s === steps ? 0 : 1,
83
+ });
84
+ }
85
+ const a = typeof p.animate === 'function' ? p.animate(frames, { duration: duration * (0.85 + Math.random() * 0.3), easing: 'linear', fill: 'forwards' }) : null;
86
+ done(a, p);
87
+ }
88
+ return count;
89
+ }
90
+ /** Horizontal error shake (`intensity` px, default 8). Reduced motion: a red outline flash. */
91
+ function shake(el, intensity = 8, duration = 480) {
92
+ const t = el;
93
+ if (typeof t.animate !== 'function')
94
+ return null;
95
+ if (prefersReducedMotion())
96
+ return t.animate([{ outline: '2px solid #e5484d' }, { outline: '2px solid transparent' }], { duration: 600 });
97
+ const k = intensity;
98
+ return t.animate([0, -k, k, -k * 0.75, k * 0.75, -k * 0.4, k * 0.4, 0].map((v) => ({ transform: `translateX(${v}px)` })), { duration, easing: 'ease-in-out' });
99
+ }
100
+ /** `navigator.vibrate()` where supported (Android Chrome, some WebViews). Returns whether it ran. */
101
+ function haptic(pattern = 10) {
102
+ try {
103
+ return typeof navigator !== 'undefined' && typeof navigator.vibrate === 'function' ? navigator.vibrate(pattern) : false;
104
+ }
105
+ catch {
106
+ return false;
107
+ }
108
+ }
109
+
110
+ var css$6 = "usa-click{position:relative;display:inline-block;overflow:hidden;isolation:isolate;border-radius:inherit;-webkit-tap-highlight-color:transparent;touch-action:manipulation}usa-click[block]{display:block}usa-click .usa-click-wave{position:absolute;border-radius:50%;pointer-events:none;z-index:-1;transform:scale(0);background:radial-gradient(circle,var(--usa-wave) 0 55%,color-mix(in srgb,var(--usa-wave) 40%,transparent) 70%,transparent 72%)}";
111
+
112
+ const CLICK_EFFECTS = ['ripple', 'burst', 'confetti', 'squish', 'press-spring', 'shake'];
113
+ function defineClick(tag = 'usa-click') {
114
+ return defineElement(tag, (Base) => class UsaClick extends Base {
115
+ constructor() {
116
+ super(...arguments);
117
+ this._press = null;
118
+ }
119
+ static get observedAttributes() {
120
+ return ['effect', 'disabled'];
121
+ }
122
+ get effects() {
123
+ return this.str('effect', 'ripple').split(/[\s,]+/).filter(Boolean);
124
+ }
125
+ mount() {
126
+ this.listen(this, 'pointerdown', (e) => {
127
+ if (this.flag('disabled') || (e.pointerType === 'mouse' && e.button !== 0))
128
+ return;
129
+ this.down();
130
+ if (this.effects.includes('ripple'))
131
+ this.ripple(e.clientX, e.clientY);
132
+ });
133
+ const up = () => this.up();
134
+ this.listen(this, 'pointerup', up);
135
+ this.listen(this, 'pointerleave', up);
136
+ this.listen(this, 'pointercancel', up);
137
+ this.listen(this, 'click', (e) => {
138
+ if (this.flag('disabled'))
139
+ return;
140
+ const kb = e.detail === 0;
141
+ const r = this.getBoundingClientRect();
142
+ const x = kb ? r.left + r.width / 2 : e.clientX;
143
+ const y = kb ? r.top + r.height / 2 : e.clientY;
144
+ if (kb && this.effects.includes('ripple'))
145
+ this.ripple(x, y);
146
+ this.play(x, y);
147
+ if (this.effects.includes('shake') && this.str('trigger') === 'click')
148
+ this.shake();
149
+ });
150
+ this.listen(this, 'keydown', (e) => {
151
+ if ((e.key === 'Enter' || e.key === ' ') && !e.repeat)
152
+ this.down();
153
+ });
154
+ this.listen(this, 'keyup', up);
155
+ if (this.effects.includes('shake'))
156
+ this.listen(this, 'invalid', () => this.shake(), { capture: true });
157
+ }
158
+ /** Particles + haptics at client (x, y) (default: centre). */
159
+ play(x, y) {
160
+ if (x === undefined || y === undefined) {
161
+ const r = this.getBoundingClientRect();
162
+ x = r.left + r.width / 2;
163
+ y = r.top + r.height / 2;
164
+ }
165
+ const fx = this.effects;
166
+ const colors = this.str('color') ? this.str('color').split(',') : undefined;
167
+ if (fx.includes('burst'))
168
+ burst(x, y, { count: this.num('count', 12), shape: this.str('shape', 'circle'), colors });
169
+ if (fx.includes('confetti'))
170
+ confetti({ x, y, count: this.num('count', 60), colors, spread: 90, velocity: 0.8 });
171
+ if (this.hasAttribute('haptic'))
172
+ haptic(this.num('haptic', 10));
173
+ this.emit('click-effect', { x, y, effects: fx });
174
+ }
175
+ shake() {
176
+ shake(this);
177
+ if (this.hasAttribute('haptic'))
178
+ haptic([30, 40, 30]);
179
+ }
180
+ ripple(x, y) {
181
+ if (this.reduced)
182
+ return;
183
+ const r = this.getBoundingClientRect();
184
+ const cx = x - r.left;
185
+ const cy = y - r.top;
186
+ const radius = Math.hypot(Math.max(cx, r.width - cx), Math.max(cy, r.height - cy));
187
+ const wave = document.createElement('span');
188
+ wave.className = 'usa-click-wave';
189
+ wave.setAttribute('aria-hidden', 'true');
190
+ wave.style.cssText = `width:${radius * 2}px;height:${radius * 2}px;left:${cx - radius}px;top:${cy - radius}px;--usa-wave:${this.str('color', 'currentColor').split(',')[0]}`;
191
+ this.append(wave);
192
+ const a = this.motion(wave, [{ transform: 'scale(0)', opacity: 0.35 }, { transform: 'scale(1)', opacity: 0.25, offset: 0.6 }, { transform: 'scale(1.05)', opacity: 0 }], {
193
+ duration: 650,
194
+ easing: EASE_OUT,
195
+ fill: 'forwards',
196
+ });
197
+ if (a)
198
+ a.onfinish = () => wave.remove();
199
+ else
200
+ wave.remove();
201
+ }
202
+ down() {
203
+ if (this.hasAttribute('data-pressed'))
204
+ return;
205
+ const fx = this.effects;
206
+ if (!fx.includes('squish') && !fx.includes('press-spring'))
207
+ return;
208
+ this.setAttribute('data-pressed', '');
209
+ this._press?.cancel();
210
+ const to = this.reduced ? { opacity: 0.75 } : fx.includes('squish') ? { transform: 'scale(1.08, 0.86)' } : { transform: 'scale(0.94)' };
211
+ const from = this.reduced ? { opacity: 1 } : { transform: 'none' };
212
+ this._press = this.motion(this, [from, to], {
213
+ duration: 110,
214
+ easing: 'ease-out',
215
+ fill: 'forwards',
216
+ });
217
+ }
218
+ up() {
219
+ if (!this.hasAttribute('data-pressed'))
220
+ return;
221
+ this.removeAttribute('data-pressed');
222
+ const fx = this.effects;
223
+ this._press?.cancel();
224
+ if (this.reduced) {
225
+ this._press = this.motion(this, [{ opacity: 0.75 }, { opacity: 1 }], { duration: 150 });
226
+ return;
227
+ }
228
+ const from = fx.includes('squish') ? 'scale(1.08, 0.86)' : 'scale(0.94)';
229
+ const frames = fx.includes('squish')
230
+ ? [{ transform: from }, { transform: 'scale(0.92, 1.1)', offset: 0.3 }, { transform: 'scale(1.03, 0.97)', offset: 0.6 }, { transform: 'none' }]
231
+ : [{ transform: from }, { transform: 'none' }];
232
+ this._press = this.motion(this, frames, fx.includes('squish') ? { duration: 520, easing: 'ease-out' } : springEasing('bouncy'));
233
+ }
234
+ }, { id: 'click', text: css$6 });
235
+ }
236
+
237
+ var css$5 = "usa-button{position:relative;display:inline-block;isolation:isolate;-webkit-tap-highlight-color:transparent;touch-action:manipulation;vertical-align:middle}usa-button[role=\"button\"]{cursor:pointer}usa-button .usa-button-face{position:relative;overflow:hidden;will-change:transform;transform-origin:50% 60%;white-space:nowrap}usa-button[deform~=\"gooey\"] .usa-button-face{overflow:visible}usa-button[deform~=\"dent\"] .usa-button-face::after{content:\"\";position:absolute;inset:0;border-radius:inherit;pointer-events:none;opacity:0;transition:opacity 0.2s ease;background:radial-gradient(circle at var(--usa-dent-x,50%) var(--usa-dent-y,50%),rgb(0 0 0 / 0.28),transparent 60%)}usa-button[deform~=\"dent\"][data-pressed] .usa-button-face::after{opacity:1}usa-button .usa-button-goo{position:absolute;inset:0;z-index:-1;pointer-events:none;filter:url(#usa-goo)}usa-button .usa-button-goo i{position:absolute;width:22px;height:22px;border-radius:50%;background:var(--usa-goo-bg);transform:translate(-50%,-50%) scale(0.4)}usa-button .usa-button-face[data-shape=\"circle\"],usa-button .usa-button-face[data-shape=\"icon\"]{padding-inline:0;aspect-ratio:1;border-radius:999px;display:inline-grid;place-items:center}usa-button .usa-button-face [data-label],usa-button .usa-button-face [data-icon]{transition:opacity 0.2s ease}usa-button .usa-button-face[data-shape=\"circle\"] [data-label],usa-button .usa-button-face[data-shape=\"icon\"] [data-label]{position:absolute;opacity:0;pointer-events:none}usa-button .usa-button-face[data-shape=\"circle\"] [data-icon]{opacity:0}usa-button .usa-button-status{position:absolute;inset:0;display:grid;place-items:center;pointer-events:none}usa-button .usa-button-status svg{grid-area:1 / 1;width:1.25em;height:1.25em;fill:none;stroke:currentColor;stroke-width:2.6;stroke-linecap:round;stroke-linejoin:round;opacity:0}usa-button .usa-button-spin circle{stroke-dasharray:60 100;transform-origin:50% 50%;animation:usa-btn-spin 0.8s linear infinite}usa-button .usa-button-ok path,usa-button .usa-button-err path{stroke-dasharray:1;stroke-dashoffset:1;transition:stroke-dashoffset 0.35s ease 0.1s}usa-button .usa-button-face[data-state=\"loading\"] .usa-button-spin,usa-button .usa-button-face[data-state=\"success\"] .usa-button-ok,usa-button .usa-button-face[data-state=\"error\"] .usa-button-err{opacity:1}usa-button .usa-button-face[data-state=\"success\"] .usa-button-ok path,usa-button .usa-button-face[data-state=\"error\"] .usa-button-err path{stroke-dashoffset:0}usa-button .usa-button-face:is([data-state=\"loading\"],[data-state=\"success\"],[data-state=\"error\"])>:not(.usa-button-status){opacity:0}usa-button .usa-button-face[data-state=\"success\"]{background:#16a34a;border-color:#16a34a;color:#fff}usa-button .usa-button-face[data-state=\"error\"]{background:#e5484d;border-color:#e5484d;color:#fff}usa-button .usa-button-face[data-state]{transition:background-color 0.25s ease,color 0.25s ease,border-radius 0.3s ease}@keyframes usa-btn-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){usa-button .usa-button-spin circle{animation-duration:2.4s}usa-button .usa-button-face,usa-button .usa-button-face *{transition:none !important}}";
238
+
239
+ const BUTTON_DEFORMS = ['squash', 'wobble', 'gooey', 'dent'];
240
+ let gooInjected = false;
241
+ function injectGoo() {
242
+ if (gooInjected || typeof document === 'undefined')
243
+ return;
244
+ gooInjected = true;
245
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
246
+ svg.setAttribute('aria-hidden', 'true');
247
+ svg.setAttribute('width', '0');
248
+ svg.setAttribute('height', '0');
249
+ svg.style.position = 'absolute';
250
+ svg.innerHTML = '<filter id="usa-goo"><feGaussianBlur in="SourceGraphic" stdDeviation="6" result="b"/><feColorMatrix in="b" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 22 -9" result="g"/><feComposite in="SourceGraphic" in2="g" operator="atop"/></filter>';
251
+ document.body.appendChild(svg);
252
+ }
253
+ function defineButton(tag = 'usa-button') {
254
+ return defineElement(tag, (Base) => class UsaButton extends Base {
255
+ constructor() {
256
+ super(...arguments);
257
+ this._press = null;
258
+ this._status = null;
259
+ this._shapeBefore = null;
260
+ }
261
+ static get observedAttributes() {
262
+ return ['deform', 'state', 'shape', 'disabled'];
263
+ }
264
+ get target() {
265
+ return this.querySelector(':scope > button, :scope > a, :scope > [role="button"]') || this;
266
+ }
267
+ deforms() {
268
+ return this.str('deform').split(/[\s,]+/).filter(Boolean);
269
+ }
270
+ get shape() {
271
+ return this.str('shape', 'pill') || 'pill';
272
+ }
273
+ set shape(v) {
274
+ this.morphTo(v);
275
+ }
276
+ get state() {
277
+ return this.str('state', 'idle') || 'idle';
278
+ }
279
+ set state(v) {
280
+ this.setAttribute('state', v);
281
+ }
282
+ mount() {
283
+ const t = this.target;
284
+ if (t === this) {
285
+ this.setAttribute('role', 'button');
286
+ if (!this.hasAttribute('tabindex'))
287
+ this.tabIndex = 0;
288
+ }
289
+ t.classList.add('usa-button-face');
290
+ if (this.str('morph') === 'submit' && !t.querySelector('.usa-button-status')) {
291
+ const s = document.createElement('span');
292
+ s.className = 'usa-button-status';
293
+ s.setAttribute('aria-hidden', 'true');
294
+ s.innerHTML =
295
+ '<svg class="usa-button-spin" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9" pathLength="100"/></svg>' +
296
+ '<svg class="usa-button-ok" viewBox="0 0 24 24"><path d="M5 12.5l4.2 4.2L19 7" pathLength="1"/></svg>' +
297
+ '<svg class="usa-button-err" viewBox="0 0 24 24"><path d="M7 7l10 10M17 7L7 17" pathLength="1"/></svg>';
298
+ t.append(s);
299
+ const live = document.createElement('span');
300
+ live.className = 'usa-sr';
301
+ live.setAttribute('role', 'status');
302
+ this.append(live);
303
+ this._status = live;
304
+ }
305
+ if (this.deforms().includes('gooey'))
306
+ injectGoo();
307
+ this.applyState();
308
+ this.listen(t, 'pointerdown', (e) => {
309
+ if (this.isOff() || (e.pointerType === 'mouse' && e.button !== 0))
310
+ return;
311
+ this.down(e.clientX, e.clientY);
312
+ });
313
+ for (const ev of ['pointerup', 'pointerleave', 'pointercancel'])
314
+ this.listen(t, ev, () => this.up());
315
+ this.listen(t, 'keydown', (e) => (e.key === 'Enter' || e.key === ' ') && !e.repeat && this.down());
316
+ this.listen(t, 'keyup', () => this.up());
317
+ this.listen(t, 'click', (e) => {
318
+ if (this.isOff()) {
319
+ if (this.state === 'loading')
320
+ e.preventDefault();
321
+ return;
322
+ }
323
+ if (this.deforms().includes('wobble'))
324
+ this.wobble();
325
+ if (this.hasAttribute('haptic'))
326
+ haptic(this.num('haptic', 10));
327
+ if (this.str('morph') === 'submit' && this.state === 'idle')
328
+ this.submit();
329
+ });
330
+ if (t === this)
331
+ this.listen(this, 'keydown', (e) => {
332
+ if ((e.key === 'Enter' || e.key === ' ') && !e.repeat) {
333
+ e.preventDefault();
334
+ this.click();
335
+ }
336
+ });
337
+ }
338
+ unmount() {
339
+ clearTimeout(this._timer);
340
+ this._press?.cancel();
341
+ }
342
+ changed(name) {
343
+ if (name === 'state')
344
+ this.applyState();
345
+ else if (name === 'shape')
346
+ this.target.setAttribute('data-shape', this.shape);
347
+ else {
348
+ super.changed(name);
349
+ }
350
+ }
351
+ isOff() {
352
+ return this.flag('disabled') || this.state === 'loading';
353
+ }
354
+ /* ------------------------------------------------ press deformations */
355
+ down(x, y) {
356
+ if (this.reduced || this.hasAttribute('data-pressed'))
357
+ return;
358
+ const d = this.deforms();
359
+ if (!d.length)
360
+ return;
361
+ this.setAttribute('data-pressed', '');
362
+ const t = this.target;
363
+ const r = t.getBoundingClientRect();
364
+ const px = x === undefined ? 0.5 : clamp((x - r.left) / (r.width || 1), 0, 1);
365
+ const py = y === undefined ? 0.5 : clamp((y - r.top) / (r.height || 1), 0, 1);
366
+ t.style.setProperty('--usa-dent-x', `${(px * 100).toFixed(1)}%`);
367
+ t.style.setProperty('--usa-dent-y', `${(py * 100).toFixed(1)}%`);
368
+ const parts = [];
369
+ if (d.includes('dent'))
370
+ parts.push(`perspective(600px) rotateX(${((0.5 - py) * -14).toFixed(2)}deg) rotateY(${((px - 0.5) * 14).toFixed(2)}deg) scale(0.97)`);
371
+ if (d.includes('squash'))
372
+ parts.push('scale(1.12, 0.84)');
373
+ if (d.includes('gooey')) {
374
+ parts.push('scale(0.96)');
375
+ this.goo(px, py);
376
+ }
377
+ if (!parts.length)
378
+ return;
379
+ this._press?.cancel();
380
+ this._press = this.motion(t, [{ transform: 'none' }, { transform: parts.join(' ') }], { duration: 110, easing: 'ease-out', fill: 'forwards' });
381
+ }
382
+ up() {
383
+ if (!this.hasAttribute('data-pressed'))
384
+ return;
385
+ this.removeAttribute('data-pressed');
386
+ const t = this.target;
387
+ const a = this._press;
388
+ const from = a?.effect?.getKeyframes?.().slice(-1)[0]?.transform;
389
+ a?.cancel();
390
+ if (!from)
391
+ return;
392
+ this._press = this.motion(t, [{ transform: from }, { transform: 'none' }], { ...springEasing(this.deforms().includes('squash') ? 'bouncy' : 'wobbly') });
393
+ }
394
+ wobble() {
395
+ if (this.reduced)
396
+ return;
397
+ const t = this.target;
398
+ const r = getComputedStyle(t).borderRadius || '12px';
399
+ this.motion(t, [
400
+ { borderRadius: r },
401
+ { borderRadius: '42% 58% 50% 50% / 60% 45% 55% 40%', offset: 0.2 },
402
+ { borderRadius: '58% 42% 45% 55% / 40% 60% 40% 60%', offset: 0.45 },
403
+ { borderRadius: '48% 52% 52% 48% / 52% 48% 52% 48%', offset: 0.7 },
404
+ { borderRadius: r },
405
+ ], { duration: 700, easing: 'ease-out' });
406
+ }
407
+ goo(px, py) {
408
+ const t = this.target;
409
+ const layer = document.createElement('span');
410
+ layer.className = 'usa-button-goo';
411
+ layer.setAttribute('aria-hidden', 'true');
412
+ layer.style.setProperty('--usa-goo-bg', getComputedStyle(t).backgroundColor || 'currentColor');
413
+ const drops = [];
414
+ for (let i = 0; i < 4; i++) {
415
+ const d = document.createElement('i');
416
+ d.style.left = `${(px * 100).toFixed(1)}%`;
417
+ d.style.top = `${(py * 100).toFixed(1)}%`;
418
+ layer.append(d);
419
+ drops.push(d);
420
+ }
421
+ this.prepend(layer);
422
+ let left = drops.length;
423
+ drops.forEach((d, i) => {
424
+ const ang = (i / drops.length) * Math.PI * 2 + 0.6;
425
+ const dist = 26 + (i % 2) * 12;
426
+ const a = this.motion(d, [
427
+ { transform: 'translate(-50%, -50%) scale(0.4)' },
428
+ { transform: `translate(calc(-50% + ${(Math.cos(ang) * dist).toFixed(1)}px), calc(-50% + ${(Math.sin(ang) * dist).toFixed(1)}px)) scale(1)`, offset: 0.45 },
429
+ { transform: 'translate(-50%, -50%) scale(0.2)' },
430
+ ], { duration: 700, easing: 'cubic-bezier(0.34, 1.3, 0.64, 1)', fill: 'forwards' });
431
+ const end = () => --left === 0 && layer.remove();
432
+ if (a)
433
+ a.onfinish = end;
434
+ else
435
+ end();
436
+ });
437
+ }
438
+ /* ------------------------------------------------ shape morph */
439
+ async morphTo(shape) {
440
+ const t = this.target;
441
+ const before = t.getBoundingClientRect();
442
+ this.setAttribute('shape', shape);
443
+ t.setAttribute('data-shape', shape);
444
+ if (this.reduced)
445
+ return;
446
+ const after = t.getBoundingClientRect();
447
+ if (!before.width || !after.width || Math.abs(before.width - after.width) < 0.5)
448
+ return;
449
+ const a = this.motion(t, [{ width: `${before.width}px` }, { width: `${after.width}px` }], springEasing('stiff'));
450
+ await a?.finished.catch(() => undefined);
451
+ }
452
+ /* ------------------------------------------------ submit morph */
453
+ submit() {
454
+ let settled = false;
455
+ const done = (ok = true) => {
456
+ if (settled)
457
+ return;
458
+ settled = true;
459
+ this.state = ok ? 'success' : 'error';
460
+ };
461
+ this.state = 'loading';
462
+ this.emit('submit', { done });
463
+ }
464
+ applyState() {
465
+ if (this.str('morph') !== 'submit')
466
+ return;
467
+ const t = this.target;
468
+ const s = this.state;
469
+ clearTimeout(this._timer);
470
+ t.setAttribute('data-state', s);
471
+ if (s === 'loading') {
472
+ if (this._shapeBefore === null)
473
+ this._shapeBefore = this.shape;
474
+ t.setAttribute('aria-busy', 'true');
475
+ this.morphTo('circle');
476
+ }
477
+ else {
478
+ t.removeAttribute('aria-busy');
479
+ }
480
+ if (s === 'error') {
481
+ shake(t);
482
+ if (this.hasAttribute('haptic'))
483
+ haptic([30, 40, 30]);
484
+ }
485
+ if (s === 'success' && !this.reduced) {
486
+ const ok = t.querySelector('.usa-button-ok');
487
+ if (ok)
488
+ this.motion(ok, [{ transform: 'scale(0.4)' }, { transform: 'scale(1)' }], springEasing('bouncy'));
489
+ }
490
+ if (this._status)
491
+ this._status.textContent = s === 'loading' ? 'Loading…' : s === 'success' ? 'Done' : s === 'error' ? 'Failed' : '';
492
+ if (s === 'success' || s === 'error') {
493
+ this._timer = setTimeout(() => {
494
+ this.state = 'idle';
495
+ }, this.num('reset', 1800));
496
+ }
497
+ if (s === 'idle' && this._shapeBefore !== null) {
498
+ const back = this._shapeBefore;
499
+ this._shapeBefore = null;
500
+ this.morphTo(back);
501
+ }
502
+ this.emit('state', { state: s });
503
+ }
504
+ }, { id: 'button', text: css$5 });
505
+ }
506
+
507
+ var css$4 = "usa-icon-morph{display:inline-grid;place-items:center;line-height:0;color:inherit;-webkit-tap-highlight-color:transparent;vertical-align:middle}usa-icon-morph svg{fill:currentColor;overflow:visible}usa-icon-morph[toggle]{cursor:pointer;border-radius:8px}usa-icon-morph[toggle]:focus-visible{outline:2px solid var(--usa-accent,#7c5cff);outline-offset:3px}";
508
+
509
+ const C = [[12, 12], [12, 12], [12, 12], [12, 12]];
510
+ /**
511
+ * Morphable icons: every icon is three quads (four points each) on a 24×24
512
+ * grid, so any icon can morph into any other by interpolating points.
513
+ */
514
+ const MORPH_ICONS = {
515
+ play: [[[7, 5], [12.5, 8.25], [12.5, 15.75], [7, 19]], [[12.5, 8.25], [19, 12], [19, 12], [12.5, 15.75]], C],
516
+ pause: [[[6, 5], [10, 5], [10, 19], [6, 19]], [[14, 5], [18, 5], [18, 19], [14, 19]], C],
517
+ menu: [[[4, 6], [20, 6], [20, 8], [4, 8]], [[4, 11], [20, 11], [20, 13], [4, 13]], [[4, 16], [20, 16], [20, 18], [4, 18]]],
518
+ close: [[[5.2, 6.6], [6.6, 5.2], [18.8, 17.4], [17.4, 18.8]], C, [[17.4, 5.2], [18.8, 6.6], [6.6, 18.8], [5.2, 17.4]]],
519
+ plus: [[[11, 4], [13, 4], [13, 20], [11, 20]], [[4, 11], [20, 11], [20, 13], [4, 13]], C],
520
+ minus: [[[4, 11], [20, 11], [20, 13], [4, 13]], [[4, 11], [20, 11], [20, 13], [4, 13]], C],
521
+ check: [[[4.3, 12.7], [5.7, 11.3], [10.4, 16], [9, 17.4]], [[9, 17.4], [7.6, 16], [18.3, 5.3], [19.7, 6.7]], C],
522
+ 'arrow-right': [[[4, 11], [17, 11], [17, 13], [4, 13]], [[12.6, 6.4], [14, 5], [21, 12], [19.6, 13.4]], [[19.6, 10.6], [21, 12], [14, 19], [12.6, 17.6]]],
523
+ };
524
+ /** SVG path data for an icon, or for the interpolation `t` (0–1) between two. */
525
+ function morphPath(from, to = from, t = 0) {
526
+ const a = MORPH_ICONS[from] || MORPH_ICONS.menu;
527
+ const b = MORPH_ICONS[to] || a;
528
+ return a
529
+ .map((q, i) => {
530
+ const pts = q.map((p, j) => {
531
+ const r = b[i][j];
532
+ return `${(p[0] + (r[0] - p[0]) * t).toFixed(2)} ${(p[1] + (r[1] - p[1]) * t).toFixed(2)}`;
533
+ });
534
+ return `M${pts.join('L')}Z`;
535
+ })
536
+ .join('');
537
+ }
538
+ function defineIconMorph(tag = 'usa-icon-morph') {
539
+ return defineElement(tag, (Base) => class UsaIconMorph extends Base {
540
+ constructor() {
541
+ super(...arguments);
542
+ this._i = 0;
543
+ this._from = 'play';
544
+ this._to = 'play';
545
+ }
546
+ static get observedAttributes() {
547
+ return ['icons', 'size', 'toggle'];
548
+ }
549
+ list() {
550
+ return this.str('icons', 'play,pause').split(',').map((s) => s.trim()).filter((s) => MORPH_ICONS[s]);
551
+ }
552
+ get index() {
553
+ return this._i;
554
+ }
555
+ set index(v) {
556
+ this.show(v);
557
+ }
558
+ get icon() {
559
+ return this.list()[this._i] || 'play';
560
+ }
561
+ draw(t) {
562
+ this.querySelector('path')?.setAttribute('d', morphPath(this._from, this._to, t));
563
+ }
564
+ mount() {
565
+ const size = this.num('size', 24);
566
+ this.innerHTML = `<svg viewBox="0 0 24 24" width="${size}" height="${size}" aria-hidden="true" focusable="false"><path/></svg>`;
567
+ this._i = Math.max(0, Math.min(this.list().length - 1, this.num('index', 0)));
568
+ this._from = this._to = this.icon;
569
+ this._t = createSpring({ value: 1, spring: this.str('preset', 'wobbly'), onUpdate: (v) => this.draw(v) });
570
+ this.draw(1);
571
+ if (this.flag('toggle')) {
572
+ this.setAttribute('role', 'button');
573
+ if (!this.hasAttribute('tabindex'))
574
+ this.tabIndex = 0;
575
+ this.listen(this, 'click', () => this.next());
576
+ this.listen(this, 'keydown', (e) => {
577
+ if ((e.key === 'Enter' || e.key === ' ') && !e.repeat) {
578
+ e.preventDefault();
579
+ this.next();
580
+ }
581
+ });
582
+ }
583
+ this.label();
584
+ }
585
+ unmount() {
586
+ this._t?.stop();
587
+ }
588
+ label() {
589
+ const labels = this.str('labels').split(',').map((s) => s.trim());
590
+ const l = labels[this._i];
591
+ if (l)
592
+ this.setAttribute('aria-label', l);
593
+ if (!this.flag('toggle') && !l)
594
+ this.setAttribute('aria-hidden', 'true');
595
+ }
596
+ show(icon) {
597
+ const list = this.list();
598
+ const i = typeof icon === 'number' ? ((icon % list.length) + list.length) % list.length : list.indexOf(icon);
599
+ if (i < 0 || !this._t)
600
+ return;
601
+ const name = list[i];
602
+ // morph from wherever we are now (interruptible)
603
+ const cur = this._t.value;
604
+ this._from = cur >= 0.5 ? this._to : this._from;
605
+ this._to = name;
606
+ this._i = i;
607
+ this._t.jump(0);
608
+ this._t.set(1);
609
+ this.label();
610
+ this.emit('change', { index: i, icon: name });
611
+ }
612
+ next() {
613
+ this.show(this._i + 1);
614
+ }
615
+ }, { id: 'icon-morph', text: css$4 });
616
+ }
617
+
618
+ var css$3 = "usa-like{--usa-like-color:#f43f5e;display:inline-flex;align-items:center;gap:0.35em;cursor:pointer;user-select:none;-webkit-tap-highlight-color:transparent;vertical-align:middle}usa-like .usa-like-heart{fill:transparent;stroke:currentColor;stroke-width:1.8;transition:fill 0.2s ease,stroke 0.2s ease;overflow:visible}usa-like[liked] .usa-like-heart{fill:var(--usa-like-color);stroke:var(--usa-like-color)}usa-like .usa-like-count{font-variant-numeric:tabular-nums}usa-like[disabled]{opacity:0.5;cursor:not-allowed}usa-like:focus-visible{outline:2px solid var(--usa-like-color);outline-offset:3px;border-radius:8px}";
619
+
620
+ const HEART = 'M12 21s-7.5-4.6-9.6-9.2C.9 8.4 3 4.5 6.7 4.5c2.1 0 3.6 1.2 5.3 3.2 1.7-2 3.2-3.2 5.3-3.2 3.7 0 5.8 3.9 4.3 7.3C19.5 16.4 12 21 12 21z';
621
+ function defineLike(tag = 'usa-like') {
622
+ return defineElement(tag, (Base) => class UsaLike extends Base {
623
+ static get observedAttributes() {
624
+ return ['liked', 'count', 'label', 'disabled'];
625
+ }
626
+ get liked() {
627
+ return this.flag('liked');
628
+ }
629
+ set liked(v) {
630
+ this.setFlag('liked', v);
631
+ }
632
+ get count() {
633
+ return this.hasAttribute('count') ? this.num('count', 0) : null;
634
+ }
635
+ set count(v) {
636
+ if (v === null)
637
+ this.removeAttribute('count');
638
+ else
639
+ this.setAttribute('count', String(v));
640
+ }
641
+ mount() {
642
+ if (!this.querySelector(':scope > .usa-like-heart')) {
643
+ const size = this.num('size', 24);
644
+ this.insertAdjacentHTML('afterbegin', `<svg class="usa-like-heart" viewBox="0 0 24 24" width="${size}" height="${size}" aria-hidden="true"><path d="${HEART}"/></svg><span class="usa-like-count" aria-hidden="true"></span>`);
645
+ }
646
+ if (this.str('color'))
647
+ this.style.setProperty('--usa-like-color', this.str('color'));
648
+ this.setAttribute('role', 'button');
649
+ if (!this.hasAttribute('tabindex'))
650
+ this.tabIndex = 0;
651
+ this.sync();
652
+ this.listen(this, 'click', () => this.toggle());
653
+ this.listen(this, 'keydown', (e) => {
654
+ if ((e.key === 'Enter' || e.key === ' ') && !e.repeat) {
655
+ e.preventDefault();
656
+ this.toggle();
657
+ }
658
+ });
659
+ }
660
+ changed() {
661
+ this.sync();
662
+ }
663
+ sync() {
664
+ const c = this.count;
665
+ const label = this.str('label', 'Like');
666
+ this.setAttribute('aria-pressed', String(this.liked));
667
+ this.setAttribute('aria-label', c === null ? label : `${label} (${c})`);
668
+ this.toggleAttribute('aria-disabled', this.flag('disabled'));
669
+ const out = this.querySelector('.usa-like-count');
670
+ if (out)
671
+ out.textContent = c === null ? '' : new Intl.NumberFormat().format(c);
672
+ }
673
+ toggle(force) {
674
+ if (this.flag('disabled'))
675
+ return;
676
+ const next = force === undefined ? !this.liked : force;
677
+ if (next === this.liked)
678
+ return;
679
+ this.liked = next;
680
+ if (this.count !== null)
681
+ this.count = Math.max(0, this.count + (next ? 1 : -1));
682
+ this.sync();
683
+ const heart = this.querySelector('.usa-like-heart');
684
+ if (next && heart && !this.reduced) {
685
+ this.motion(heart, [{ transform: 'scale(0.4)' }, { transform: 'scale(1)' }], springEasing('bouncy'));
686
+ const r = heart.getBoundingClientRect();
687
+ burst(r.left + r.width / 2, r.top + r.height / 2, { count: 10, distance: 28, size: 5, colors: [getComputedStyle(this).getPropertyValue('--usa-like-color').trim() || '#f43f5e', '#fb923c', '#facc15'] });
688
+ }
689
+ if (this.hasAttribute('haptic'))
690
+ haptic(this.num('haptic', 12));
691
+ this.dispatchEvent(new Event('change', { bubbles: true }));
692
+ this.emit('change', { liked: next, count: this.count });
693
+ }
694
+ }, { id: 'like', text: css$3 });
695
+ }
696
+
697
+ var css$2 = "usa-hold{--usa-hold-color:#e5484d;position:relative;display:inline-flex;align-items:center;gap:0.5em;cursor:pointer;user-select:none;-webkit-user-select:none;touch-action:manipulation;-webkit-tap-highlight-color:transparent}usa-hold .usa-hold-ring{width:1.6em;height:1.6em;transform:rotate(-90deg);flex:none}usa-hold .usa-hold-ring circle{fill:none;stroke-width:3.5;stroke-linecap:round}usa-hold .usa-hold-ring circle:first-child{stroke:color-mix(in srgb,currentColor 18%,transparent)}usa-hold .usa-hold-ring circle:last-child{stroke:var(--usa-hold-color);stroke-dasharray:100;stroke-dashoffset:100}usa-hold[data-holding]{transform:scale(calc(1 - var(--usa-hold,0) * 0.05))}usa-hold[data-done] .usa-hold-ring{animation:usa-hold-pop 0.4s cubic-bezier(0.34,1.56,0.64,1)}@keyframes usa-hold-pop{50%{transform:rotate(-90deg) scale(1.3)}}usa-hold[disabled]{opacity:0.5;cursor:not-allowed}usa-hold:focus-visible{outline:2px solid var(--usa-hold-color);outline-offset:3px;border-radius:10px}@media (prefers-reduced-motion:reduce){usa-hold[data-holding]{transform:none}usa-hold[data-done] .usa-hold-ring{animation:none}}";
698
+
699
+ function defineHold(tag = 'usa-hold') {
700
+ return defineElement(tag, (Base) => class UsaHold extends Base {
701
+ constructor() {
702
+ super(...arguments);
703
+ this._p = 0;
704
+ this._frame = 0;
705
+ this._holding = false;
706
+ }
707
+ static get observedAttributes() {
708
+ return ['duration', 'disabled'];
709
+ }
710
+ get progress() {
711
+ return this._p;
712
+ }
713
+ set(p) {
714
+ this._p = p;
715
+ this.style.setProperty('--usa-hold', p.toFixed(4));
716
+ const c = this.querySelector('.usa-hold-ring circle:last-child');
717
+ if (c)
718
+ c.style.strokeDashoffset = String(100 - p * 100);
719
+ }
720
+ mount() {
721
+ if (!this.querySelector(':scope > .usa-hold-ring')) {
722
+ this.insertAdjacentHTML('beforeend', '<svg class="usa-hold-ring" viewBox="0 0 36 36" aria-hidden="true"><circle cx="18" cy="18" r="15.9" pathLength="100"/><circle cx="18" cy="18" r="15.9" pathLength="100"/></svg>');
723
+ }
724
+ if (this.str('color'))
725
+ this.style.setProperty('--usa-hold-color', this.str('color'));
726
+ this.setAttribute('role', 'button');
727
+ if (!this.hasAttribute('tabindex'))
728
+ this.tabIndex = 0;
729
+ if (this.str('label'))
730
+ this.setAttribute('aria-label', this.str('label'));
731
+ this.setAttribute('aria-description', 'Press and hold to confirm');
732
+ this.set(0);
733
+ this.listen(this, 'pointerdown', (e) => (e.pointerType !== 'mouse' || e.button === 0) && this.start());
734
+ for (const t of ['pointerup', 'pointerleave', 'pointercancel'])
735
+ this.listen(this, t, () => this.stop());
736
+ this.listen(this, 'keydown', (e) => {
737
+ if (e.key === 'Enter' || e.key === ' ') {
738
+ e.preventDefault();
739
+ if (!e.repeat)
740
+ this.start();
741
+ }
742
+ });
743
+ this.listen(this, 'keyup', (e) => (e.key === 'Enter' || e.key === ' ') && this.stop());
744
+ this.listen(this, 'contextmenu', (e) => this._holding && e.preventDefault());
745
+ }
746
+ unmount() {
747
+ caf(this._frame);
748
+ this._holding = false;
749
+ }
750
+ start() {
751
+ if (this.flag('disabled') || this._holding)
752
+ return;
753
+ this._holding = true;
754
+ this.setAttribute('data-holding', '');
755
+ const dur = Math.max(100, this.num('duration', 1200));
756
+ let last = now();
757
+ const tick = () => {
758
+ const t = now();
759
+ const p = Math.min(1, this._p + (t - last) / dur);
760
+ last = t;
761
+ this.set(p);
762
+ this.emit('progress', { progress: p });
763
+ if (p >= 1) {
764
+ this._holding = false;
765
+ this.removeAttribute('data-holding');
766
+ this.setAttribute('data-done', '');
767
+ if (this.hasAttribute('haptic'))
768
+ haptic(20);
769
+ this.emit('confirm');
770
+ setTimeout(() => {
771
+ this.removeAttribute('data-done');
772
+ this.rewind();
773
+ }, 700);
774
+ return;
775
+ }
776
+ this._frame = raf(tick);
777
+ };
778
+ this._frame = raf(tick);
779
+ }
780
+ stop() {
781
+ if (!this._holding)
782
+ return;
783
+ this._holding = false;
784
+ caf(this._frame);
785
+ this.removeAttribute('data-holding');
786
+ if (this._p < 1) {
787
+ this.emit('cancel', { progress: this._p });
788
+ this.rewind();
789
+ }
790
+ }
791
+ rewind() {
792
+ let last = now();
793
+ const back = () => {
794
+ if (this._holding)
795
+ return;
796
+ const t = now();
797
+ const p = Math.max(0, this._p - (t - last) / 250);
798
+ last = t;
799
+ this.set(p);
800
+ if (p > 0)
801
+ this._frame = raf(back);
802
+ };
803
+ this._frame = raf(back);
804
+ }
805
+ cancel() {
806
+ this.stop();
807
+ }
808
+ }, { id: 'hold', text: css$2 });
809
+ }
810
+
811
+ var css$1 = "usa-double-tap{position:relative;display:block;touch-action:manipulation;-webkit-tap-highlight-color:transparent;user-select:none;-webkit-user-select:none}usa-double-tap .usa-double-tap-icon{position:absolute;pointer-events:none;font-size:64px;line-height:1;color:#f43f5e;transform:translate(-50%,-50%);text-shadow:0 6px 18px rgb(0 0 0 / 0.35);z-index:2}";
812
+
813
+ function defineDoubleTap(tag = 'usa-double-tap') {
814
+ return defineElement(tag, (Base) => class UsaDoubleTap extends Base {
815
+ mount() {
816
+ let last = 0;
817
+ let lx = 0;
818
+ let ly = 0;
819
+ this.listen(this, 'pointerup', (e) => {
820
+ if (this.flag('disabled'))
821
+ return;
822
+ const t = e.timeStamp || Date.now();
823
+ if (t - last < this.num('delay', 300) && Math.hypot(e.clientX - lx, e.clientY - ly) < 40) {
824
+ last = 0;
825
+ this.pop(e.clientX, e.clientY);
826
+ }
827
+ else {
828
+ last = t;
829
+ lx = e.clientX;
830
+ ly = e.clientY;
831
+ }
832
+ });
833
+ this.listen(this, 'dblclick', (e) => e.preventDefault());
834
+ this.listen(this, 'keydown', (e) => e.key.toLowerCase() === 'l' && !e.repeat && this.pop());
835
+ }
836
+ pop(x, y) {
837
+ const r = this.getBoundingClientRect();
838
+ if (x === undefined || y === undefined) {
839
+ x = r.left + r.width / 2;
840
+ y = r.top + r.height / 2;
841
+ }
842
+ const icon = document.createElement('span');
843
+ icon.className = 'usa-double-tap-icon';
844
+ icon.setAttribute('aria-hidden', 'true');
845
+ icon.textContent = this.str('icon', '♥');
846
+ icon.style.left = `${x - r.left}px`;
847
+ icon.style.top = `${y - r.top}px`;
848
+ if (this.str('color'))
849
+ icon.style.color = this.str('color');
850
+ this.append(icon);
851
+ const frames = this.reduced
852
+ ? [{ opacity: 0 }, { opacity: 1, offset: 0.3 }, { opacity: 0 }]
853
+ : [
854
+ { opacity: 0, transform: 'translate(-50%, -50%) scale(0.2) rotate(-15deg)' },
855
+ { opacity: 1, transform: 'translate(-50%, -50%) scale(1.15) rotate(5deg)', offset: 0.3 },
856
+ { opacity: 1, transform: 'translate(-50%, -50%) scale(1) rotate(0deg)', offset: 0.6 },
857
+ { opacity: 0, transform: 'translate(-50%, -110%) scale(0.9)' },
858
+ ];
859
+ const a = this.motion(icon, frames, { duration: 900, easing: 'ease-out', fill: 'forwards' });
860
+ if (a)
861
+ a.onfinish = () => icon.remove();
862
+ else
863
+ icon.remove();
864
+ burst(x, y, { count: 8, distance: 40, size: 5, colors: [this.str('color', '#f43f5e'), '#fb923c', '#facc15'] });
865
+ if (this.hasAttribute('haptic'))
866
+ haptic(15);
867
+ this.emit('double-tap', { x: x - r.left, y: y - r.top });
868
+ }
869
+ }, { id: 'double-tap', text: css$1 });
870
+ }
871
+
872
+ var css = "usa-checkbox{--usa-checkbox-on:var(--usa-accent,#7c5cff);display:inline-flex;align-items:center;gap:0.55em;cursor:pointer;user-select:none;-webkit-tap-highlight-color:transparent;vertical-align:middle}usa-checkbox .usa-checkbox-box{flex:none;width:1.25em;height:1.25em;border-radius:0.3em;border:1.5px solid color-mix(in srgb,currentColor 55%,transparent);display:grid;place-items:center;transition:background-color 0.2s ease,border-color 0.2s ease;box-sizing:border-box}usa-checkbox[shape=\"circle\"] .usa-checkbox-box{border-radius:50%}usa-checkbox svg{width:85%;height:85%;fill:none;stroke:#fff;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}usa-checkbox svg path{stroke-dasharray:1;stroke-dashoffset:1;transition:stroke-dashoffset 0.28s cubic-bezier(0.65,0,0.35,1) 0.05s}usa-checkbox[checked]:not([indeterminate]) .usa-checkbox-check,usa-checkbox[indeterminate] .usa-checkbox-dash{stroke-dashoffset:0}usa-checkbox[checked] .usa-checkbox-box,usa-checkbox[indeterminate] .usa-checkbox-box{background:var(--usa-checkbox-on);border-color:var(--usa-checkbox-on)}usa-checkbox[disabled]{opacity:0.45;cursor:not-allowed}usa-checkbox:focus-visible{outline:none}usa-checkbox:focus-visible .usa-checkbox-box{outline:2px solid var(--usa-checkbox-on);outline-offset:2px}@media (prefers-reduced-motion:reduce){usa-checkbox svg path,usa-checkbox .usa-checkbox-box{transition:none}}";
873
+
874
+ function defineCheckbox(tag = 'usa-checkbox') {
875
+ return defineElement(tag, (Base) => {
876
+ class UsaCheckbox extends Base {
877
+ static get observedAttributes() {
878
+ return ['checked', 'indeterminate', 'disabled', 'label'];
879
+ }
880
+ constructor() {
881
+ super();
882
+ this._internals = null;
883
+ try {
884
+ this._internals = this.attachInternals?.() ?? null;
885
+ }
886
+ catch {
887
+ this._internals = null;
888
+ }
889
+ }
890
+ get checked() {
891
+ return this.flag('checked');
892
+ }
893
+ set checked(v) {
894
+ this.setFlag('checked', v);
895
+ }
896
+ get indeterminate() {
897
+ return this.flag('indeterminate');
898
+ }
899
+ set indeterminate(v) {
900
+ this.setFlag('indeterminate', v);
901
+ }
902
+ mount() {
903
+ if (!this.querySelector(':scope > .usa-checkbox-box')) {
904
+ this.insertAdjacentHTML('afterbegin', '<span class="usa-checkbox-box" aria-hidden="true"><svg viewBox="0 0 24 24"><path class="usa-checkbox-check" d="M5 12.5l4.2 4.2L19 7" pathLength="1"/><path class="usa-checkbox-dash" d="M6 12h12" pathLength="1"/></svg></span>');
905
+ }
906
+ this.setAttribute('role', 'checkbox');
907
+ if (!this.hasAttribute('tabindex'))
908
+ this.tabIndex = 0;
909
+ this.sync();
910
+ this.listen(this, 'click', () => this.toggle());
911
+ this.listen(this, 'keydown', (e) => {
912
+ if (e.key === ' ' && !e.repeat) {
913
+ e.preventDefault();
914
+ this.toggle();
915
+ }
916
+ });
917
+ }
918
+ changed() {
919
+ this.sync();
920
+ }
921
+ sync() {
922
+ this.setAttribute('aria-checked', this.indeterminate ? 'mixed' : String(this.checked));
923
+ if (this.str('label'))
924
+ this.setAttribute('aria-label', this.str('label'));
925
+ this.toggleAttribute('aria-disabled', this.flag('disabled'));
926
+ this._internals?.setFormValue?.(this.checked ? this.str('value', 'on') : null);
927
+ }
928
+ toggle(force) {
929
+ if (this.flag('disabled'))
930
+ return;
931
+ const next = force === undefined ? !this.checked || this.indeterminate : force;
932
+ this.indeterminate = false;
933
+ this.checked = next;
934
+ this.sync();
935
+ const box = this.querySelector('.usa-checkbox-box');
936
+ if (box && !this.reduced)
937
+ this.motion(box, [{ transform: 'scale(0.75)' }, { transform: 'scale(1)' }], springEasing('bouncy'));
938
+ this.dispatchEvent(new Event('change', { bubbles: true }));
939
+ this.emit('change', { checked: next });
940
+ }
941
+ }
942
+ UsaCheckbox.formAssociated = true;
943
+ return UsaCheckbox;
944
+ }, { id: 'checkbox', text: css });
945
+ }
946
+
947
+ /**
948
+ * use-scroll-animate/components/click — click & tap effects (v2.5).
949
+ * `<usa-click>` (ripple, burst, confetti, squish, press-spring, shake),
950
+ * `<usa-button>` (button click deformation: squash, wobble, gooey, dent;
951
+ * shape morph; submit → loading → success), `<usa-icon-morph>`,
952
+ * `<usa-like>`, `<usa-hold>`, `<usa-double-tap>`, `<usa-checkbox>`, plus
953
+ * `burst()`, `confetti()`, `shake()` and `haptic()`.
954
+ */
955
+ /** Register every component of this category under its default tag. */
956
+ function defineClickComponents() {
957
+ defineClick();
958
+ defineButton();
959
+ defineIconMorph();
960
+ defineLike();
961
+ defineHold();
962
+ defineDoubleTap();
963
+ defineCheckbox();
964
+ }
965
+
966
+ export { BUTTON_DEFORMS, CLICK_EFFECTS, MORPH_ICONS, burst, confetti, defineButton, defineCheckbox, defineClick, defineClickComponents, defineDoubleTap, defineHold, defineIconMorph, defineLike, haptic, morphPath, prefersReducedMotion, shake };
967
+ //# sourceMappingURL=click.js.map