use-scroll-animate 7.5.0 → 7.7.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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [7.7.0] - 2026-10-08
11
+
12
+ ### Added
13
+ - **3 new components (7.7)** in `motionary/components/widgets` (also in `dist/widgets.umd.js`):
14
+ - `<usa-field>` — animated text input: the `label` floats up on focus / when filled, the underline grows, native validation on blur (`type`, `required`, `pattern`, `minlength`, `maxlength`) — invalid shakes and slides the message in (`usa:invalid`, custom text via `error`), valid draws a check (`usa:valid`); `hint`; `strength` adds a 4-step password meter. The `<input>` is in the light DOM, so forms submit it by `name`. `value`, `validate()`, `input`; `passwordStrength()`.
15
+ - `<usa-otp>` — one-time-code input: `length` boxes (3–10, default 6), auto-advance, Backspace / arrow keys, paste a whole code, `autocomplete="one-time-code"`, `mode="alnum"`, initial `value`; digits pop in; `usa:complete` { code }; `error(message)` shakes red and clears, `success()` green wave, `fillCode(code)`, `clear()`. `sanitizeCode()`.
16
+ - `<usa-upload-progress>` — file row: `name`, `size` (bytes), `value` 0–100 (bar eases, shine while uploading), `status` uploading | done | error, `message`; done draws a check (`usa:done`), error shakes + Retry (`usa:error`, `usa:retry`). `formatBytes()`.
17
+ - **Form motion — `motionary/fx/form`** (= `motionary/components/fx-form`, `registerFormPack()`, also in `registerEffectPacks()`): `field-shake` (attention), `field-success` (attention), `label-float` (enter), `form-cascade` (enter). `shakeFrames()`.
18
+ - Showcase: 5 new gallery cards with copyable code, live demos and live Store thumbnails; Animation Store 321 → 326 items.
19
+ - Note: the plan's "success check" ships as `field-success` (the existing `success-check` effect is unchanged).
20
+
21
+ ### Accessibility
22
+ - Field: real `<label for>`, `aria-invalid`, message in an `aria-live` region linked with `aria-describedby`. OTP: labelled `role="group"`, each box labelled "Digit n of N". Upload: `role="progressbar"` with `aria-valuenow` / `aria-valuetext`, a real Retry button. Reduced motion: no shake, pop, wave, shine or easing — states switch at once; `field-shake` / `field-success` only flash the outline, `label-float` / `form-cascade` fade in.
23
+
24
+ ## [7.6.0] - 2026-10-08
25
+
26
+ ### Added
27
+ - **2 new components (7.6)** in `motionary/components/widgets` (also in `dist/widgets.umd.js`):
28
+ - `<usa-globe>` — SVG globe in orthographic projection (no WebGL, no tiles): graticule, pulsing `markers` ("Shanghai:31.2,121.5; London:51.5,-0.1"), `speed` (°/s, 0 = still), `tilt`, `lon`; spins only while on screen, drag to turn; `flyTo(name)` eases a marker round to the front (`usa:focus`). `project()`, `parseMarkers()`.
29
+ - `<usa-location-card>` — place card with a stylised mini map: `name`, `address`, `lat`/`lon`, origin `from-lat`/`from-lon` (distance by haversine, `unit` km | mi, or `distance`), `href` → “Directions”. On first view the route draws itself, the pin drops with a bounce and a ring pulses; `replay()`; `usa:arrive`. `haversine()`, `formatDistance()`.
30
+ - **Maps & geo motion — `motionary/fx/geo`** (= `motionary/components/fx-geo`, `registerGeoPack()`, also in `registerEffectPacks()`): `route-draw` (enter), `marker-pulse` (attention), `pin-drop` (enter), `globe-spin` (enter). `routeLength()`.
31
+ - Showcase: 4 new gallery cards with copyable code and live demos (and live Store thumbnails); Animation Store 317 → 321 items.
32
+ - Note: the plan's “flight lines” (飞线) ship as `route-draw` over any SVG path.
33
+
34
+ ### Accessibility
35
+ - Globe = `role="img"` labelled with its marker names; location card = labelled `<article>` with a heading and a real link. Reduced motion: the globe does not spin and `flyTo()` jumps; pin and route appear at once; `route-draw` shows the routes, `marker-pulse` does nothing, `pin-drop` / `globe-spin` fade in.
36
+
10
37
  ## [7.5.0] - 2026-10-08
11
38
 
12
39
  ### Added
