motionary 7.2.0 → 7.4.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 +36 -0
- package/dist/chunks/builtins-HlqAkK0f.js +99 -0
- package/dist/chunks/builtins-HlqAkK0f.js.map +1 -0
- package/dist/chunks/builtins-xalxV2d5.cjs +101 -0
- package/dist/chunks/builtins-xalxV2d5.cjs.map +1 -0
- package/dist/components/angular.cjs +1 -0
- package/dist/components/angular.cjs.map +1 -1
- package/dist/components/angular.js +1 -0
- package/dist/components/angular.js.map +1 -1
- package/dist/components/effects.cjs +1 -0
- package/dist/components/effects.cjs.map +1 -1
- package/dist/components/effects.js +1 -0
- package/dist/components/effects.js.map +1 -1
- package/dist/components/fx-shop.cjs +133 -0
- package/dist/components/fx-shop.cjs.map +1 -0
- package/dist/components/fx-shop.d.cts +70 -0
- package/dist/components/fx-shop.d.ts +70 -0
- package/dist/components/fx-shop.js +129 -0
- package/dist/components/fx-shop.js.map +1 -0
- package/dist/components/fx-social.cjs +121 -0
- package/dist/components/fx-social.cjs.map +1 -0
- package/dist/components/fx-social.d.cts +66 -0
- package/dist/components/fx-social.d.ts +66 -0
- package/dist/components/fx-social.js +117 -0
- package/dist/components/fx-social.js.map +1 -0
- package/dist/components/fx.cjs +5 -98
- package/dist/components/fx.cjs.map +1 -1
- package/dist/components/fx.js +3 -96
- package/dist/components/fx.js.map +1 -1
- package/dist/components/fx2.cjs +12 -0
- package/dist/components/fx2.cjs.map +1 -1
- package/dist/components/fx2.d.cts +49 -1
- package/dist/components/fx2.d.ts +49 -1
- package/dist/components/fx2.js +9 -1
- package/dist/components/fx2.js.map +1 -1
- package/dist/components/solid.cjs +1 -0
- package/dist/components/solid.cjs.map +1 -1
- package/dist/components/solid.js +1 -0
- package/dist/components/solid.js.map +1 -1
- package/dist/components/svelte.cjs +1 -0
- package/dist/components/svelte.cjs.map +1 -1
- package/dist/components/svelte.js +1 -0
- package/dist/components/svelte.js.map +1 -1
- package/dist/components/vue.cjs +1 -0
- package/dist/components/vue.cjs.map +1 -1
- package/dist/components/vue.js +1 -0
- package/dist/components/vue.js.map +1 -1
- package/dist/components/widgets.cjs +887 -78
- package/dist/components/widgets.cjs.map +1 -1
- package/dist/components/widgets.d.cts +184 -2
- package/dist/components/widgets.d.ts +184 -2
- package/dist/components/widgets.js +874 -79
- package/dist/components/widgets.js.map +1 -1
- package/dist/components.cjs +2 -1
- package/dist/components.cjs.map +1 -1
- package/dist/components.js +2 -1
- package/dist/components.js.map +1 -1
- package/dist/widgets.umd.js +1 -1
- package/dist/widgets.umd.js.map +1 -1
- package/docs/ROADMAP.md +2 -2
- package/docs/components.md +50 -0
- package/package.json +41 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var registry = require('../chunks/registry-DehBVRDV.cjs');
|
|
4
|
+
require('../chunks/base-BaQV-2ha.cjs');
|
|
5
|
+
|
|
6
|
+
/** Quadratic-bezier arc points from a to b, lifted by `lift` px (7.3). */
|
|
7
|
+
function arcPath(ax, ay, bx, by, lift = 120, steps = 8) {
|
|
8
|
+
const cx = (ax + bx) / 2;
|
|
9
|
+
const cy = Math.min(ay, by) - lift;
|
|
10
|
+
return Array.from({ length: steps + 1 }, (_, i) => {
|
|
11
|
+
const t = i / steps;
|
|
12
|
+
const u = 1 - t;
|
|
13
|
+
return { x: u * u * ax + 2 * u * t * cx + t * t * bx, y: u * u * ay + 2 * u * t * cy + t * t * by };
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
const bump = (el, ctx) => el && ctx.animate(el, ctx.reduced ? [{ opacity: 0.5 }, { opacity: 1 }] : [{ transform: 'scale(1)' }, { transform: 'scale(1.25)', offset: 0.35 }, { transform: 'scale(.92)', offset: 0.7 }, { transform: 'scale(1)' }], { duration: ctx.reduced ? 250 : 480, easing: 'ease-out' });
|
|
17
|
+
const SHOP_FX = [
|
|
18
|
+
{
|
|
19
|
+
name: 'fly-to-cart',
|
|
20
|
+
kind: 'click',
|
|
21
|
+
description: 'A ghost of the element (or its first `img`) flies on an arc into the cart (`to` selector, default `[data-cart]`), shrinking; the cart bumps (`duration`, `lift`).',
|
|
22
|
+
defaults: { to: '[data-cart]', duration: 750, lift: 120 },
|
|
23
|
+
run: (el, o, ctx) => {
|
|
24
|
+
const cart = document.querySelector(o.to);
|
|
25
|
+
if (!cart)
|
|
26
|
+
return;
|
|
27
|
+
if (ctx.reduced)
|
|
28
|
+
return void bump(cart, ctx);
|
|
29
|
+
const src = el.querySelector('img') || el;
|
|
30
|
+
const a = src.getBoundingClientRect();
|
|
31
|
+
const b = cart.getBoundingClientRect();
|
|
32
|
+
const ghost = src.cloneNode(true);
|
|
33
|
+
ghost.removeAttribute('id');
|
|
34
|
+
ghost.setAttribute('aria-hidden', 'true');
|
|
35
|
+
Object.assign(ghost.style, { position: 'fixed', left: `${a.left}px`, top: `${a.top}px`, width: `${a.width}px`, height: `${a.height}px`, margin: '0', zIndex: '2147483000', pointerEvents: 'none', borderRadius: '12px', overflow: 'hidden' });
|
|
36
|
+
document.body.appendChild(ghost);
|
|
37
|
+
const pts = arcPath(0, 0, b.left + b.width / 2 - (a.left + a.width / 2), b.top + b.height / 2 - (a.top + a.height / 2), o.lift);
|
|
38
|
+
const s = Math.max(0.12, Math.min(1, 28 / Math.max(a.width, a.height, 1)));
|
|
39
|
+
const anim = ctx.animate(ghost, pts.map((p, i) => ({ transform: `translate(${p.x.toFixed(1)}px,${p.y.toFixed(1)}px) scale(${(1 - (1 - s) * (i / (pts.length - 1))).toFixed(3)})`, opacity: i === pts.length - 1 ? 0.6 : 1 })), { duration: o.duration, easing: 'cubic-bezier(.5,0,.75,.4)', fill: 'forwards' });
|
|
40
|
+
const done = () => (ghost.remove(), bump(cart, ctx));
|
|
41
|
+
if (anim)
|
|
42
|
+
anim.finished.then(done, () => ghost.remove());
|
|
43
|
+
else
|
|
44
|
+
done();
|
|
45
|
+
ctx.onCleanup(() => ghost.remove());
|
|
46
|
+
return anim?.finished.catch(() => undefined);
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'price-flip',
|
|
51
|
+
kind: 'enter',
|
|
52
|
+
description: 'The price flips character by character like a split-flap display from `data-from` (or scrambled digits) to its text (`stagger`).',
|
|
53
|
+
defaults: { stagger: 55, duration: 380 },
|
|
54
|
+
run: (el, o, ctx) => {
|
|
55
|
+
const to = el.dataset.priceTo || el.textContent || '';
|
|
56
|
+
el.dataset.priceTo = to;
|
|
57
|
+
if (ctx.reduced)
|
|
58
|
+
return void (el.textContent = to);
|
|
59
|
+
const from = (el.dataset.from || '').padStart(to.length, ' ');
|
|
60
|
+
el.textContent = '';
|
|
61
|
+
el.setAttribute('aria-label', to.trim());
|
|
62
|
+
const anims = Array.from(to).map((ch, i) => {
|
|
63
|
+
const span = document.createElement('span');
|
|
64
|
+
span.setAttribute('aria-hidden', 'true');
|
|
65
|
+
span.style.display = 'inline-block';
|
|
66
|
+
span.textContent = /\d/.test(ch) ? (from[i] && from[i] !== ' ' ? from[i] : String((Number(ch) + 5) % 10)) : ch;
|
|
67
|
+
el.appendChild(span);
|
|
68
|
+
const a = ctx.animate(span, [{ transform: 'rotateX(0)' }, { transform: 'rotateX(90deg)', offset: 0.5 }, { transform: 'rotateX(0)' }], { duration: o.duration, delay: i * o.stagger, easing: 'ease-in-out' });
|
|
69
|
+
setTimeout(() => (span.textContent = ch), i * o.stagger + o.duration / 2);
|
|
70
|
+
return a;
|
|
71
|
+
});
|
|
72
|
+
return Promise.all(anims.map((a) => a?.finished.catch(() => undefined))).then(() => {
|
|
73
|
+
el.textContent = to;
|
|
74
|
+
el.removeAttribute('aria-label');
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'stock-pulse',
|
|
80
|
+
kind: 'loop',
|
|
81
|
+
description: 'A soft urgency pulse — a glow ring in `color` — for low stock / last items (`color`, `period`).',
|
|
82
|
+
defaults: { color: 'rgba(239,68,68,.55)', period: 1400 },
|
|
83
|
+
reduced: 'skip',
|
|
84
|
+
run: (el, o, ctx) => {
|
|
85
|
+
const a = ctx.animate(el, [{ boxShadow: `0 0 0 0 ${o.color}` }, { boxShadow: '0 0 0 12px rgba(0,0,0,0)' }], { duration: o.period, iterations: Infinity, easing: 'ease-out' });
|
|
86
|
+
const stop = () => a?.cancel();
|
|
87
|
+
ctx.onCleanup(stop);
|
|
88
|
+
return stop;
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'sale-shine',
|
|
93
|
+
kind: 'hover',
|
|
94
|
+
description: 'A diagonal light sweep across the element (product cards, buy buttons) (`duration`).',
|
|
95
|
+
defaults: { duration: 700 },
|
|
96
|
+
reduced: 'skip',
|
|
97
|
+
run: (el, o, ctx) => {
|
|
98
|
+
const s = document.createElement('span');
|
|
99
|
+
s.setAttribute('aria-hidden', 'true');
|
|
100
|
+
Object.assign(s.style, { position: 'absolute', inset: '0', pointerEvents: 'none', background: 'linear-gradient(115deg,transparent 30%,rgba(255,255,255,.55) 50%,transparent 70%)', borderRadius: 'inherit' });
|
|
101
|
+
if (getComputedStyle(el).position === 'static')
|
|
102
|
+
el.style.position = 'relative';
|
|
103
|
+
const prev = el.style.overflow;
|
|
104
|
+
el.style.overflow = 'hidden';
|
|
105
|
+
el.appendChild(s);
|
|
106
|
+
const a = ctx.animate(s, [{ transform: 'translateX(-100%)' }, { transform: 'translateX(100%)' }], { duration: o.duration, easing: 'ease-in-out' });
|
|
107
|
+
const end = () => (s.remove(), (el.style.overflow = prev));
|
|
108
|
+
if (a)
|
|
109
|
+
a.finished.then(end, end);
|
|
110
|
+
else
|
|
111
|
+
end();
|
|
112
|
+
return a?.finished.catch(() => undefined);
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: 'badge-pop',
|
|
117
|
+
kind: 'attention',
|
|
118
|
+
description: 'A sale badge pops in with a wobble (`duration`).',
|
|
119
|
+
defaults: { duration: 650 },
|
|
120
|
+
run: (el, o, ctx) => ctx.reduced
|
|
121
|
+
? ctx.animate(el, [{ opacity: 0 }, { opacity: 1 }], { duration: 250 })
|
|
122
|
+
: ctx.animate(el, [{ transform: 'scale(0) rotate(-20deg)' }, { transform: 'scale(1.2) rotate(8deg)', offset: 0.5 }, { transform: 'scale(.95) rotate(-4deg)', offset: 0.75 }, { transform: 'none' }], { duration: o.duration, easing: 'ease-out' }),
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
/** Register the 7.3 e-commerce pack (idempotent). */
|
|
126
|
+
function registerShopPack() {
|
|
127
|
+
registry.registerEffects(SHOP_FX);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
exports.SHOP_FX = SHOP_FX;
|
|
131
|
+
exports.arcPath = arcPath;
|
|
132
|
+
exports.registerShopPack = registerShopPack;
|
|
133
|
+
//# sourceMappingURL=fx-shop.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fx-shop.cjs","sources":["../../src/components/fx2/shop.ts"],"sourcesContent":["/**\n * 7.3 — E-commerce motion (`motionary/fx/shop`, also\n * `motionary/components/fx-shop`):\n *\n * - `fly-to-cart` (click) — a ghost of the element (or its first `img`) flies\n * on an arc into the cart (`to`, default `[data-cart]`), shrinking; the cart\n * bumps when it lands.\n * - `price-flip` (enter) — the price flips like a split-flap display from\n * `data-from` (or a scramble) to its text.\n * - `stock-pulse` (loop) — a soft urgency pulse (glow ring) for low stock.\n * - `sale-shine` (hover) — a diagonal light sweep across the element.\n * - `badge-pop` (attention) — a sale badge pops in with a wobble.\n *\n * Reduced motion: fly-to-cart only bumps the cart with a fade, price-flip\n * sets the text, stock-pulse and sale-shine do nothing, badge-pop fades.\n */\nimport type { EffectContext, EffectDefinition } from '../fx/registry';\nimport { registerEffects } from '../fx/registry';\n\n/** Quadratic-bezier arc points from a to b, lifted by `lift` px (7.3). */\nexport function arcPath(ax: number, ay: number, bx: number, by: number, lift = 120, steps = 8): { x: number; y: number }[] {\n const cx = (ax + bx) / 2;\n const cy = Math.min(ay, by) - lift;\n return Array.from({ length: steps + 1 }, (_, i) => {\n const t = i / steps;\n const u = 1 - t;\n return { x: u * u * ax + 2 * u * t * cx + t * t * bx, y: u * u * ay + 2 * u * t * cy + t * t * by };\n });\n}\n\nconst bump = (el: Element | null, ctx: EffectContext) =>\n el && ctx.animate(el, ctx.reduced ? [{ opacity: 0.5 }, { opacity: 1 }] : [{ transform: 'scale(1)' }, { transform: 'scale(1.25)', offset: 0.35 }, { transform: 'scale(.92)', offset: 0.7 }, { transform: 'scale(1)' }], { duration: ctx.reduced ? 250 : 480, easing: 'ease-out' });\n\nexport const SHOP_FX: EffectDefinition[] = [\n {\n name: 'fly-to-cart',\n kind: 'click',\n description: 'A ghost of the element (or its first `img`) flies on an arc into the cart (`to` selector, default `[data-cart]`), shrinking; the cart bumps (`duration`, `lift`).',\n defaults: { to: '[data-cart]', duration: 750, lift: 120 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const cart = document.querySelector(o.to) as HTMLElement | null;\n if (!cart) return;\n if (ctx.reduced) return void bump(cart, ctx);\n const src = (el.querySelector('img') as HTMLElement | null) || el;\n const a = src.getBoundingClientRect();\n const b = cart.getBoundingClientRect();\n const ghost = src.cloneNode(true) as HTMLElement;\n ghost.removeAttribute('id');\n ghost.setAttribute('aria-hidden', 'true');\n Object.assign(ghost.style, { position: 'fixed', left: `${a.left}px`, top: `${a.top}px`, width: `${a.width}px`, height: `${a.height}px`, margin: '0', zIndex: '2147483000', pointerEvents: 'none', borderRadius: '12px', overflow: 'hidden' });\n document.body.appendChild(ghost);\n const pts = arcPath(0, 0, b.left + b.width / 2 - (a.left + a.width / 2), b.top + b.height / 2 - (a.top + a.height / 2), o.lift);\n const s = Math.max(0.12, Math.min(1, 28 / Math.max(a.width, a.height, 1)));\n const anim = ctx.animate(ghost, pts.map((p, i) => ({ transform: `translate(${p.x.toFixed(1)}px,${p.y.toFixed(1)}px) scale(${(1 - (1 - s) * (i / (pts.length - 1))).toFixed(3)})`, opacity: i === pts.length - 1 ? 0.6 : 1 })), { duration: o.duration, easing: 'cubic-bezier(.5,0,.75,.4)', fill: 'forwards' });\n const done = () => (ghost.remove(), bump(cart, ctx));\n if (anim) anim.finished.then(done, () => ghost.remove());\n else done();\n ctx.onCleanup(() => ghost.remove());\n return anim?.finished.catch(() => undefined);\n },\n },\n {\n name: 'price-flip',\n kind: 'enter',\n description: 'The price flips character by character like a split-flap display from `data-from` (or scrambled digits) to its text (`stagger`).',\n defaults: { stagger: 55, duration: 380 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const to = el.dataset.priceTo || el.textContent || '';\n el.dataset.priceTo = to;\n if (ctx.reduced) return void (el.textContent = to);\n const from = (el.dataset.from || '').padStart(to.length, ' ');\n el.textContent = '';\n el.setAttribute('aria-label', to.trim());\n const anims = Array.from(to).map((ch, i) => {\n const span = document.createElement('span');\n span.setAttribute('aria-hidden', 'true');\n span.style.display = 'inline-block';\n span.textContent = /\\d/.test(ch) ? (from[i] && from[i] !== ' ' ? from[i] : String((Number(ch) + 5) % 10)) : ch;\n el.appendChild(span);\n const a = ctx.animate(span, [{ transform: 'rotateX(0)' }, { transform: 'rotateX(90deg)', offset: 0.5 }, { transform: 'rotateX(0)' }], { duration: o.duration, delay: i * o.stagger, easing: 'ease-in-out' });\n setTimeout(() => (span.textContent = ch), i * o.stagger + o.duration / 2);\n return a;\n });\n return Promise.all(anims.map((a) => a?.finished.catch(() => undefined))).then(() => {\n el.textContent = to;\n el.removeAttribute('aria-label');\n });\n },\n },\n {\n name: 'stock-pulse',\n kind: 'loop',\n description: 'A soft urgency pulse — a glow ring in `color` — for low stock / last items (`color`, `period`).',\n defaults: { color: 'rgba(239,68,68,.55)', period: 1400 },\n reduced: 'skip',\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const a = ctx.animate(el, [{ boxShadow: `0 0 0 0 ${o.color}` }, { boxShadow: '0 0 0 12px rgba(0,0,0,0)' }], { duration: o.period, iterations: Infinity, easing: 'ease-out' });\n const stop = () => a?.cancel();\n ctx.onCleanup(stop);\n return stop;\n },\n },\n {\n name: 'sale-shine',\n kind: 'hover',\n description: 'A diagonal light sweep across the element (product cards, buy buttons) (`duration`).',\n defaults: { duration: 700 },\n reduced: 'skip',\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const s = document.createElement('span');\n s.setAttribute('aria-hidden', 'true');\n Object.assign(s.style, { position: 'absolute', inset: '0', pointerEvents: 'none', background: 'linear-gradient(115deg,transparent 30%,rgba(255,255,255,.55) 50%,transparent 70%)', borderRadius: 'inherit' });\n if (getComputedStyle(el).position === 'static') el.style.position = 'relative';\n const prev = el.style.overflow;\n el.style.overflow = 'hidden';\n el.appendChild(s);\n const a = ctx.animate(s, [{ transform: 'translateX(-100%)' }, { transform: 'translateX(100%)' }], { duration: o.duration, easing: 'ease-in-out' });\n const end = () => (s.remove(), (el.style.overflow = prev));\n if (a) a.finished.then(end, end);\n else end();\n return a?.finished.catch(() => undefined);\n },\n },\n {\n name: 'badge-pop',\n kind: 'attention',\n description: 'A sale badge pops in with a wobble (`duration`).',\n defaults: { duration: 650 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) =>\n ctx.reduced\n ? ctx.animate(el, [{ opacity: 0 }, { opacity: 1 }], { duration: 250 })\n : ctx.animate(el, [{ transform: 'scale(0) rotate(-20deg)' }, { transform: 'scale(1.2) rotate(8deg)', offset: 0.5 }, { transform: 'scale(.95) rotate(-4deg)', offset: 0.75 }, { transform: 'none' }], { duration: o.duration, easing: 'ease-out' }),\n },\n];\n\n/** Register the 7.3 e-commerce pack (idempotent). */\nexport function registerShopPack(): void {\n registerEffects(SHOP_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.3 — E-commerce motion (`motionary/fx/shop`, also
|
|
46
|
+
* `motionary/components/fx-shop`):
|
|
47
|
+
*
|
|
48
|
+
* - `fly-to-cart` (click) — a ghost of the element (or its first `img`) flies
|
|
49
|
+
* on an arc into the cart (`to`, default `[data-cart]`), shrinking; the cart
|
|
50
|
+
* bumps when it lands.
|
|
51
|
+
* - `price-flip` (enter) — the price flips like a split-flap display from
|
|
52
|
+
* `data-from` (or a scramble) to its text.
|
|
53
|
+
* - `stock-pulse` (loop) — a soft urgency pulse (glow ring) for low stock.
|
|
54
|
+
* - `sale-shine` (hover) — a diagonal light sweep across the element.
|
|
55
|
+
* - `badge-pop` (attention) — a sale badge pops in with a wobble.
|
|
56
|
+
*
|
|
57
|
+
* Reduced motion: fly-to-cart only bumps the cart with a fade, price-flip
|
|
58
|
+
* sets the text, stock-pulse and sale-shine do nothing, badge-pop fades.
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/** Quadratic-bezier arc points from a to b, lifted by `lift` px (7.3). */
|
|
62
|
+
declare function arcPath(ax: number, ay: number, bx: number, by: number, lift?: number, steps?: number): {
|
|
63
|
+
x: number;
|
|
64
|
+
y: number;
|
|
65
|
+
}[];
|
|
66
|
+
declare const SHOP_FX: EffectDefinition[];
|
|
67
|
+
/** Register the 7.3 e-commerce pack (idempotent). */
|
|
68
|
+
declare function registerShopPack(): void;
|
|
69
|
+
|
|
70
|
+
export { SHOP_FX, arcPath, registerShopPack };
|
|
@@ -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.3 — E-commerce motion (`motionary/fx/shop`, also
|
|
46
|
+
* `motionary/components/fx-shop`):
|
|
47
|
+
*
|
|
48
|
+
* - `fly-to-cart` (click) — a ghost of the element (or its first `img`) flies
|
|
49
|
+
* on an arc into the cart (`to`, default `[data-cart]`), shrinking; the cart
|
|
50
|
+
* bumps when it lands.
|
|
51
|
+
* - `price-flip` (enter) — the price flips like a split-flap display from
|
|
52
|
+
* `data-from` (or a scramble) to its text.
|
|
53
|
+
* - `stock-pulse` (loop) — a soft urgency pulse (glow ring) for low stock.
|
|
54
|
+
* - `sale-shine` (hover) — a diagonal light sweep across the element.
|
|
55
|
+
* - `badge-pop` (attention) — a sale badge pops in with a wobble.
|
|
56
|
+
*
|
|
57
|
+
* Reduced motion: fly-to-cart only bumps the cart with a fade, price-flip
|
|
58
|
+
* sets the text, stock-pulse and sale-shine do nothing, badge-pop fades.
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/** Quadratic-bezier arc points from a to b, lifted by `lift` px (7.3). */
|
|
62
|
+
declare function arcPath(ax: number, ay: number, bx: number, by: number, lift?: number, steps?: number): {
|
|
63
|
+
x: number;
|
|
64
|
+
y: number;
|
|
65
|
+
}[];
|
|
66
|
+
declare const SHOP_FX: EffectDefinition[];
|
|
67
|
+
/** Register the 7.3 e-commerce pack (idempotent). */
|
|
68
|
+
declare function registerShopPack(): void;
|
|
69
|
+
|
|
70
|
+
export { SHOP_FX, arcPath, registerShopPack };
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { registerEffects } from '../chunks/registry-CyKExAmE.js';
|
|
2
|
+
import '../chunks/base-2-yYc93C.js';
|
|
3
|
+
|
|
4
|
+
/** Quadratic-bezier arc points from a to b, lifted by `lift` px (7.3). */
|
|
5
|
+
function arcPath(ax, ay, bx, by, lift = 120, steps = 8) {
|
|
6
|
+
const cx = (ax + bx) / 2;
|
|
7
|
+
const cy = Math.min(ay, by) - lift;
|
|
8
|
+
return Array.from({ length: steps + 1 }, (_, i) => {
|
|
9
|
+
const t = i / steps;
|
|
10
|
+
const u = 1 - t;
|
|
11
|
+
return { x: u * u * ax + 2 * u * t * cx + t * t * bx, y: u * u * ay + 2 * u * t * cy + t * t * by };
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
const bump = (el, ctx) => el && ctx.animate(el, ctx.reduced ? [{ opacity: 0.5 }, { opacity: 1 }] : [{ transform: 'scale(1)' }, { transform: 'scale(1.25)', offset: 0.35 }, { transform: 'scale(.92)', offset: 0.7 }, { transform: 'scale(1)' }], { duration: ctx.reduced ? 250 : 480, easing: 'ease-out' });
|
|
15
|
+
const SHOP_FX = [
|
|
16
|
+
{
|
|
17
|
+
name: 'fly-to-cart',
|
|
18
|
+
kind: 'click',
|
|
19
|
+
description: 'A ghost of the element (or its first `img`) flies on an arc into the cart (`to` selector, default `[data-cart]`), shrinking; the cart bumps (`duration`, `lift`).',
|
|
20
|
+
defaults: { to: '[data-cart]', duration: 750, lift: 120 },
|
|
21
|
+
run: (el, o, ctx) => {
|
|
22
|
+
const cart = document.querySelector(o.to);
|
|
23
|
+
if (!cart)
|
|
24
|
+
return;
|
|
25
|
+
if (ctx.reduced)
|
|
26
|
+
return void bump(cart, ctx);
|
|
27
|
+
const src = el.querySelector('img') || el;
|
|
28
|
+
const a = src.getBoundingClientRect();
|
|
29
|
+
const b = cart.getBoundingClientRect();
|
|
30
|
+
const ghost = src.cloneNode(true);
|
|
31
|
+
ghost.removeAttribute('id');
|
|
32
|
+
ghost.setAttribute('aria-hidden', 'true');
|
|
33
|
+
Object.assign(ghost.style, { position: 'fixed', left: `${a.left}px`, top: `${a.top}px`, width: `${a.width}px`, height: `${a.height}px`, margin: '0', zIndex: '2147483000', pointerEvents: 'none', borderRadius: '12px', overflow: 'hidden' });
|
|
34
|
+
document.body.appendChild(ghost);
|
|
35
|
+
const pts = arcPath(0, 0, b.left + b.width / 2 - (a.left + a.width / 2), b.top + b.height / 2 - (a.top + a.height / 2), o.lift);
|
|
36
|
+
const s = Math.max(0.12, Math.min(1, 28 / Math.max(a.width, a.height, 1)));
|
|
37
|
+
const anim = ctx.animate(ghost, pts.map((p, i) => ({ transform: `translate(${p.x.toFixed(1)}px,${p.y.toFixed(1)}px) scale(${(1 - (1 - s) * (i / (pts.length - 1))).toFixed(3)})`, opacity: i === pts.length - 1 ? 0.6 : 1 })), { duration: o.duration, easing: 'cubic-bezier(.5,0,.75,.4)', fill: 'forwards' });
|
|
38
|
+
const done = () => (ghost.remove(), bump(cart, ctx));
|
|
39
|
+
if (anim)
|
|
40
|
+
anim.finished.then(done, () => ghost.remove());
|
|
41
|
+
else
|
|
42
|
+
done();
|
|
43
|
+
ctx.onCleanup(() => ghost.remove());
|
|
44
|
+
return anim?.finished.catch(() => undefined);
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'price-flip',
|
|
49
|
+
kind: 'enter',
|
|
50
|
+
description: 'The price flips character by character like a split-flap display from `data-from` (or scrambled digits) to its text (`stagger`).',
|
|
51
|
+
defaults: { stagger: 55, duration: 380 },
|
|
52
|
+
run: (el, o, ctx) => {
|
|
53
|
+
const to = el.dataset.priceTo || el.textContent || '';
|
|
54
|
+
el.dataset.priceTo = to;
|
|
55
|
+
if (ctx.reduced)
|
|
56
|
+
return void (el.textContent = to);
|
|
57
|
+
const from = (el.dataset.from || '').padStart(to.length, ' ');
|
|
58
|
+
el.textContent = '';
|
|
59
|
+
el.setAttribute('aria-label', to.trim());
|
|
60
|
+
const anims = Array.from(to).map((ch, i) => {
|
|
61
|
+
const span = document.createElement('span');
|
|
62
|
+
span.setAttribute('aria-hidden', 'true');
|
|
63
|
+
span.style.display = 'inline-block';
|
|
64
|
+
span.textContent = /\d/.test(ch) ? (from[i] && from[i] !== ' ' ? from[i] : String((Number(ch) + 5) % 10)) : ch;
|
|
65
|
+
el.appendChild(span);
|
|
66
|
+
const a = ctx.animate(span, [{ transform: 'rotateX(0)' }, { transform: 'rotateX(90deg)', offset: 0.5 }, { transform: 'rotateX(0)' }], { duration: o.duration, delay: i * o.stagger, easing: 'ease-in-out' });
|
|
67
|
+
setTimeout(() => (span.textContent = ch), i * o.stagger + o.duration / 2);
|
|
68
|
+
return a;
|
|
69
|
+
});
|
|
70
|
+
return Promise.all(anims.map((a) => a?.finished.catch(() => undefined))).then(() => {
|
|
71
|
+
el.textContent = to;
|
|
72
|
+
el.removeAttribute('aria-label');
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'stock-pulse',
|
|
78
|
+
kind: 'loop',
|
|
79
|
+
description: 'A soft urgency pulse — a glow ring in `color` — for low stock / last items (`color`, `period`).',
|
|
80
|
+
defaults: { color: 'rgba(239,68,68,.55)', period: 1400 },
|
|
81
|
+
reduced: 'skip',
|
|
82
|
+
run: (el, o, ctx) => {
|
|
83
|
+
const a = ctx.animate(el, [{ boxShadow: `0 0 0 0 ${o.color}` }, { boxShadow: '0 0 0 12px rgba(0,0,0,0)' }], { duration: o.period, iterations: Infinity, easing: 'ease-out' });
|
|
84
|
+
const stop = () => a?.cancel();
|
|
85
|
+
ctx.onCleanup(stop);
|
|
86
|
+
return stop;
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'sale-shine',
|
|
91
|
+
kind: 'hover',
|
|
92
|
+
description: 'A diagonal light sweep across the element (product cards, buy buttons) (`duration`).',
|
|
93
|
+
defaults: { duration: 700 },
|
|
94
|
+
reduced: 'skip',
|
|
95
|
+
run: (el, o, ctx) => {
|
|
96
|
+
const s = document.createElement('span');
|
|
97
|
+
s.setAttribute('aria-hidden', 'true');
|
|
98
|
+
Object.assign(s.style, { position: 'absolute', inset: '0', pointerEvents: 'none', background: 'linear-gradient(115deg,transparent 30%,rgba(255,255,255,.55) 50%,transparent 70%)', borderRadius: 'inherit' });
|
|
99
|
+
if (getComputedStyle(el).position === 'static')
|
|
100
|
+
el.style.position = 'relative';
|
|
101
|
+
const prev = el.style.overflow;
|
|
102
|
+
el.style.overflow = 'hidden';
|
|
103
|
+
el.appendChild(s);
|
|
104
|
+
const a = ctx.animate(s, [{ transform: 'translateX(-100%)' }, { transform: 'translateX(100%)' }], { duration: o.duration, easing: 'ease-in-out' });
|
|
105
|
+
const end = () => (s.remove(), (el.style.overflow = prev));
|
|
106
|
+
if (a)
|
|
107
|
+
a.finished.then(end, end);
|
|
108
|
+
else
|
|
109
|
+
end();
|
|
110
|
+
return a?.finished.catch(() => undefined);
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: 'badge-pop',
|
|
115
|
+
kind: 'attention',
|
|
116
|
+
description: 'A sale badge pops in with a wobble (`duration`).',
|
|
117
|
+
defaults: { duration: 650 },
|
|
118
|
+
run: (el, o, ctx) => ctx.reduced
|
|
119
|
+
? ctx.animate(el, [{ opacity: 0 }, { opacity: 1 }], { duration: 250 })
|
|
120
|
+
: ctx.animate(el, [{ transform: 'scale(0) rotate(-20deg)' }, { transform: 'scale(1.2) rotate(8deg)', offset: 0.5 }, { transform: 'scale(.95) rotate(-4deg)', offset: 0.75 }, { transform: 'none' }], { duration: o.duration, easing: 'ease-out' }),
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
/** Register the 7.3 e-commerce pack (idempotent). */
|
|
124
|
+
function registerShopPack() {
|
|
125
|
+
registerEffects(SHOP_FX);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export { SHOP_FX, arcPath, registerShopPack };
|
|
129
|
+
//# sourceMappingURL=fx-shop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fx-shop.js","sources":["../../src/components/fx2/shop.ts"],"sourcesContent":["/**\n * 7.3 — E-commerce motion (`motionary/fx/shop`, also\n * `motionary/components/fx-shop`):\n *\n * - `fly-to-cart` (click) — a ghost of the element (or its first `img`) flies\n * on an arc into the cart (`to`, default `[data-cart]`), shrinking; the cart\n * bumps when it lands.\n * - `price-flip` (enter) — the price flips like a split-flap display from\n * `data-from` (or a scramble) to its text.\n * - `stock-pulse` (loop) — a soft urgency pulse (glow ring) for low stock.\n * - `sale-shine` (hover) — a diagonal light sweep across the element.\n * - `badge-pop` (attention) — a sale badge pops in with a wobble.\n *\n * Reduced motion: fly-to-cart only bumps the cart with a fade, price-flip\n * sets the text, stock-pulse and sale-shine do nothing, badge-pop fades.\n */\nimport type { EffectContext, EffectDefinition } from '../fx/registry';\nimport { registerEffects } from '../fx/registry';\n\n/** Quadratic-bezier arc points from a to b, lifted by `lift` px (7.3). */\nexport function arcPath(ax: number, ay: number, bx: number, by: number, lift = 120, steps = 8): { x: number; y: number }[] {\n const cx = (ax + bx) / 2;\n const cy = Math.min(ay, by) - lift;\n return Array.from({ length: steps + 1 }, (_, i) => {\n const t = i / steps;\n const u = 1 - t;\n return { x: u * u * ax + 2 * u * t * cx + t * t * bx, y: u * u * ay + 2 * u * t * cy + t * t * by };\n });\n}\n\nconst bump = (el: Element | null, ctx: EffectContext) =>\n el && ctx.animate(el, ctx.reduced ? [{ opacity: 0.5 }, { opacity: 1 }] : [{ transform: 'scale(1)' }, { transform: 'scale(1.25)', offset: 0.35 }, { transform: 'scale(.92)', offset: 0.7 }, { transform: 'scale(1)' }], { duration: ctx.reduced ? 250 : 480, easing: 'ease-out' });\n\nexport const SHOP_FX: EffectDefinition[] = [\n {\n name: 'fly-to-cart',\n kind: 'click',\n description: 'A ghost of the element (or its first `img`) flies on an arc into the cart (`to` selector, default `[data-cart]`), shrinking; the cart bumps (`duration`, `lift`).',\n defaults: { to: '[data-cart]', duration: 750, lift: 120 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const cart = document.querySelector(o.to) as HTMLElement | null;\n if (!cart) return;\n if (ctx.reduced) return void bump(cart, ctx);\n const src = (el.querySelector('img') as HTMLElement | null) || el;\n const a = src.getBoundingClientRect();\n const b = cart.getBoundingClientRect();\n const ghost = src.cloneNode(true) as HTMLElement;\n ghost.removeAttribute('id');\n ghost.setAttribute('aria-hidden', 'true');\n Object.assign(ghost.style, { position: 'fixed', left: `${a.left}px`, top: `${a.top}px`, width: `${a.width}px`, height: `${a.height}px`, margin: '0', zIndex: '2147483000', pointerEvents: 'none', borderRadius: '12px', overflow: 'hidden' });\n document.body.appendChild(ghost);\n const pts = arcPath(0, 0, b.left + b.width / 2 - (a.left + a.width / 2), b.top + b.height / 2 - (a.top + a.height / 2), o.lift);\n const s = Math.max(0.12, Math.min(1, 28 / Math.max(a.width, a.height, 1)));\n const anim = ctx.animate(ghost, pts.map((p, i) => ({ transform: `translate(${p.x.toFixed(1)}px,${p.y.toFixed(1)}px) scale(${(1 - (1 - s) * (i / (pts.length - 1))).toFixed(3)})`, opacity: i === pts.length - 1 ? 0.6 : 1 })), { duration: o.duration, easing: 'cubic-bezier(.5,0,.75,.4)', fill: 'forwards' });\n const done = () => (ghost.remove(), bump(cart, ctx));\n if (anim) anim.finished.then(done, () => ghost.remove());\n else done();\n ctx.onCleanup(() => ghost.remove());\n return anim?.finished.catch(() => undefined);\n },\n },\n {\n name: 'price-flip',\n kind: 'enter',\n description: 'The price flips character by character like a split-flap display from `data-from` (or scrambled digits) to its text (`stagger`).',\n defaults: { stagger: 55, duration: 380 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const to = el.dataset.priceTo || el.textContent || '';\n el.dataset.priceTo = to;\n if (ctx.reduced) return void (el.textContent = to);\n const from = (el.dataset.from || '').padStart(to.length, ' ');\n el.textContent = '';\n el.setAttribute('aria-label', to.trim());\n const anims = Array.from(to).map((ch, i) => {\n const span = document.createElement('span');\n span.setAttribute('aria-hidden', 'true');\n span.style.display = 'inline-block';\n span.textContent = /\\d/.test(ch) ? (from[i] && from[i] !== ' ' ? from[i] : String((Number(ch) + 5) % 10)) : ch;\n el.appendChild(span);\n const a = ctx.animate(span, [{ transform: 'rotateX(0)' }, { transform: 'rotateX(90deg)', offset: 0.5 }, { transform: 'rotateX(0)' }], { duration: o.duration, delay: i * o.stagger, easing: 'ease-in-out' });\n setTimeout(() => (span.textContent = ch), i * o.stagger + o.duration / 2);\n return a;\n });\n return Promise.all(anims.map((a) => a?.finished.catch(() => undefined))).then(() => {\n el.textContent = to;\n el.removeAttribute('aria-label');\n });\n },\n },\n {\n name: 'stock-pulse',\n kind: 'loop',\n description: 'A soft urgency pulse — a glow ring in `color` — for low stock / last items (`color`, `period`).',\n defaults: { color: 'rgba(239,68,68,.55)', period: 1400 },\n reduced: 'skip',\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const a = ctx.animate(el, [{ boxShadow: `0 0 0 0 ${o.color}` }, { boxShadow: '0 0 0 12px rgba(0,0,0,0)' }], { duration: o.period, iterations: Infinity, easing: 'ease-out' });\n const stop = () => a?.cancel();\n ctx.onCleanup(stop);\n return stop;\n },\n },\n {\n name: 'sale-shine',\n kind: 'hover',\n description: 'A diagonal light sweep across the element (product cards, buy buttons) (`duration`).',\n defaults: { duration: 700 },\n reduced: 'skip',\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const s = document.createElement('span');\n s.setAttribute('aria-hidden', 'true');\n Object.assign(s.style, { position: 'absolute', inset: '0', pointerEvents: 'none', background: 'linear-gradient(115deg,transparent 30%,rgba(255,255,255,.55) 50%,transparent 70%)', borderRadius: 'inherit' });\n if (getComputedStyle(el).position === 'static') el.style.position = 'relative';\n const prev = el.style.overflow;\n el.style.overflow = 'hidden';\n el.appendChild(s);\n const a = ctx.animate(s, [{ transform: 'translateX(-100%)' }, { transform: 'translateX(100%)' }], { duration: o.duration, easing: 'ease-in-out' });\n const end = () => (s.remove(), (el.style.overflow = prev));\n if (a) a.finished.then(end, end);\n else end();\n return a?.finished.catch(() => undefined);\n },\n },\n {\n name: 'badge-pop',\n kind: 'attention',\n description: 'A sale badge pops in with a wobble (`duration`).',\n defaults: { duration: 650 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) =>\n ctx.reduced\n ? ctx.animate(el, [{ opacity: 0 }, { opacity: 1 }], { duration: 250 })\n : ctx.animate(el, [{ transform: 'scale(0) rotate(-20deg)' }, { transform: 'scale(1.2) rotate(8deg)', offset: 0.5 }, { transform: 'scale(.95) rotate(-4deg)', offset: 0.75 }, { transform: 'none' }], { duration: o.duration, easing: 'ease-out' }),\n },\n];\n\n/** Register the 7.3 e-commerce pack (idempotent). */\nexport function registerShopPack(): void {\n registerEffects(SHOP_FX);\n}\n"],"names":[],"mappings":";;;AAmBA;SACgB,OAAO,CAAC,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,IAAI,GAAG,GAAG,EAAE,KAAK,GAAG,CAAC,EAAA;IAC3F,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC;AACxB,IAAA,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI;AAClC,IAAA,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAI;AAChD,QAAA,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK;AACnB,QAAA,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;QACf,OAAO,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE;AACrG,IAAA,CAAC,CAAC;AACJ;AAEA,MAAM,IAAI,GAAG,CAAC,EAAkB,EAAE,GAAkB,KAClD,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,OAAO,GAAG,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,CAAC,OAAO,GAAG,GAAG,GAAG,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAE5Q,MAAM,OAAO,GAAuB;AACzC,IAAA;AACE,QAAA,IAAI,EAAE,aAAa;AACnB,QAAA,IAAI,EAAE,OAAO;AACb,QAAA,WAAW,EAAE,mKAAmK;AAChL,QAAA,QAAQ,EAAE,EAAE,EAAE,EAAE,aAAa,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE;QACzD,GAAG,EAAE,CAAC,EAAe,EAAE,CAAM,EAAE,GAAkB,KAAI;YACnD,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAuB;AAC/D,YAAA,IAAI,CAAC,IAAI;gBAAE;YACX,IAAI,GAAG,CAAC,OAAO;AAAE,gBAAA,OAAO,KAAK,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC;YAC5C,MAAM,GAAG,GAAI,EAAE,CAAC,aAAa,CAAC,KAAK,CAAwB,IAAI,EAAE;AACjE,YAAA,MAAM,CAAC,GAAG,GAAG,CAAC,qBAAqB,EAAE;AACrC,YAAA,MAAM,CAAC,GAAG,IAAI,CAAC,qBAAqB,EAAE;YACtC,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,CAAC,IAAI,CAAgB;AAChD,YAAA,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC;AAC3B,YAAA,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC;AACzC,YAAA,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA,EAAG,CAAC,CAAC,IAAI,CAAA,EAAA,CAAI,EAAE,GAAG,EAAE,CAAA,EAAG,CAAC,CAAC,GAAG,CAAA,EAAA,CAAI,EAAE,KAAK,EAAE,CAAA,EAAG,CAAC,CAAC,KAAK,CAAA,EAAA,CAAI,EAAE,MAAM,EAAE,CAAA,EAAG,CAAC,CAAC,MAAM,CAAA,EAAA,CAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAC7O,YAAA,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;YAChC,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC;AAC/H,YAAA,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;YAC1E,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,CAAA,UAAA,EAAa,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA,UAAA,EAAa,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA,CAAA,CAAG,EAAE,OAAO,EAAE,CAAC,KAAK,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,2BAA2B,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAC/S,YAAA,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AACpD,YAAA,IAAI,IAAI;AAAE,gBAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC;;AACnD,gBAAA,IAAI,EAAE;YACX,GAAG,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC;YACnC,OAAO,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,SAAS,CAAC;QAC9C,CAAC;AACF,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,YAAY;AAClB,QAAA,IAAI,EAAE,OAAO;AACb,QAAA,WAAW,EAAE,kIAAkI;QAC/I,QAAQ,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE;QACxC,GAAG,EAAE,CAAC,EAAe,EAAE,CAAM,EAAE,GAAkB,KAAI;AACnD,YAAA,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,WAAW,IAAI,EAAE;AACrD,YAAA,EAAE,CAAC,OAAO,CAAC,OAAO,GAAG,EAAE;YACvB,IAAI,GAAG,CAAC,OAAO;gBAAE,OAAO,MAAM,EAAE,CAAC,WAAW,GAAG,EAAE,CAAC;YAClD,MAAM,IAAI,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,EAAE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC;AAC7D,YAAA,EAAE,CAAC,WAAW,GAAG,EAAE;YACnB,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;AACxC,YAAA,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,KAAI;gBACzC,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC;AAC3C,gBAAA,IAAI,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC;AACxC,gBAAA,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,cAAc;gBACnC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;AAC9G,gBAAA,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC;gBACpB,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;gBAC5M,UAAU,CAAC,OAAO,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC;AACzE,gBAAA,OAAO,CAAC;AACV,YAAA,CAAC,CAAC;AACF,YAAA,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAK;AACjF,gBAAA,EAAE,CAAC,WAAW,GAAG,EAAE;AACnB,gBAAA,EAAE,CAAC,eAAe,CAAC,YAAY,CAAC;AAClC,YAAA,CAAC,CAAC;QACJ,CAAC;AACF,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,aAAa;AACnB,QAAA,IAAI,EAAE,MAAM;AACZ,QAAA,WAAW,EAAE,iGAAiG;QAC9G,QAAQ,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE,MAAM,EAAE,IAAI,EAAE;AACxD,QAAA,OAAO,EAAE,MAAM;QACf,GAAG,EAAE,CAAC,EAAe,EAAE,CAAM,EAAE,GAAkB,KAAI;AACnD,YAAA,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC,KAAK,CAAA,CAAE,EAAE,EAAE,EAAE,SAAS,EAAE,0BAA0B,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;YAC7K,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,MAAM,EAAE;AAC9B,YAAA,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC;AACnB,YAAA,OAAO,IAAI;QACb,CAAC;AACF,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,YAAY;AAClB,QAAA,IAAI,EAAE,OAAO;AACb,QAAA,WAAW,EAAE,sFAAsF;AACnG,QAAA,QAAQ,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE;AAC3B,QAAA,OAAO,EAAE,MAAM;QACf,GAAG,EAAE,CAAC,EAAe,EAAE,CAAM,EAAE,GAAkB,KAAI;YACnD,MAAM,CAAC,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC;AACxC,YAAA,CAAC,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC;AACrC,YAAA,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,mFAAmF,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;AAC7M,YAAA,IAAI,gBAAgB,CAAC,EAAE,CAAC,CAAC,QAAQ,KAAK,QAAQ;AAAE,gBAAA,EAAE,CAAC,KAAK,CAAC,QAAQ,GAAG,UAAU;AAC9E,YAAA,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ;AAC9B,YAAA,EAAE,CAAC,KAAK,CAAC,QAAQ,GAAG,QAAQ;AAC5B,YAAA,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;AACjB,YAAA,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,mBAAmB,EAAE,EAAE,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;YAClJ,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;AAC1D,YAAA,IAAI,CAAC;gBAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC;;AAC3B,gBAAA,GAAG,EAAE;YACV,OAAO,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,SAAS,CAAC;QAC3C,CAAC;AACF,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,WAAW;AACjB,QAAA,IAAI,EAAE,WAAW;AACjB,QAAA,WAAW,EAAE,kDAAkD;AAC/D,QAAA,QAAQ,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE;AAC3B,QAAA,GAAG,EAAE,CAAC,EAAe,EAAE,CAAM,EAAE,GAAkB,KAC/C,GAAG,CAAC;cACA,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;cACnE,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,yBAAyB,EAAE,EAAE,EAAE,SAAS,EAAE,yBAAyB,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,0BAA0B,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AACvP,KAAA;;AAGH;SACgB,gBAAgB,GAAA;IAC9B,eAAe,CAAC,OAAO,CAAC;AAC1B;;;;"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var registry = require('../chunks/registry-DehBVRDV.cjs');
|
|
4
|
+
require('../chunks/base-BaQV-2ha.cjs');
|
|
5
|
+
|
|
6
|
+
/** Fan-out angles (deg) for `n` floating emoji, centred on straight up (7.4). */
|
|
7
|
+
function fanAngles(n, spread = 60) {
|
|
8
|
+
if (n <= 1)
|
|
9
|
+
return [0];
|
|
10
|
+
return Array.from({ length: n }, (_, i) => Math.round(-spread / 2 + (spread * i) / (n - 1)));
|
|
11
|
+
}
|
|
12
|
+
const span = (cls, text = '') => {
|
|
13
|
+
const s = document.createElement('span');
|
|
14
|
+
s.className = cls;
|
|
15
|
+
s.setAttribute('aria-hidden', 'true');
|
|
16
|
+
s.textContent = text;
|
|
17
|
+
return s;
|
|
18
|
+
};
|
|
19
|
+
const SOCIAL_FX = [
|
|
20
|
+
{
|
|
21
|
+
name: 'typing-dots',
|
|
22
|
+
kind: 'loop',
|
|
23
|
+
description: 'Three dots bounce in a wave inside the element — the “is typing…” indicator (`color`, `period`).',
|
|
24
|
+
defaults: { color: 'currentColor', period: 1100 },
|
|
25
|
+
run: (el, o, ctx) => {
|
|
26
|
+
const box = span('usa-fx-typing');
|
|
27
|
+
Object.assign(box.style, { display: 'inline-flex', gap: '4px', alignItems: 'center', padding: '2px 0' });
|
|
28
|
+
const dots = [0, 1, 2].map(() => {
|
|
29
|
+
const d = span('');
|
|
30
|
+
Object.assign(d.style, { width: '7px', height: '7px', borderRadius: '50%', background: o.color, opacity: '.6', display: 'inline-block' });
|
|
31
|
+
box.appendChild(d);
|
|
32
|
+
return d;
|
|
33
|
+
});
|
|
34
|
+
el.appendChild(box);
|
|
35
|
+
const anims = ctx.reduced ? [] : dots.map((d, i) => ctx.animate(d, [{ transform: 'translateY(0)', opacity: 0.4 }, { transform: 'translateY(-5px)', opacity: 1, offset: 0.3 }, { transform: 'translateY(0)', opacity: 0.4, offset: 0.6 }, { transform: 'translateY(0)', opacity: 0.4 }], { duration: o.period, delay: (i * o.period) / 6, iterations: Infinity }));
|
|
36
|
+
const stop = () => (anims.forEach((a) => a?.cancel()), box.remove());
|
|
37
|
+
ctx.onCleanup(stop);
|
|
38
|
+
return stop;
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'message-in',
|
|
43
|
+
kind: 'enter',
|
|
44
|
+
description: 'A chat bubble pops in from its side (`side` left | right, or `data-side`) with a small overshoot (`duration`).',
|
|
45
|
+
defaults: { side: '', duration: 420 },
|
|
46
|
+
run: (el, o, ctx) => {
|
|
47
|
+
const side = o.side || el.dataset.side || 'left';
|
|
48
|
+
if (ctx.reduced)
|
|
49
|
+
return ctx.animate(el, [{ opacity: 0 }, { opacity: 1 }], { duration: 200 })?.finished.catch(() => undefined);
|
|
50
|
+
el.style.transformOrigin = side === 'right' ? '100% 100%' : '0 100%';
|
|
51
|
+
return ctx.animate(el, [{ transform: `translateX(${side === 'right' ? 24 : -24}px) scale(.6)`, opacity: 0 }, { transform: 'scale(1.04)', opacity: 1, offset: 0.65 }, { transform: 'none', opacity: 1 }], { duration: o.duration, easing: 'cubic-bezier(.2,.9,.3,1)' })?.finished.catch(() => undefined);
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'reaction-burst',
|
|
56
|
+
kind: 'click',
|
|
57
|
+
description: 'The reaction emoji (`emoji`, default the element text) floats up in a small fan and fades (`count`, `spread`).',
|
|
58
|
+
defaults: { emoji: '', count: 5, spread: 70, duration: 900 },
|
|
59
|
+
reduced: 'skip',
|
|
60
|
+
run: (el, o, ctx) => {
|
|
61
|
+
const r = el.getBoundingClientRect();
|
|
62
|
+
const emo = o.emoji || (el.textContent || '❤️').trim().slice(0, 2) || '❤️';
|
|
63
|
+
const parts = fanAngles(o.count, o.spread).map((deg, i) => {
|
|
64
|
+
const s = span('', emo);
|
|
65
|
+
Object.assign(s.style, { position: 'fixed', left: `${r.left + r.width / 2 - 10}px`, top: `${r.top}px`, fontSize: '20px', pointerEvents: 'none', zIndex: '2147483000' });
|
|
66
|
+
document.body.appendChild(s);
|
|
67
|
+
const rad = (deg * Math.PI) / 180;
|
|
68
|
+
const dist = 50 + (i % 2) * 18;
|
|
69
|
+
const a = ctx.animate(s, [{ transform: 'translate(0,0) scale(.4)', opacity: 0 }, { transform: `translate(${(Math.sin(rad) * dist * 0.5).toFixed(1)}px,${(-dist * 0.5).toFixed(1)}px) scale(1.1)`, opacity: 1, offset: 0.3 }, { transform: `translate(${(Math.sin(rad) * dist).toFixed(1)}px,${(-Math.cos(rad) * dist - 20).toFixed(1)}px) scale(.9)`, opacity: 0 }], { duration: o.duration, delay: i * 40, easing: 'ease-out' });
|
|
70
|
+
const end = () => s.remove();
|
|
71
|
+
if (a)
|
|
72
|
+
a.finished.then(end, end);
|
|
73
|
+
else
|
|
74
|
+
end();
|
|
75
|
+
ctx.onCleanup(end);
|
|
76
|
+
return a;
|
|
77
|
+
});
|
|
78
|
+
return Promise.all(parts.map((a) => a?.finished.catch(() => undefined)));
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'read-receipt',
|
|
83
|
+
kind: 'enter',
|
|
84
|
+
description: '✓✓ ticks draw in one after the other and turn `color` (blue) — “seen” (`color`).',
|
|
85
|
+
defaults: { color: '#3b82f6', duration: 600 },
|
|
86
|
+
run: (el, o, ctx) => {
|
|
87
|
+
let t = el.querySelector('.usa-fx-ticks');
|
|
88
|
+
if (!t) {
|
|
89
|
+
t = span('usa-fx-ticks', '✓✓');
|
|
90
|
+
Object.assign(t.style, { display: 'inline-block', marginLeft: '4px', letterSpacing: '-4px', fontWeight: '700' });
|
|
91
|
+
el.appendChild(t);
|
|
92
|
+
}
|
|
93
|
+
if (ctx.reduced)
|
|
94
|
+
return void (t.style.color = o.color);
|
|
95
|
+
return ctx.animate(t, [{ clipPath: 'inset(0 100% 0 0)', color: 'currentColor' }, { clipPath: 'inset(0 0 0 0)', color: 'currentColor', offset: 0.6 }, { clipPath: 'inset(0 0 0 0)', color: o.color }], { duration: o.duration, easing: 'ease-out', fill: 'forwards' })?.finished.catch(() => undefined);
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: 'mention-glow',
|
|
100
|
+
kind: 'attention',
|
|
101
|
+
description: 'A soft highlight sweeps in behind an @mention, then settles (`color`).',
|
|
102
|
+
defaults: { color: 'rgba(250,204,21,.45)', duration: 900 },
|
|
103
|
+
run: (el, o, ctx) => {
|
|
104
|
+
el.style.backgroundImage = `linear-gradient(${o.color},${o.color})`;
|
|
105
|
+
el.style.backgroundRepeat = 'no-repeat';
|
|
106
|
+
el.style.borderRadius = el.style.borderRadius || '4px';
|
|
107
|
+
if (ctx.reduced)
|
|
108
|
+
return void (el.style.backgroundSize = '100% 100%');
|
|
109
|
+
return ctx.animate(el, [{ backgroundSize: '0% 100%' }, { backgroundSize: '100% 100%' }], { duration: o.duration, easing: 'cubic-bezier(.3,.8,.3,1)', fill: 'forwards' })?.finished.catch(() => undefined);
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
/** Register the 7.4 chat & social pack (idempotent). */
|
|
114
|
+
function registerSocialPack() {
|
|
115
|
+
registry.registerEffects(SOCIAL_FX);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
exports.SOCIAL_FX = SOCIAL_FX;
|
|
119
|
+
exports.fanAngles = fanAngles;
|
|
120
|
+
exports.registerSocialPack = registerSocialPack;
|
|
121
|
+
//# sourceMappingURL=fx-social.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fx-social.cjs","sources":["../../src/components/fx2/social.ts"],"sourcesContent":["/**\n * 7.4 — Chat & social motion (`motionary/fx/social`, also\n * `motionary/components/fx-social`):\n *\n * - `typing-dots` (loop) — three dots bounce in a wave inside the element.\n * - `message-in` (enter) — a chat bubble pops in from its side (`side`\n * \"left\" | \"right\", or `data-side`), with a little overshoot.\n * - `reaction-burst` (click) — the element's emoji (`emoji`) floats up in a\n * small fan and fades.\n * - `read-receipt` (enter) — ✓✓ ticks draw in and turn blue (`color`).\n * - `mention-glow` (attention) — a soft highlight sweeps behind an @mention.\n *\n * Reduced motion: typing-dots shows static dots, message-in / read-receipt\n * fade, reaction-burst does nothing, mention-glow sets a static highlight.\n */\nimport type { EffectContext, EffectDefinition } from '../fx/registry';\nimport { registerEffects } from '../fx/registry';\n\n/** Fan-out angles (deg) for `n` floating emoji, centred on straight up (7.4). */\nexport function fanAngles(n: number, spread = 60): number[] {\n if (n <= 1) return [0];\n return Array.from({ length: n }, (_, i) => Math.round(-spread / 2 + (spread * i) / (n - 1)));\n}\n\nconst span = (cls: string, text = ''): HTMLSpanElement => {\n const s = document.createElement('span');\n s.className = cls;\n s.setAttribute('aria-hidden', 'true');\n s.textContent = text;\n return s;\n};\n\nexport const SOCIAL_FX: EffectDefinition[] = [\n {\n name: 'typing-dots',\n kind: 'loop',\n description: 'Three dots bounce in a wave inside the element — the “is typing…” indicator (`color`, `period`).',\n defaults: { color: 'currentColor', period: 1100 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const box = span('usa-fx-typing');\n Object.assign(box.style, { display: 'inline-flex', gap: '4px', alignItems: 'center', padding: '2px 0' });\n const dots = [0, 1, 2].map(() => {\n const d = span('');\n Object.assign(d.style, { width: '7px', height: '7px', borderRadius: '50%', background: o.color, opacity: '.6', display: 'inline-block' });\n box.appendChild(d);\n return d;\n });\n el.appendChild(box);\n const anims = ctx.reduced ? [] : dots.map((d, i) => ctx.animate(d, [{ transform: 'translateY(0)', opacity: 0.4 }, { transform: 'translateY(-5px)', opacity: 1, offset: 0.3 }, { transform: 'translateY(0)', opacity: 0.4, offset: 0.6 }, { transform: 'translateY(0)', opacity: 0.4 }], { duration: o.period, delay: (i * o.period) / 6, iterations: Infinity }));\n const stop = () => (anims.forEach((a) => a?.cancel()), box.remove());\n ctx.onCleanup(stop);\n return stop;\n },\n },\n {\n name: 'message-in',\n kind: 'enter',\n description: 'A chat bubble pops in from its side (`side` left | right, or `data-side`) with a small overshoot (`duration`).',\n defaults: { side: '', duration: 420 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const side = o.side || el.dataset.side || 'left';\n if (ctx.reduced) return ctx.animate(el, [{ opacity: 0 }, { opacity: 1 }], { duration: 200 })?.finished.catch(() => undefined);\n el.style.transformOrigin = side === 'right' ? '100% 100%' : '0 100%';\n return ctx.animate(el, [{ transform: `translateX(${side === 'right' ? 24 : -24}px) scale(.6)`, opacity: 0 }, { transform: 'scale(1.04)', opacity: 1, offset: 0.65 }, { transform: 'none', opacity: 1 }], { duration: o.duration, easing: 'cubic-bezier(.2,.9,.3,1)' })?.finished.catch(() => undefined);\n },\n },\n {\n name: 'reaction-burst',\n kind: 'click',\n description: 'The reaction emoji (`emoji`, default the element text) floats up in a small fan and fades (`count`, `spread`).',\n defaults: { emoji: '', count: 5, spread: 70, duration: 900 },\n reduced: 'skip',\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n const r = el.getBoundingClientRect();\n const emo = o.emoji || (el.textContent || '❤️').trim().slice(0, 2) || '❤️';\n const parts = fanAngles(o.count, o.spread).map((deg, i) => {\n const s = span('', emo);\n Object.assign(s.style, { position: 'fixed', left: `${r.left + r.width / 2 - 10}px`, top: `${r.top}px`, fontSize: '20px', pointerEvents: 'none', zIndex: '2147483000' });\n document.body.appendChild(s);\n const rad = (deg * Math.PI) / 180;\n const dist = 50 + (i % 2) * 18;\n const a = ctx.animate(s, [{ transform: 'translate(0,0) scale(.4)', opacity: 0 }, { transform: `translate(${(Math.sin(rad) * dist * 0.5).toFixed(1)}px,${(-dist * 0.5).toFixed(1)}px) scale(1.1)`, opacity: 1, offset: 0.3 }, { transform: `translate(${(Math.sin(rad) * dist).toFixed(1)}px,${(-Math.cos(rad) * dist - 20).toFixed(1)}px) scale(.9)`, opacity: 0 }], { duration: o.duration, delay: i * 40, easing: 'ease-out' });\n const end = () => s.remove();\n if (a) a.finished.then(end, end);\n else end();\n ctx.onCleanup(end);\n return a;\n });\n return Promise.all(parts.map((a) => a?.finished.catch(() => undefined)));\n },\n },\n {\n name: 'read-receipt',\n kind: 'enter',\n description: '✓✓ ticks draw in one after the other and turn `color` (blue) — “seen” (`color`).',\n defaults: { color: '#3b82f6', duration: 600 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n let t = el.querySelector('.usa-fx-ticks') as HTMLElement | null;\n if (!t) {\n t = span('usa-fx-ticks', '✓✓');\n Object.assign(t.style, { display: 'inline-block', marginLeft: '4px', letterSpacing: '-4px', fontWeight: '700' });\n el.appendChild(t);\n }\n if (ctx.reduced) return void (t.style.color = o.color);\n return ctx.animate(t, [{ clipPath: 'inset(0 100% 0 0)', color: 'currentColor' }, { clipPath: 'inset(0 0 0 0)', color: 'currentColor', offset: 0.6 }, { clipPath: 'inset(0 0 0 0)', color: o.color }], { duration: o.duration, easing: 'ease-out', fill: 'forwards' })?.finished.catch(() => undefined);\n },\n },\n {\n name: 'mention-glow',\n kind: 'attention',\n description: 'A soft highlight sweeps in behind an @mention, then settles (`color`).',\n defaults: { color: 'rgba(250,204,21,.45)', duration: 900 },\n run: (el: HTMLElement, o: any, ctx: EffectContext) => {\n el.style.backgroundImage = `linear-gradient(${o.color},${o.color})`;\n el.style.backgroundRepeat = 'no-repeat';\n el.style.borderRadius = el.style.borderRadius || '4px';\n if (ctx.reduced) return void (el.style.backgroundSize = '100% 100%');\n return ctx.animate(el, [{ backgroundSize: '0% 100%' }, { backgroundSize: '100% 100%' }], { duration: o.duration, easing: 'cubic-bezier(.3,.8,.3,1)', fill: 'forwards' })?.finished.catch(() => undefined);\n },\n },\n];\n\n/** Register the 7.4 chat & social pack (idempotent). */\nexport function registerSocialPack(): void {\n registerEffects(SOCIAL_FX);\n}\n"],"names":["registerEffects"],"mappings":"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"}
|