@@ -0,0 +1,92 @@
1
+ 'use strict';
2
+
3
+ var registry = require('../chunks/registry-DehBVRDV.cjs');
4
+ require('../chunks/base-BaQV-2ha.cjs');
5
+
6
+ /** Decaying sideways shake keyframes (7.7). */
7
+ function shakeFrames(distance = 8, steps = 5) {
8
+ const f = [{ transform: 'none' }];
9
+ for (let i = 0; i < steps; i++) {
10
+ const d = Math.round(distance * (1 - i / steps) * 10) / 10;
11
+ f.push({ transform: `translateX(${i % 2 ? d : -d}px)` });
12
+ }
13
+ f.push({ transform: 'none' });
14
+ return f;
15
+ }
16
+ const fade = (el, ctx) => ctx.animate(el, [{ opacity: 0 }, { opacity: 1 }], { duration: 250 })?.finished.catch(() => undefined);
17
+ const ring = (el, color, ctx, duration) => ctx.animate(el, [{ boxShadow: `0 0 0 0 ${color}` }, { boxShadow: `0 0 0 4px ${color}`, offset: 0.3 }, { boxShadow: '0 0 0 0 transparent' }], { duration })?.finished.catch(() => undefined);
18
+ const FORM_FX = [
19
+ {
20
+ name: 'field-shake',
21
+ kind: 'attention',
22
+ description: 'Shakes the element sideways with a red outline flash — the "invalid" cue (`distance`, `color`).',
23
+ defaults: { distance: 8, color: 'rgba(225,29,72,.55)', duration: 450 },
24
+ run: (el, o, ctx) => {
25
+ const r = ring(el, o.color, ctx, o.duration + 200);
26
+ if (ctx.reduced)
27
+ return r;
28
+ return ctx.animate(el, shakeFrames(Number(o.distance) || 8), { duration: o.duration, easing: 'ease-out' })?.finished.catch(() => undefined);
29
+ },
30
+ },
31
+ {
32
+ name: 'field-success',
33
+ kind: 'attention',
34
+ description: 'A green glow pulses round the element and a check badge pops on its corner (`color`).',
35
+ defaults: { color: 'rgba(22,163,74,.5)', duration: 900 },
36
+ run: (el, o, ctx) => {
37
+ const r = ring(el, o.color, ctx, o.duration);
38
+ if (ctx.reduced)
39
+ return r;
40
+ if (getComputedStyle(el).position === 'static') {
41
+ const prev = el.style.position;
42
+ el.style.position = 'relative';
43
+ ctx.onCleanup(() => (el.style.position = prev));
44
+ }
45
+ const b = document.createElement('span');
46
+ b.setAttribute('aria-hidden', 'true');
47
+ b.textContent = '✓';
48
+ Object.assign(b.style, { position: 'absolute', right: '-6px', top: '-6px', width: '18px', height: '18px', borderRadius: '50%', background: '#16a34a', color: '#fff', font: '700 12px/18px system-ui,sans-serif', textAlign: 'center', pointerEvents: 'none' });
49
+ el.appendChild(b);
50
+ const end = () => b.remove();
51
+ ctx.onCleanup(end);
52
+ const a = ctx.animate(b, [{ transform: 'scale(0)', opacity: 0 }, { transform: 'scale(1.25)', opacity: 1, offset: 0.25 }, { transform: 'scale(1)', opacity: 1, offset: 0.4 }, { transform: 'scale(1)', opacity: 1, offset: 0.85 }, { transform: 'scale(.6)', opacity: 0 }], { duration: o.duration + 500, easing: 'ease-out' });
53
+ return a ? a.finished.then(end, end) : (end(), undefined);
54
+ },
55
+ },
56
+ {
57
+ name: 'label-float',
58
+ kind: 'enter',
59
+ description: 'Every `label` (or `[data-label]`) inside rises and settles one after another, like floating labels (`stagger`).',
60
+ defaults: { stagger: 80, duration: 450 },
61
+ run: (el, o, ctx) => {
62
+ const marked = Array.from(el.querySelectorAll('[data-label]'));
63
+ const labels = marked.length ? marked : Array.from(el.querySelectorAll('label'));
64
+ const items = labels.length ? labels : [el];
65
+ return Promise.all(items.map((l, i) => ctx.reduced
66
+ ? fade(l, ctx)
67
+ : ctx.animate(l, [{ transform: 'translateY(14px) scale(1.12)', opacity: 0, transformOrigin: '0 50%' }, { transform: 'translateY(-3px) scale(.96)', opacity: 1, offset: 0.7 }, { transform: 'none', opacity: 1 }], { duration: o.duration, delay: i * o.stagger, easing: 'cubic-bezier(.2,.8,.2,1)', fill: 'backwards' })?.finished.catch(() => undefined))).then(() => undefined);
68
+ },
69
+ },
70
+ {
71
+ name: 'form-cascade',
72
+ kind: 'enter',
73
+ description: "The element's children (fields, buttons) slide in one after another (`stagger`, `distance`).",
74
+ defaults: { stagger: 70, distance: 18, duration: 500 },
75
+ run: (el, o, ctx) => {
76
+ const kids = Array.from(el.children);
77
+ const items = kids.length ? kids : [el];
78
+ return Promise.all(items.map((k, i) => ctx.reduced
79
+ ? fade(k, ctx)
80
+ : ctx.animate(k, [{ transform: `translateY(${Number(o.distance) || 18}px)`, opacity: 0 }, { transform: 'none', opacity: 1 }], { duration: o.duration, delay: i * o.stagger, easing: 'cubic-bezier(.2,.8,.2,1)', fill: 'backwards' })?.finished.catch(() => undefined))).then(() => undefined);
81
+ },
82
+ },
83
+ ];
84
+ /** Register field-shake, field-success, label-float and form-cascade (7.7). */
85
+ function registerFormPack() {
86
+ registry.registerEffects(FORM_FX);
87
+ }
88
+
89
+ exports.FORM_FX = FORM_FX;
90
+ exports.registerFormPack = registerFormPack;
91
+ exports.shakeFrames = shakeFrames;
92
+ //# sourceMappingURL=fx-form.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fx-form.cjs","sources":["../../src/components/fx2/form.ts"],"sourcesContent":["/**\n * 7.7 — Form motion (`motionary/fx/form`, also `motionary/components/fx-form`):\n *\n * - `field-shake` (attention) — the element shakes sideways with a red\n * outline flash, the classic \"invalid\" cue (`distance`, `color`).\n * - `field-success` (attention) — a green glow pulses round the element and a\n * small check badge pops on its corner (`color`).\n * - `label-float` (enter) — every `label` (or `[data-label]`) inside rises and\n * settles, one after another, like floating labels (`stagger`).\n * - `form-cascade` (enter) — the element's children (fields, buttons) slide\n * in one after another (`stagger`, `distance`).\n *\n * Reduced motion: field-shake / field-success only flash the outline colour,\n * label-float and form-cascade fade in.\n */\nimport type { EffectContext, EffectDefinition } from '../fx/registry';\nimport { registerEffects } from '../fx/registry';\n\n/** Decaying sideways shake keyframes (7.7). */\nexport function shakeFrames(distance = 8, steps = 5): Keyframe[] {\n const f: Keyframe[] = [{ transform: 'none' }];\n for (let i = 0; i < steps; i++) {\n const d = Math.round(distance * (1 - i / steps) * 10) / 10;\n f.push({ transform: `translateX(${i % 2 ? d : -d}px)` });\n }\n f.push({ transform: 'none' });\n return f;\n}\n\nconst fade = (el: HTMLElement, ctx: EffectContext) => ctx.animate(el, [{ opacity: 0 }, { opacity: 1 }], { duration: 250 })?.finished.catch(() => undefined);\nconst ring = (el: HTMLElement, color: string, ctx: EffectContext, duration: number) =>\n ctx.animate(el, [{ boxShadow: `0 0 0 0 ${color}` }, { boxShadow: `0 0 0 4px ${color}`, offset: 0.3 }, { boxShadow: '0 0 0 0 transparent' }], { duration })?.finished.catch(() => undefined);\n\nexport const FORM_FX: EffectDefinition[] = [\n {\n name: 'field-shake',\n kind: 'attention',\n description: 'Shakes the element sideways with a red outline flash — the \"invalid\" cue (`distance`, `color`).',\n defaults: { distance: 8, color: 'rgba(225,29,72,.55)', duration: 450 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const r = ring(el, o.color, ctx, o.duration + 200);\n if (ctx.reduced) return r;\n return ctx.animate(el, shakeFrames(Number(o.distance) || 8), { duration: o.duration, easing: 'ease-out' })?.finished.catch(() => undefined);\n },\n },\n {\n name: 'field-success',\n kind: 'attention',\n description: 'A green glow pulses round the element and a check badge pops on its corner (`color`).',\n defaults: { color: 'rgba(22,163,74,.5)', duration: 900 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const r = ring(el, o.color, ctx, o.duration);\n if (ctx.reduced) return r;\n if (getComputedStyle(el).position === 'static') {\n const prev = el.style.position;\n el.style.position = 'relative';\n ctx.onCleanup(() => (el.style.position = prev));\n }\n const b = document.createElement('span');\n b.setAttribute('aria-hidden', 'true');\n b.textContent = '✓';\n Object.assign(b.style, { position: 'absolute', right: '-6px', top: '-6px', width: '18px', height: '18px', borderRadius: '50%', background: '#16a34a', color: '#fff', font: '700 12px/18px system-ui,sans-serif', textAlign: 'center', pointerEvents: 'none' });\n el.appendChild(b);\n const end = () => b.remove();\n ctx.onCleanup(end);\n const a = ctx.animate(b, [{ transform: 'scale(0)', opacity: 0 }, { transform: 'scale(1.25)', opacity: 1, offset: 0.25 }, { transform: 'scale(1)', opacity: 1, offset: 0.4 }, { transform: 'scale(1)', opacity: 1, offset: 0.85 }, { transform: 'scale(.6)', opacity: 0 }], { duration: o.duration + 500, easing: 'ease-out' });\n return a ? a.finished.then(end, end) : (end(), undefined);\n },\n },\n {\n name: 'label-float',\n kind: 'enter',\n description: 'Every `label` (or `[data-label]`) inside rises and settles one after another, like floating labels (`stagger`).',\n defaults: { stagger: 80, duration: 450 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const marked = Array.from(el.querySelectorAll<HTMLElement>('[data-label]'));\n const labels = marked.length ? marked : Array.from(el.querySelectorAll<HTMLElement>('label'));\n const items = labels.length ? labels : [el];\n return Promise.all(\n items.map((l, i) =>\n ctx.reduced\n ? fade(l, ctx)\n : ctx.animate(l, [{ transform: 'translateY(14px) scale(1.12)', opacity: 0, transformOrigin: '0 50%' }, { transform: 'translateY(-3px) scale(.96)', opacity: 1, offset: 0.7 }, { transform: 'none', opacity: 1 }], { duration: o.duration, delay: i * o.stagger, easing: 'cubic-bezier(.2,.8,.2,1)', fill: 'backwards' })?.finished.catch(() => undefined)\n )\n ).then(() => undefined);\n },\n },\n {\n name: 'form-cascade',\n kind: 'enter',\n description: \"The element's children (fields, buttons) slide in one after another (`stagger`, `distance`).\",\n defaults: { stagger: 70, distance: 18, duration: 500 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const kids = Array.from(el.children) as HTMLElement[];\n const items = kids.length ? kids : [el];\n return Promise.all(\n items.map((k, i) =>\n ctx.reduced\n ? fade(k, ctx)\n : ctx.animate(k, [{ transform: `translateY(${Number(o.distance) || 18}px)`, opacity: 0 }, { transform: 'none', opacity: 1 }], { duration: o.duration, delay: i * o.stagger, easing: 'cubic-bezier(.2,.8,.2,1)', fill: 'backwards' })?.finished.catch(() => undefined)\n )\n ).then(() => undefined);\n },\n },\n];\n\n/** Register field-shake, field-success, label-float and form-cascade (7.7). */\nexport function registerFormPack(): void {\n registerEffects(FORM_FX);\n}\n"],"names":["registerEffects"],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ type MotionSensitivity = 'full' | 'gentle' | 'minimal' | 'static';
2
+ type Cleanup = () => void;
3
+
4
+ /**
5
+ * 5.0 — unified plugin-style effect registration. Every effect (built-in or
6
+ * yours) is a plain object registered once and played the same way:
7
+ * `playEffect(el, name)`, `bindEffect(el, name, { trigger })` or
8
+ * `<usa-fx effect="name" trigger="click">`. Effects get a context that
9
+ * already applies reduced motion, motion sensitivity, intensity and the
10
+ * animation budget.
11
+ */
12
+
13
+ declare const EFFECT_KINDS: readonly ["enter", "exit", "attention", "click", "hover", "card", "loop", "page", "background", "text", "cursor", "scroll"];
14
+ type EffectKind = (typeof EFFECT_KINDS)[number];
15
+ interface EffectContext {
16
+ /** Reduced motion applies (OS setting, `minimal` / `static` sensitivity). */
17
+ readonly reduced: boolean;
18
+ readonly sensitivity: MotionSensitivity;
19
+ /** The triggering event (pointer position for click effects), if any. */
20
+ readonly event?: Event;
21
+ /** `el.animate()` with the library's motion rules (may return `null`). */
22
+ animate(el: Element, keyframes: Keyframe[], options: KeyframeAnimationOptions): Animation | null;
23
+ /** Register teardown for long-running effects (loops, listeners). */
24
+ onCleanup(fn: Cleanup): void;
25
+ }
26
+ interface EffectDefinition<O extends Record<string, unknown> = Record<string, any>> {
27
+ /** Unique, kebab-case. */
28
+ name: string;
29
+ kind: EffectKind;
30
+ /** One line for docs and the gallery. */
31
+ description?: string;
32
+ /** Option defaults (merged under the caller's options). */
33
+ defaults?: Partial<O>;
34
+ /**
35
+ * Under reduced motion: `'skip'` (do nothing — default for loop, background
36
+ * and cursor effects) or `'run'` (run with `ctx.reduced === true`, the
37
+ * effect degrades itself — default for everything else).
38
+ */
39
+ reduced?: 'skip' | 'run';
40
+ /** Play the effect. Return an Animation / Promise to be awaited, or a cleanup. */
41
+ run(el: HTMLElement, options: O, ctx: EffectContext): void | Cleanup | Animation | null | Promise<unknown>;
42
+ }
43
+
44
+ /**
45
+ * 7.7 — Form motion (`motionary/fx/form`, also `motionary/components/fx-form`):
46
+ *
47
+ * - `field-shake` (attention) — the element shakes sideways with a red
48
+ * outline flash, the classic "invalid" cue (`distance`, `color`).
49
+ * - `field-success` (attention) — a green glow pulses round the element and a
50
+ * small check badge pops on its corner (`color`).
51
+ * - `label-float` (enter) — every `label` (or `[data-label]`) inside rises and
52
+ * settles, one after another, like floating labels (`stagger`).
53
+ * - `form-cascade` (enter) — the element's children (fields, buttons) slide
54
+ * in one after another (`stagger`, `distance`).
55
+ *
56
+ * Reduced motion: field-shake / field-success only flash the outline colour,
57
+ * label-float and form-cascade fade in.
58
+ */
59
+
60
+ /** Decaying sideways shake keyframes (7.7). */
61
+ declare function shakeFrames(distance?: number, steps?: number): Keyframe[];
62
+ declare const FORM_FX: EffectDefinition[];
63
+ /** Register field-shake, field-success, label-float and form-cascade (7.7). */
64
+ declare function registerFormPack(): void;
65
+
66
+ export { FORM_FX, registerFormPack, shakeFrames };
@@ -0,0 +1,66 @@
1
+ type MotionSensitivity = 'full' | 'gentle' | 'minimal' | 'static';
2
+ type Cleanup = () => void;
3
+
4
+ /**
5
+ * 5.0 — unified plugin-style effect registration. Every effect (built-in or
6
+ * yours) is a plain object registered once and played the same way:
7
+ * `playEffect(el, name)`, `bindEffect(el, name, { trigger })` or
8
+ * `<usa-fx effect="name" trigger="click">`. Effects get a context that
9
+ * already applies reduced motion, motion sensitivity, intensity and the
10
+ * animation budget.
11
+ */
12
+
13
+ declare const EFFECT_KINDS: readonly ["enter", "exit", "attention", "click", "hover", "card", "loop", "page", "background", "text", "cursor", "scroll"];
14
+ type EffectKind = (typeof EFFECT_KINDS)[number];
15
+ interface EffectContext {
16
+ /** Reduced motion applies (OS setting, `minimal` / `static` sensitivity). */
17
+ readonly reduced: boolean;
18
+ readonly sensitivity: MotionSensitivity;
19
+ /** The triggering event (pointer position for click effects), if any. */
20
+ readonly event?: Event;
21
+ /** `el.animate()` with the library's motion rules (may return `null`). */
22
+ animate(el: Element, keyframes: Keyframe[], options: KeyframeAnimationOptions): Animation | null;
23
+ /** Register teardown for long-running effects (loops, listeners). */
24
+ onCleanup(fn: Cleanup): void;
25
+ }
26
+ interface EffectDefinition<O extends Record<string, unknown> = Record<string, any>> {
27
+ /** Unique, kebab-case. */
28
+ name: string;
29
+ kind: EffectKind;
30
+ /** One line for docs and the gallery. */
31
+ description?: string;
32
+ /** Option defaults (merged under the caller's options). */
33
+ defaults?: Partial<O>;
34
+ /**
35
+ * Under reduced motion: `'skip'` (do nothing — default for loop, background
36
+ * and cursor effects) or `'run'` (run with `ctx.reduced === true`, the
37
+ * effect degrades itself — default for everything else).
38
+ */
39
+ reduced?: 'skip' | 'run';
40
+ /** Play the effect. Return an Animation / Promise to be awaited, or a cleanup. */
41
+ run(el: HTMLElement, options: O, ctx: EffectContext): void | Cleanup | Animation | null | Promise<unknown>;
42
+ }
43
+
44
+ /**
45
+ * 7.7 — Form motion (`motionary/fx/form`, also `motionary/components/fx-form`):
46
+ *
47
+ * - `field-shake` (attention) — the element shakes sideways with a red
48
+ * outline flash, the classic "invalid" cue (`distance`, `color`).
49
+ * - `field-success` (attention) — a green glow pulses round the element and a
50
+ * small check badge pops on its corner (`color`).
51
+ * - `label-float` (enter) — every `label` (or `[data-label]`) inside rises and
52
+ * settles, one after another, like floating labels (`stagger`).
53
+ * - `form-cascade` (enter) — the element's children (fields, buttons) slide
54
+ * in one after another (`stagger`, `distance`).
55
+ *
56
+ * Reduced motion: field-shake / field-success only flash the outline colour,
57
+ * label-float and form-cascade fade in.
58
+ */
59
+
60
+ /** Decaying sideways shake keyframes (7.7). */
61
+ declare function shakeFrames(distance?: number, steps?: number): Keyframe[];
62
+ declare const FORM_FX: EffectDefinition[];
63
+ /** Register field-shake, field-success, label-float and form-cascade (7.7). */
64
+ declare function registerFormPack(): void;
65
+
66
+ export { FORM_FX, registerFormPack, shakeFrames };
@@ -0,0 +1,88 @@
1
+ import { registerEffects } from '../chunks/registry-CyKExAmE.js';
2
+ import '../chunks/base-2-yYc93C.js';
3
+
4
+ /** Decaying sideways shake keyframes (7.7). */
5
+ function shakeFrames(distance = 8, steps = 5) {
6
+ const f = [{ transform: 'none' }];
7
+ for (let i = 0; i < steps; i++) {
8
+ const d = Math.round(distance * (1 - i / steps) * 10) / 10;
9
+ f.push({ transform: `translateX(${i % 2 ? d : -d}px)` });
10
+ }
11
+ f.push({ transform: 'none' });
12
+ return f;
13
+ }
14
+ const fade = (el, ctx) => ctx.animate(el, [{ opacity: 0 }, { opacity: 1 }], { duration: 250 })?.finished.catch(() => undefined);
15
+ const ring = (el, color, ctx, duration) => ctx.animate(el, [{ boxShadow: `0 0 0 0 ${color}` }, { boxShadow: `0 0 0 4px ${color}`, offset: 0.3 }, { boxShadow: '0 0 0 0 transparent' }], { duration })?.finished.catch(() => undefined);
16
+ const FORM_FX = [
17
+ {
18
+ name: 'field-shake',
19
+ kind: 'attention',
20
+ description: 'Shakes the element sideways with a red outline flash — the "invalid" cue (`distance`, `color`).',
21
+ defaults: { distance: 8, color: 'rgba(225,29,72,.55)', duration: 450 },
22
+ run: (el, o, ctx) => {
23
+ const r = ring(el, o.color, ctx, o.duration + 200);
24
+ if (ctx.reduced)
25
+ return r;
26
+ return ctx.animate(el, shakeFrames(Number(o.distance) || 8), { duration: o.duration, easing: 'ease-out' })?.finished.catch(() => undefined);
27
+ },
28
+ },
29
+ {
30
+ name: 'field-success',
31
+ kind: 'attention',
32
+ description: 'A green glow pulses round the element and a check badge pops on its corner (`color`).',
33
+ defaults: { color: 'rgba(22,163,74,.5)', duration: 900 },
34
+ run: (el, o, ctx) => {
35
+ const r = ring(el, o.color, ctx, o.duration);
36
+ if (ctx.reduced)
37
+ return r;
38
+ if (getComputedStyle(el).position === 'static') {
39
+ const prev = el.style.position;
40
+ el.style.position = 'relative';
41
+ ctx.onCleanup(() => (el.style.position = prev));
42
+ }
43
+ const b = document.createElement('span');
44
+ b.setAttribute('aria-hidden', 'true');
45
+ b.textContent = '✓';
46
+ Object.assign(b.style, { position: 'absolute', right: '-6px', top: '-6px', width: '18px', height: '18px', borderRadius: '50%', background: '#16a34a', color: '#fff', font: '700 12px/18px system-ui,sans-serif', textAlign: 'center', pointerEvents: 'none' });
47
+ el.appendChild(b);
48
+ const end = () => b.remove();
49
+ ctx.onCleanup(end);
50
+ const a = ctx.animate(b, [{ transform: 'scale(0)', opacity: 0 }, { transform: 'scale(1.25)', opacity: 1, offset: 0.25 }, { transform: 'scale(1)', opacity: 1, offset: 0.4 }, { transform: 'scale(1)', opacity: 1, offset: 0.85 }, { transform: 'scale(.6)', opacity: 0 }], { duration: o.duration + 500, easing: 'ease-out' });
51
+ return a ? a.finished.then(end, end) : (end(), undefined);
52
+ },
53
+ },
54
+ {
55
+ name: 'label-float',
56
+ kind: 'enter',
57
+ description: 'Every `label` (or `[data-label]`) inside rises and settles one after another, like floating labels (`stagger`).',
58
+ defaults: { stagger: 80, duration: 450 },
59
+ run: (el, o, ctx) => {
60
+ const marked = Array.from(el.querySelectorAll('[data-label]'));
61
+ const labels = marked.length ? marked : Array.from(el.querySelectorAll('label'));
62
+ const items = labels.length ? labels : [el];
63
+ return Promise.all(items.map((l, i) => ctx.reduced
64
+ ? fade(l, ctx)
65
+ : ctx.animate(l, [{ transform: 'translateY(14px) scale(1.12)', opacity: 0, transformOrigin: '0 50%' }, { transform: 'translateY(-3px) scale(.96)', opacity: 1, offset: 0.7 }, { transform: 'none', opacity: 1 }], { duration: o.duration, delay: i * o.stagger, easing: 'cubic-bezier(.2,.8,.2,1)', fill: 'backwards' })?.finished.catch(() => undefined))).then(() => undefined);
66
+ },
67
+ },
68
+ {
69
+ name: 'form-cascade',
70
+ kind: 'enter',
71
+ description: "The element's children (fields, buttons) slide in one after another (`stagger`, `distance`).",
72
+ defaults: { stagger: 70, distance: 18, duration: 500 },
73
+ run: (el, o, ctx) => {
74
+ const kids = Array.from(el.children);
75
+ const items = kids.length ? kids : [el];
76
+ return Promise.all(items.map((k, i) => ctx.reduced
77
+ ? fade(k, ctx)
78
+ : ctx.animate(k, [{ transform: `translateY(${Number(o.distance) || 18}px)`, opacity: 0 }, { transform: 'none', opacity: 1 }], { duration: o.duration, delay: i * o.stagger, easing: 'cubic-bezier(.2,.8,.2,1)', fill: 'backwards' })?.finished.catch(() => undefined))).then(() => undefined);
79
+ },
80
+ },
81
+ ];
82
+ /** Register field-shake, field-success, label-float and form-cascade (7.7). */
83
+ function registerFormPack() {
84
+ registerEffects(FORM_FX);
85
+ }
86
+
87
+ export { FORM_FX, registerFormPack, shakeFrames };
88
+ //# sourceMappingURL=fx-form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fx-form.js","sources":["../../src/components/fx2/form.ts"],"sourcesContent":["/**\n * 7.7 — Form motion (`motionary/fx/form`, also `motionary/components/fx-form`):\n *\n * - `field-shake` (attention) — the element shakes sideways with a red\n * outline flash, the classic \"invalid\" cue (`distance`, `color`).\n * - `field-success` (attention) — a green glow pulses round the element and a\n * small check badge pops on its corner (`color`).\n * - `label-float` (enter) — every `label` (or `[data-label]`) inside rises and\n * settles, one after another, like floating labels (`stagger`).\n * - `form-cascade` (enter) — the element's children (fields, buttons) slide\n * in one after another (`stagger`, `distance`).\n *\n * Reduced motion: field-shake / field-success only flash the outline colour,\n * label-float and form-cascade fade in.\n */\nimport type { EffectContext, EffectDefinition } from '../fx/registry';\nimport { registerEffects } from '../fx/registry';\n\n/** Decaying sideways shake keyframes (7.7). */\nexport function shakeFrames(distance = 8, steps = 5): Keyframe[] {\n const f: Keyframe[] = [{ transform: 'none' }];\n for (let i = 0; i < steps; i++) {\n const d = Math.round(distance * (1 - i / steps) * 10) / 10;\n f.push({ transform: `translateX(${i % 2 ? d : -d}px)` });\n }\n f.push({ transform: 'none' });\n return f;\n}\n\nconst fade = (el: HTMLElement, ctx: EffectContext) => ctx.animate(el, [{ opacity: 0 }, { opacity: 1 }], { duration: 250 })?.finished.catch(() => undefined);\nconst ring = (el: HTMLElement, color: string, ctx: EffectContext, duration: number) =>\n ctx.animate(el, [{ boxShadow: `0 0 0 0 ${color}` }, { boxShadow: `0 0 0 4px ${color}`, offset: 0.3 }, { boxShadow: '0 0 0 0 transparent' }], { duration })?.finished.catch(() => undefined);\n\nexport const FORM_FX: EffectDefinition[] = [\n {\n name: 'field-shake',\n kind: 'attention',\n description: 'Shakes the element sideways with a red outline flash — the \"invalid\" cue (`distance`, `color`).',\n defaults: { distance: 8, color: 'rgba(225,29,72,.55)', duration: 450 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const r = ring(el, o.color, ctx, o.duration + 200);\n if (ctx.reduced) return r;\n return ctx.animate(el, shakeFrames(Number(o.distance) || 8), { duration: o.duration, easing: 'ease-out' })?.finished.catch(() => undefined);\n },\n },\n {\n name: 'field-success',\n kind: 'attention',\n description: 'A green glow pulses round the element and a check badge pops on its corner (`color`).',\n defaults: { color: 'rgba(22,163,74,.5)', duration: 900 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const r = ring(el, o.color, ctx, o.duration);\n if (ctx.reduced) return r;\n if (getComputedStyle(el).position === 'static') {\n const prev = el.style.position;\n el.style.position = 'relative';\n ctx.onCleanup(() => (el.style.position = prev));\n }\n const b = document.createElement('span');\n b.setAttribute('aria-hidden', 'true');\n b.textContent = '✓';\n Object.assign(b.style, { position: 'absolute', right: '-6px', top: '-6px', width: '18px', height: '18px', borderRadius: '50%', background: '#16a34a', color: '#fff', font: '700 12px/18px system-ui,sans-serif', textAlign: 'center', pointerEvents: 'none' });\n el.appendChild(b);\n const end = () => b.remove();\n ctx.onCleanup(end);\n const a = ctx.animate(b, [{ transform: 'scale(0)', opacity: 0 }, { transform: 'scale(1.25)', opacity: 1, offset: 0.25 }, { transform: 'scale(1)', opacity: 1, offset: 0.4 }, { transform: 'scale(1)', opacity: 1, offset: 0.85 }, { transform: 'scale(.6)', opacity: 0 }], { duration: o.duration + 500, easing: 'ease-out' });\n return a ? a.finished.then(end, end) : (end(), undefined);\n },\n },\n {\n name: 'label-float',\n kind: 'enter',\n description: 'Every `label` (or `[data-label]`) inside rises and settles one after another, like floating labels (`stagger`).',\n defaults: { stagger: 80, duration: 450 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const marked = Array.from(el.querySelectorAll<HTMLElement>('[data-label]'));\n const labels = marked.length ? marked : Array.from(el.querySelectorAll<HTMLElement>('label'));\n const items = labels.length ? labels : [el];\n return Promise.all(\n items.map((l, i) =>\n ctx.reduced\n ? fade(l, ctx)\n : ctx.animate(l, [{ transform: 'translateY(14px) scale(1.12)', opacity: 0, transformOrigin: '0 50%' }, { transform: 'translateY(-3px) scale(.96)', opacity: 1, offset: 0.7 }, { transform: 'none', opacity: 1 }], { duration: o.duration, delay: i * o.stagger, easing: 'cubic-bezier(.2,.8,.2,1)', fill: 'backwards' })?.finished.catch(() => undefined)\n )\n ).then(() => undefined);\n },\n },\n {\n name: 'form-cascade',\n kind: 'enter',\n description: \"The element's children (fields, buttons) slide in one after another (`stagger`, `distance`).\",\n defaults: { stagger: 70, distance: 18, duration: 500 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const kids = Array.from(el.children) as HTMLElement[];\n const items = kids.length ? kids : [el];\n return Promise.all(\n items.map((k, i) =>\n ctx.reduced\n ? fade(k, ctx)\n : ctx.animate(k, [{ transform: `translateY(${Number(o.distance) || 18}px)`, opacity: 0 }, { transform: 'none', opacity: 1 }], { duration: o.duration, delay: i * o.stagger, easing: 'cubic-bezier(.2,.8,.2,1)', fill: 'backwards' })?.finished.catch(() => undefined)\n )\n ).then(() => undefined);\n },\n },\n];\n\n/** Register field-shake, field-success, label-float and form-cascade (7.7). */\nexport function registerFormPack(): void {\n registerEffects(FORM_FX);\n}\n"],"names":[],"mappings":";;;AAkBA;AACM,SAAU,WAAW,CAAC,QAAQ,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAA;IACjD,MAAM,CAAC,GAAe,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AAC7C,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE;QAC9B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE;QAC1D,CAAC,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,CAAA,WAAA,EAAc,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA,GAAA,CAAK,EAAE,CAAC;IAC1D;IACA,CAAC,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AAC7B,IAAA,OAAO,CAAC;AACV;AAEA,MAAM,IAAI,GAAG,CAAC,EAAe,EAAE,GAAkB,KAAK,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,SAAS,CAAC;AAC3J,MAAM,IAAI,GAAG,CAAC,EAAe,EAAE,KAAa,EAAE,GAAkB,EAAE,QAAgB,KAChF,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,CAAA,QAAA,EAAW,KAAK,CAAA,CAAE,EAAE,EAAE,EAAE,SAAS,EAAE,CAAA,UAAA,EAAa,KAAK,CAAA,CAAE,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,qBAAqB,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,SAAS,CAAC;AAEtL,MAAM,OAAO,GAAuB;AACzC,IAAA;AACE,QAAA,IAAI,EAAE,aAAa;AACnB,QAAA,IAAI,EAAE,WAAW;AACjB,QAAA,WAAW,EAAE,iGAAiG;AAC9G,QAAA,QAAQ,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,QAAQ,EAAE,GAAG,EAAE;QACtE,GAAG,EAAE,CAAC,EAAe,EAAE,CAAM,EAAE,GAAkB,KAAI;AACnD,YAAA,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,QAAQ,GAAG,GAAG,CAAC;YAClD,IAAI,GAAG,CAAC,OAAO;AAAE,gBAAA,OAAO,CAAC;AACzB,YAAA,OAAO,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,SAAS,CAAC;QAC7I,CAAC;AACF,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,eAAe;AACrB,QAAA,IAAI,EAAE,WAAW;AACjB,QAAA,WAAW,EAAE,uFAAuF;QACpG,QAAQ,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,GAAG,EAAE;QACxD,GAAG,EAAE,CAAC,EAAe,EAAE,CAAM,EAAE,GAAkB,KAAI;AACnD,YAAA,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC;YAC5C,IAAI,GAAG,CAAC,OAAO;AAAE,gBAAA,OAAO,CAAC;YACzB,IAAI,gBAAgB,CAAC,EAAE,CAAC,CAAC,QAAQ,KAAK,QAAQ,EAAE;AAC9C,gBAAA,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ;AAC9B,gBAAA,EAAE,CAAC,KAAK,CAAC,QAAQ,GAAG,UAAU;AAC9B,gBAAA,GAAG,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;YACjD;YACA,MAAM,CAAC,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC;AACxC,YAAA,CAAC,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC;AACrC,YAAA,CAAC,CAAC,WAAW,GAAG,GAAG;YACnB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,oCAAoC,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC;AAC9P,YAAA,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,MAAM,EAAE;AAC5B,YAAA,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC;AAClB,YAAA,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,GAAG,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;YAC9T,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,GAAG,EAAE,EAAE,SAAS,CAAC;QAC3D,CAAC;AACF,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,aAAa;AACnB,QAAA,IAAI,EAAE,OAAO;AACb,QAAA,WAAW,EAAE,iHAAiH;QAC9H,QAAQ,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE;QACxC,GAAG,EAAE,CAAC,EAAe,EAAE,CAAM,EAAE,GAAkB,KAAI;AACnD,YAAA,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAc,cAAc,CAAC,CAAC;YAC3E,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAc,OAAO,CAAC,CAAC;AAC7F,YAAA,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC;AAC3C,YAAA,OAAO,OAAO,CAAC,GAAG,CAChB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KACb,GAAG,CAAC;AACF,kBAAE,IAAI,CAAC,CAAC,EAAE,GAAG;AACb,kBAAE,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,8BAA8B,EAAE,OAAO,EAAE,CAAC,EAAE,eAAe,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,6BAA6B,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,0BAA0B,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,SAAS,CAAC,CAC5V,CACF,CAAC,IAAI,CAAC,MAAM,SAAS,CAAC;QACzB,CAAC;AACF,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,cAAc;AACpB,QAAA,IAAI,EAAE,OAAO;AACb,QAAA,WAAW,EAAE,8FAA8F;AAC3G,QAAA,QAAQ,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE;QACtD,GAAG,EAAE,CAAC,EAAe,EAAE,CAAM,EAAE,GAAkB,KAAI;YACnD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAkB;AACrD,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC;AACvC,YAAA,OAAO,OAAO,CAAC,GAAG,CAChB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KACb,GAAG,CAAC;AACF,kBAAE,IAAI,CAAC,CAAC,EAAE,GAAG;AACb,kBAAE,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,CAAA,WAAA,EAAc,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAA,GAAA,CAAK,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,0BAA0B,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,SAAS,CAAC,CACxQ,CACF,CAAC,IAAI,CAAC,MAAM,SAAS,CAAC;QACzB,CAAC;AACF,KAAA;;AAGH;SACgB,gBAAgB,GAAA;IAC9B,eAAe,CAAC,OAAO,CAAC;AAC1B;;;;"}
@@ -0,0 +1,125 @@
1
+ 'use strict';
2
+
3
+ var registry = require('../chunks/registry-DehBVRDV.cjs');
4
+ require('../chunks/base-BaQV-2ha.cjs');
5
+
6
+ /** Length of a polyline through `points` (7.6). */
7
+ function routeLength(points) {
8
+ let len = 0;
9
+ for (let i = 1; i < points.length; i++)
10
+ len += Math.hypot(points[i].x - points[i - 1].x, points[i].y - points[i - 1].y);
11
+ return Math.round(len * 100) / 100;
12
+ }
13
+ const lengthOf = (p) => {
14
+ try {
15
+ const n = typeof p.getTotalLength === 'function' ? p.getTotalLength() : 0;
16
+ if (n > 0)
17
+ return n;
18
+ }
19
+ catch {
20
+ /* not rendered */
21
+ }
22
+ const pts = (p.getAttribute('points') || '')
23
+ .trim()
24
+ .split(/[\s,]+/)
25
+ .map(Number);
26
+ const xy = [];
27
+ for (let i = 0; i + 1 < pts.length; i += 2)
28
+ xy.push({ x: pts[i], y: pts[i + 1] });
29
+ return routeLength(xy) || 100;
30
+ };
31
+ const fade = (el, ctx) => ctx.animate(el, [{ opacity: 0 }, { opacity: 1 }], { duration: 250 })?.finished.catch(() => undefined);
32
+ const GEO_FX = [
33
+ {
34
+ name: 'route-draw',
35
+ kind: 'enter',
36
+ description: 'SVG paths / polylines inside the element (or `[data-route]`) draw themselves along their length, one after another (`duration`, `stagger`).',
37
+ defaults: { duration: 1200, stagger: 250 },
38
+ run: (el, o, ctx) => {
39
+ const marked = Array.from(el.querySelectorAll('[data-route]'));
40
+ const paths = marked.length ? marked : Array.from(el.querySelectorAll('path, polyline'));
41
+ if (ctx.reduced || !paths.length)
42
+ return;
43
+ const runs = paths.map((p, i) => {
44
+ const len = lengthOf(p);
45
+ const prev = { a: p.style.strokeDasharray, o: p.style.strokeDashoffset };
46
+ p.style.strokeDasharray = `${len}`;
47
+ ctx.onCleanup(() => {
48
+ p.style.strokeDasharray = prev.a;
49
+ p.style.strokeDashoffset = prev.o;
50
+ });
51
+ return ctx.animate(p, [{ strokeDashoffset: `${len}` }, { strokeDashoffset: '0' }], { duration: o.duration, delay: i * o.stagger, easing: 'ease-in-out', fill: 'backwards' })?.finished.catch(() => undefined);
52
+ });
53
+ return Promise.all(runs).then(() => undefined);
54
+ },
55
+ },
56
+ {
57
+ name: 'marker-pulse',
58
+ kind: 'attention',
59
+ description: 'Rings expand out of the element like a location beacon (`color`, `rings`).',
60
+ defaults: { color: '#ef4444', rings: 2, duration: 1100 },
61
+ run: (el, o, ctx) => {
62
+ if (ctx.reduced)
63
+ return;
64
+ if (getComputedStyle(el).position === 'static') {
65
+ const prev = el.style.position;
66
+ el.style.position = 'relative';
67
+ ctx.onCleanup(() => (el.style.position = prev));
68
+ }
69
+ const all = Array.from({ length: Math.max(1, Math.min(4, o.rings)) }, (_, i) => {
70
+ const ring = document.createElement('span');
71
+ ring.setAttribute('aria-hidden', 'true');
72
+ Object.assign(ring.style, { position: 'absolute', left: '50%', top: '50%', width: '100%', aspectRatio: '1', borderRadius: '50%', border: `2px solid ${o.color}`, pointerEvents: 'none', translate: '-50% -50%' });
73
+ el.appendChild(ring);
74
+ const a = ctx.animate(ring, [{ transform: 'scale(.4)', opacity: 0.9 }, { transform: 'scale(2.2)', opacity: 0 }], { duration: o.duration, delay: i * (o.duration / 3), easing: 'ease-out', fill: 'backwards' });
75
+ const end = () => ring.remove();
76
+ ctx.onCleanup(end);
77
+ return a ? a.finished.then(end, end) : (end(), undefined);
78
+ });
79
+ return Promise.all(all).then(() => undefined);
80
+ },
81
+ },
82
+ {
83
+ name: 'pin-drop',
84
+ kind: 'enter',
85
+ description: 'The element drops onto its spot with a squash and a landing shadow (`height`).',
86
+ defaults: { height: 80, duration: 700 },
87
+ run: (el, o, ctx) => {
88
+ if (ctx.reduced)
89
+ return fade(el, ctx);
90
+ const h = Math.max(10, Number(o.height) || 80);
91
+ ctx.animate(el, [{ filter: 'drop-shadow(0 0 0 rgba(0,0,0,0))' }, { filter: 'drop-shadow(0 6px 4px rgba(0,0,0,.35))', offset: 0.6 }, { filter: 'drop-shadow(0 2px 2px rgba(0,0,0,.2))' }], { duration: o.duration });
92
+ return ctx
93
+ .animate(el, [
94
+ { transform: `translateY(${-h}px)`, opacity: 0, easing: 'cubic-bezier(.5,0,.9,.5)' },
95
+ { transform: 'translateY(0) scale(1.08, .88)', opacity: 1, offset: 0.6 },
96
+ { transform: 'translateY(-8px) scale(.97, 1.04)', offset: 0.8 },
97
+ { transform: 'none', opacity: 1 },
98
+ ], { duration: o.duration, fill: 'backwards' })
99
+ ?.finished.catch(() => undefined);
100
+ },
101
+ },
102
+ {
103
+ name: 'globe-spin',
104
+ kind: 'enter',
105
+ description: 'The element turns in like a globe coming round — rotateY with perspective (`turns`).',
106
+ defaults: { turns: 0.5, duration: 1100 },
107
+ run: (el, o, ctx) => {
108
+ if (ctx.reduced)
109
+ return fade(el, ctx);
110
+ const deg = Math.round(360 * (Number(o.turns) || 0.5));
111
+ return ctx
112
+ .animate(el, [{ transform: `perspective(600px) rotateY(${-deg}deg) scale(.8)`, opacity: 0 }, { transform: 'perspective(600px) rotateY(0) scale(1)', opacity: 1 }], { duration: o.duration, easing: 'cubic-bezier(.2,.8,.2,1)', fill: 'backwards' })
113
+ ?.finished.catch(() => undefined);
114
+ },
115
+ },
116
+ ];
117
+ /** Register route-draw, marker-pulse, pin-drop and globe-spin (7.6). */
118
+ function registerGeoPack() {
119
+ registry.registerEffects(GEO_FX);
120
+ }
121
+
122
+ exports.GEO_FX = GEO_FX;
123
+ exports.registerGeoPack = registerGeoPack;
124
+ exports.routeLength = routeLength;
125
+ //# sourceMappingURL=fx-geo.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fx-geo.cjs","sources":["../../src/components/fx2/geo.ts"],"sourcesContent":["/**\n * 7.6 — Maps & geo motion (`motionary/fx/geo`, also `motionary/components/fx-geo`):\n *\n * - `route-draw` (enter) — every SVG `path` / `polyline` inside the element\n * (or `[data-route]` only, when present) draws itself along its length,\n * one after another (`duration`, `stagger`).\n * - `marker-pulse` (attention) — rings expand out of the element like a\n * location beacon (`color`, `rings`).\n * - `pin-drop` (enter) — the element drops onto its spot with a squash and a\n * landing shadow (`height`).\n * - `globe-spin` (enter) — the element turns in like a globe coming round\n * (rotateY with perspective) (`turns`).\n *\n * Reduced motion: route-draw shows the routes, marker-pulse does nothing,\n * pin-drop and globe-spin fade in.\n */\nimport type { EffectContext, EffectDefinition } from '../fx/registry';\nimport { registerEffects } from '../fx/registry';\n\n/** Length of a polyline through `points` (7.6). */\nexport function routeLength(points: { x: number; y: number }[]): number {\n let len = 0;\n for (let i = 1; i < points.length; i++) len += Math.hypot(points[i].x - points[i - 1].x, points[i].y - points[i - 1].y);\n return Math.round(len * 100) / 100;\n}\n\nconst lengthOf = (p: SVGGeometryElement): number => {\n try {\n const n = typeof p.getTotalLength === 'function' ? p.getTotalLength() : 0;\n if (n > 0) return n;\n } catch {\n /* not rendered */\n }\n const pts = (p.getAttribute('points') || '')\n .trim()\n .split(/[\\s,]+/)\n .map(Number);\n const xy = [];\n for (let i = 0; i + 1 < pts.length; i += 2) xy.push({ x: pts[i], y: pts[i + 1] });\n return routeLength(xy) || 100;\n};\n\nconst fade = (el: HTMLElement, ctx: EffectContext) => ctx.animate(el, [{ opacity: 0 }, { opacity: 1 }], { duration: 250 })?.finished.catch(() => undefined);\n\nexport const GEO_FX: EffectDefinition[] = [\n {\n name: 'route-draw',\n kind: 'enter',\n description: 'SVG paths / polylines inside the element (or `[data-route]`) draw themselves along their length, one after another (`duration`, `stagger`).',\n defaults: { duration: 1200, stagger: 250 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const marked = Array.from(el.querySelectorAll<SVGGeometryElement>('[data-route]'));\n const paths = marked.length ? marked : Array.from(el.querySelectorAll<SVGGeometryElement>('path, polyline'));\n if (ctx.reduced || !paths.length) return;\n const runs = paths.map((p, i) => {\n const len = lengthOf(p);\n const prev = { a: p.style.strokeDasharray, o: p.style.strokeDashoffset };\n p.style.strokeDasharray = `${len}`;\n ctx.onCleanup(() => {\n p.style.strokeDasharray = prev.a;\n p.style.strokeDashoffset = prev.o;\n });\n return ctx.animate(p, [{ strokeDashoffset: `${len}` }, { strokeDashoffset: '0' }], { duration: o.duration, delay: i * o.stagger, easing: 'ease-in-out', fill: 'backwards' })?.finished.catch(() => undefined);\n });\n return Promise.all(runs).then(() => undefined);\n },\n },\n {\n name: 'marker-pulse',\n kind: 'attention',\n description: 'Rings expand out of the element like a location beacon (`color`, `rings`).',\n defaults: { color: '#ef4444', rings: 2, duration: 1100 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n if (ctx.reduced) return;\n if (getComputedStyle(el).position === 'static') {\n const prev = el.style.position;\n el.style.position = 'relative';\n ctx.onCleanup(() => (el.style.position = prev));\n }\n const all = Array.from({ length: Math.max(1, Math.min(4, o.rings)) }, (_, i) => {\n const ring = document.createElement('span');\n ring.setAttribute('aria-hidden', 'true');\n Object.assign(ring.style, { position: 'absolute', left: '50%', top: '50%', width: '100%', aspectRatio: '1', borderRadius: '50%', border: `2px solid ${o.color}`, pointerEvents: 'none', translate: '-50% -50%' });\n el.appendChild(ring);\n const a = ctx.animate(ring, [{ transform: 'scale(.4)', opacity: 0.9 }, { transform: 'scale(2.2)', opacity: 0 }], { duration: o.duration, delay: i * (o.duration / 3), easing: 'ease-out', fill: 'backwards' });\n const end = () => ring.remove();\n ctx.onCleanup(end);\n return a ? a.finished.then(end, end) : (end(), undefined);\n });\n return Promise.all(all).then(() => undefined);\n },\n },\n {\n name: 'pin-drop',\n kind: 'enter',\n description: 'The element drops onto its spot with a squash and a landing shadow (`height`).',\n defaults: { height: 80, duration: 700 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n if (ctx.reduced) return fade(el, ctx);\n const h = Math.max(10, Number(o.height) || 80);\n ctx.animate(el, [{ filter: 'drop-shadow(0 0 0 rgba(0,0,0,0))' }, { filter: 'drop-shadow(0 6px 4px rgba(0,0,0,.35))', offset: 0.6 }, { filter: 'drop-shadow(0 2px 2px rgba(0,0,0,.2))' }], { duration: o.duration });\n return ctx\n .animate(\n el,\n [\n { transform: `translateY(${-h}px)`, opacity: 0, easing: 'cubic-bezier(.5,0,.9,.5)' },\n { transform: 'translateY(0) scale(1.08, .88)', opacity: 1, offset: 0.6 },\n { transform: 'translateY(-8px) scale(.97, 1.04)', offset: 0.8 },\n { transform: 'none', opacity: 1 },\n ],\n { duration: o.duration, fill: 'backwards' }\n )\n ?.finished.catch(() => undefined);\n },\n },\n {\n name: 'globe-spin',\n kind: 'enter',\n description: 'The element turns in like a globe coming round — rotateY with perspective (`turns`).',\n defaults: { turns: 0.5, duration: 1100 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n if (ctx.reduced) return fade(el, ctx);\n const deg = Math.round(360 * (Number(o.turns) || 0.5));\n return ctx\n .animate(el, [{ transform: `perspective(600px) rotateY(${-deg}deg) scale(.8)`, opacity: 0 }, { transform: 'perspective(600px) rotateY(0) scale(1)', opacity: 1 }], { duration: o.duration, easing: 'cubic-bezier(.2,.8,.2,1)', fill: 'backwards' })\n ?.finished.catch(() => undefined);\n },\n },\n];\n\n/** Register route-draw, marker-pulse, pin-drop and globe-spin (7.6). */\nexport function registerGeoPack(): void {\n registerEffects(GEO_FX);\n}\n"],"names":["registerEffects"],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ type MotionSensitivity = 'full' | 'gentle' | 'minimal' | 'static';
2
+ type Cleanup = () => void;
3
+
4
+ /**
5
+ * 5.0 — unified plugin-style effect registration. Every effect (built-in or
6
+ * yours) is a plain object registered once and played the same way:
7
+ * `playEffect(el, name)`, `bindEffect(el, name, { trigger })` or
8
+ * `<usa-fx effect="name" trigger="click">`. Effects get a context that
9
+ * already applies reduced motion, motion sensitivity, intensity and the
10
+ * animation budget.
11
+ */
12
+
13
+ declare const EFFECT_KINDS: readonly ["enter", "exit", "attention", "click", "hover", "card", "loop", "page", "background", "text", "cursor", "scroll"];
14
+ type EffectKind = (typeof EFFECT_KINDS)[number];
15
+ interface EffectContext {
16
+ /** Reduced motion applies (OS setting, `minimal` / `static` sensitivity). */
17
+ readonly reduced: boolean;
18
+ readonly sensitivity: MotionSensitivity;
19
+ /** The triggering event (pointer position for click effects), if any. */
20
+ readonly event?: Event;
21
+ /** `el.animate()` with the library's motion rules (may return `null`). */
22
+ animate(el: Element, keyframes: Keyframe[], options: KeyframeAnimationOptions): Animation | null;
23
+ /** Register teardown for long-running effects (loops, listeners). */
24
+ onCleanup(fn: Cleanup): void;
25
+ }
26
+ interface EffectDefinition<O extends Record<string, unknown> = Record<string, any>> {
27
+ /** Unique, kebab-case. */
28
+ name: string;
29
+ kind: EffectKind;
30
+ /** One line for docs and the gallery. */
31
+ description?: string;
32
+ /** Option defaults (merged under the caller's options). */
33
+ defaults?: Partial<O>;
34
+ /**
35
+ * Under reduced motion: `'skip'` (do nothing — default for loop, background
36
+ * and cursor effects) or `'run'` (run with `ctx.reduced === true`, the
37
+ * effect degrades itself — default for everything else).
38
+ */
39
+ reduced?: 'skip' | 'run';
40
+ /** Play the effect. Return an Animation / Promise to be awaited, or a cleanup. */
41
+ run(el: HTMLElement, options: O, ctx: EffectContext): void | Cleanup | Animation | null | Promise<unknown>;
42
+ }
43
+
44
+ /**
45
+ * 7.6 — Maps & geo motion (`motionary/fx/geo`, also `motionary/components/fx-geo`):
46
+ *
47
+ * - `route-draw` (enter) — every SVG `path` / `polyline` inside the element
48
+ * (or `[data-route]` only, when present) draws itself along its length,
49
+ * one after another (`duration`, `stagger`).
50
+ * - `marker-pulse` (attention) — rings expand out of the element like a
51
+ * location beacon (`color`, `rings`).
52
+ * - `pin-drop` (enter) — the element drops onto its spot with a squash and a
53
+ * landing shadow (`height`).
54
+ * - `globe-spin` (enter) — the element turns in like a globe coming round
55
+ * (rotateY with perspective) (`turns`).
56
+ *
57
+ * Reduced motion: route-draw shows the routes, marker-pulse does nothing,
58
+ * pin-drop and globe-spin fade in.
59
+ */
60
+
61
+ /** Length of a polyline through `points` (7.6). */
62
+ declare function routeLength(points: {
63
+ x: number;
64
+ y: number;
65
+ }[]): number;
66
+ declare const GEO_FX: EffectDefinition[];
67
+ /** Register route-draw, marker-pulse, pin-drop and globe-spin (7.6). */
68
+ declare function registerGeoPack(): void;
69
+
70
+ export { GEO_FX, registerGeoPack, routeLength };