@sfinterface/numbers 0.1.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/dist/index.js ADDED
@@ -0,0 +1,343 @@
1
+ "use client";
2
+
3
+ // src/Numbers.tsx
4
+ import * as React from "react";
5
+
6
+ // src/utils/cx.ts
7
+ function cx(...values) {
8
+ let out = "";
9
+ for (const value of values) {
10
+ if (!value && value !== 0) continue;
11
+ const part = Array.isArray(value) ? cx(...value) : String(value);
12
+ if (part) out += out ? " " + part : part;
13
+ }
14
+ return out;
15
+ }
16
+
17
+ // src/utils/refs.ts
18
+ function assignRef(ref, value) {
19
+ if (!ref) return;
20
+ if (typeof ref === "function") ref(value);
21
+ else ref.current = value;
22
+ }
23
+ function mergeRefs(...refs) {
24
+ return (value) => {
25
+ for (const ref of refs) assignRef(ref, value);
26
+ };
27
+ }
28
+
29
+ // src/Numbers.tsx
30
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
31
+ var CYCLE = 10;
32
+ var HOME = CYCLE;
33
+ var CELLS = Array.from({ length: CYCLE * 3 }, (_, i) => i % CYCLE);
34
+ function numeralsOf(locale, format) {
35
+ let glyphs = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
36
+ try {
37
+ const numberingSystem = new Intl.NumberFormat(locale, format).resolvedOptions().numberingSystem;
38
+ const plain = new Intl.NumberFormat(locale, { numberingSystem, useGrouping: false });
39
+ glyphs = Array.from({ length: CYCLE }, (_, i) => plain.format(i));
40
+ } catch {
41
+ }
42
+ return { glyphs, index: new Map(glyphs.map((glyph, i) => [glyph, i])) };
43
+ }
44
+ function cellsFor(value, locale, format, index) {
45
+ const parts = new Intl.NumberFormat(locale, format).formatToParts(value);
46
+ let integers = 0;
47
+ for (const part of parts) if (part.type === "integer") integers += part.value.length;
48
+ const out = [];
49
+ let seen = 0;
50
+ let fraction = 0;
51
+ let marks = 0;
52
+ for (const part of parts) {
53
+ if (part.type === "integer") {
54
+ for (const glyph of part.value) {
55
+ out.push({ key: `d${integers - 1 - seen}`, kind: "digit", digit: index.get(glyph) ?? 0 });
56
+ seen += 1;
57
+ }
58
+ } else if (part.type === "fraction") {
59
+ for (const glyph of part.value) {
60
+ fraction += 1;
61
+ out.push({ key: `f${fraction}`, kind: "digit", digit: index.get(glyph) ?? 0 });
62
+ }
63
+ } else if (part.type === "group") {
64
+ out.push({ key: `g${integers - 1 - seen}`, kind: "mark", text: part.value });
65
+ } else {
66
+ marks += 1;
67
+ out.push({ key: `${part.type}${marks}`, kind: "mark", text: part.value });
68
+ }
69
+ }
70
+ return out;
71
+ }
72
+ function turn(from, to, dir) {
73
+ if (from === to) return 0;
74
+ const forward = ((to - from) * dir % CYCLE + CYCLE) % CYCLE;
75
+ return dir * forward;
76
+ }
77
+ function ms(value, fallback) {
78
+ const n = parseFloat(value);
79
+ if (!Number.isFinite(n)) return fallback;
80
+ return value.trim().endsWith("s") && !value.trim().endsWith("ms") ? n * 1e3 : n;
81
+ }
82
+ function px(value, fontSize, fallback) {
83
+ const n = parseFloat(value);
84
+ if (!Number.isFinite(n)) return fallback;
85
+ return value.includes("em") ? n * fontSize : n;
86
+ }
87
+ var reduced = () => typeof window !== "undefined" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
88
+ var Numbers = React.forwardRef(function Numbers2({ value, locale, format, transition = "roll", trend = "auto", blur = true, fade, duration, label, className, ...props }, ref) {
89
+ const numerals = React.useMemo(() => numeralsOf(locale, format), [locale, format]);
90
+ const cells = React.useMemo(
91
+ () => cellsFor(value, locale, format, numerals.index),
92
+ [value, locale, format, numerals]
93
+ );
94
+ const text = React.useMemo(() => new Intl.NumberFormat(locale, format).format(value), [value, locale, format]);
95
+ const host = React.useRef(null);
96
+ const row = React.useRef(null);
97
+ const shown = React.useRef(/* @__PURE__ */ new Map());
98
+ const seats = React.useRef(/* @__PURE__ */ new Map());
99
+ const previous = React.useRef(value);
100
+ const widthSeat = React.useRef(null);
101
+ const widthRun = React.useRef(null);
102
+ const [leaving, setLeaving] = React.useState([]);
103
+ const heading = React.useRef(1);
104
+ const spun = React.useRef(/* @__PURE__ */ new Set());
105
+ const [outgoing, setOutgoing] = React.useState(() => /* @__PURE__ */ new Map());
106
+ const outgoingTimer = React.useRef(null);
107
+ const [generation, setGeneration] = React.useState(0);
108
+ React.useEffect(() => () => {
109
+ if (outgoingTimer.current) clearTimeout(outgoingTimer.current);
110
+ }, []);
111
+ React.useLayoutEffect(() => {
112
+ const rowEl = row.current;
113
+ const hostEl = host.current;
114
+ if (!rowEl || !hostEl) return;
115
+ const from = previous.current;
116
+ previous.current = value;
117
+ const still = reduced();
118
+ const cs = getComputedStyle(rowEl);
119
+ const fontSize = parseFloat(cs.fontSize) || 16;
120
+ const roll = duration ?? ms(cs.getPropertyValue("--sfi-numbers-roll"), 520);
121
+ const shift = ms(cs.getPropertyValue("--sfi-settle"), 240);
122
+ const ease = cs.getPropertyValue("--sfi-ease").trim() || "ease";
123
+ const deepest = px(cs.getPropertyValue("--sfi-numbers-blur"), fontSize, fontSize * 0.1);
124
+ const dir = trend === "up" ? 1 : trend === "down" ? -1 : value >= from ? 1 : -1;
125
+ heading.current = dir;
126
+ const nextSeats = /* @__PURE__ */ new Map();
127
+ const gone = new Map(shown.current);
128
+ const swapped = /* @__PURE__ */ new Map();
129
+ rowEl.querySelectorAll("[data-cell]").forEach((el) => {
130
+ const key = el.dataset.cell;
131
+ const seat = el.offsetLeft;
132
+ nextSeats.set(key, seat);
133
+ gone.delete(key);
134
+ const was = seats.current.get(key);
135
+ const before = shown.current.get(key);
136
+ if (was === void 0) {
137
+ if (!still && seats.current.size > 0) el.setAttribute("data-arriving", "");
138
+ return;
139
+ }
140
+ el.removeAttribute("data-arriving");
141
+ if (still) return;
142
+ const dx = was - seat;
143
+ if (Math.abs(dx) >= 1) {
144
+ el.animate([{ transform: `translateX(${dx}px)` }, { transform: "translateX(0)" }], {
145
+ duration: shift,
146
+ easing: ease
147
+ });
148
+ }
149
+ const digit = el.dataset.digit;
150
+ if (digit === void 0 || !before || before.kind !== "digit") return;
151
+ if (Number(digit) === before.digit) return;
152
+ if (transition !== "roll") {
153
+ swapped.set(key, before.digit);
154
+ return;
155
+ }
156
+ const steps = turn(before.digit, Number(digit), dir);
157
+ if (steps === 0) return;
158
+ const strip = el.querySelector(".sfi-numbers-strip");
159
+ const cell = strip?.firstElementChild;
160
+ if (!strip || !cell) return;
161
+ const height = cell.getBoundingClientRect().height;
162
+ if (!height) return;
163
+ const start2 = (HOME + before.digit) * height;
164
+ const turning = strip.animate(
165
+ [{ transform: `translateY(${-start2}px)` }, { transform: `translateY(${-(start2 + steps * height)}px)` }],
166
+ { duration: roll, easing: ease }
167
+ );
168
+ el.setAttribute("data-turning", "");
169
+ turning.finished.then(() => el.removeAttribute("data-turning")).catch(() => {
170
+ });
171
+ if (blur && deepest > 0) {
172
+ const depth = Math.min(1, Math.abs(steps) / 4) * deepest;
173
+ el.animate(
174
+ [
175
+ { filter: "blur(0px)", offset: 0 },
176
+ { filter: `blur(${depth.toFixed(2)}px)`, offset: 0.15 },
177
+ { filter: "blur(0px)", offset: 0.62 },
178
+ { filter: "blur(0px)", offset: 1 }
179
+ ],
180
+ { duration: roll, easing: "linear" }
181
+ );
182
+ }
183
+ });
184
+ if (gone.size && !still) {
185
+ const ghosts = [];
186
+ gone.forEach((cell, key) => {
187
+ const seat = seats.current.get(key);
188
+ if (seat !== void 0) ghosts.push({ cell, left: seat });
189
+ });
190
+ if (ghosts.length) setLeaving((current) => [...current, ...ghosts]);
191
+ }
192
+ shown.current = new Map(cells.map((cell) => [cell.key, cell]));
193
+ seats.current = nextSeats;
194
+ if (swapped.size && !still) {
195
+ hostEl.style.setProperty("--sfi-numbers-dir", String(dir));
196
+ setOutgoing(swapped);
197
+ setGeneration((n) => n + 1);
198
+ if (outgoingTimer.current) clearTimeout(outgoingTimer.current);
199
+ outgoingTimer.current = setTimeout(() => {
200
+ outgoingTimer.current = null;
201
+ setOutgoing(/* @__PURE__ */ new Map());
202
+ }, roll + 40);
203
+ }
204
+ const running = widthRun.current;
205
+ const visual = running ? hostEl.getBoundingClientRect().width : null;
206
+ if (running) {
207
+ running.cancel();
208
+ widthRun.current = null;
209
+ }
210
+ const now = rowEl.offsetWidth;
211
+ const start = visual ?? widthSeat.current;
212
+ widthSeat.current = now;
213
+ if (start === null || Math.abs(start - now) < 0.5 || still) return;
214
+ const grow = hostEl.animate([{ width: `${start}px` }, { width: `${now}px` }], {
215
+ duration: shift,
216
+ easing: ease
217
+ });
218
+ widthRun.current = grow;
219
+ grow.finished.then(() => {
220
+ if (widthRun.current === grow) widthRun.current = null;
221
+ }).catch(() => {
222
+ });
223
+ }, [cells, value, trend, blur, duration, transition]);
224
+ React.useLayoutEffect(() => {
225
+ const rowEl = row.current;
226
+ if (!rowEl || leaving.length === 0 || reduced()) return;
227
+ const cs = getComputedStyle(rowEl);
228
+ const fontSize = parseFloat(cs.fontSize) || 16;
229
+ const going = ms(cs.getPropertyValue("--sfi-numbers-exit"), 240);
230
+ const ease = cs.getPropertyValue("--sfi-ease").trim() || "ease";
231
+ const deepest = px(cs.getPropertyValue("--sfi-numbers-blur"), fontSize, fontSize * 0.1);
232
+ const dir = heading.current;
233
+ rowEl.querySelectorAll("[data-ghost]").forEach((el) => {
234
+ const key = el.dataset.ghost;
235
+ if (spun.current.has(key)) return;
236
+ spun.current.add(key);
237
+ const strip = el.querySelector(".sfi-numbers-strip");
238
+ const cell = strip?.firstElementChild;
239
+ if (!strip || !cell) return;
240
+ const height = cell.getBoundingClientRect().height;
241
+ if (!height) return;
242
+ const start = (HOME + Number(el.dataset.digit ?? 0)) * height;
243
+ strip.animate(
244
+ [{ transform: `translateY(${-start}px)` }, { transform: `translateY(${-(start + dir * 3 * height)}px)` }],
245
+ { duration: going, easing: ease }
246
+ );
247
+ if (blur && deepest > 0) {
248
+ el.animate([{ filter: "blur(0px)" }, { filter: `blur(${deepest.toFixed(2)}px)` }], {
249
+ duration: going,
250
+ easing: ease
251
+ });
252
+ }
253
+ });
254
+ }, [leaving, blur, transition]);
255
+ const setHost = React.useMemo(() => mergeRefs(ref, host), [ref]);
256
+ const knobs = {};
257
+ if (duration !== void 0) knobs["--sfi-numbers-roll"] = `${duration}ms`;
258
+ if (fade !== void 0) knobs["--sfi-numbers-fade"] = `${fade}`;
259
+ const style = Object.keys(knobs).length ? { ...knobs, ...props.style } : props.style;
260
+ return /* @__PURE__ */ jsxs(
261
+ "span",
262
+ {
263
+ ref: setHost,
264
+ className: cx("sfi-numbers", className),
265
+ "data-transition": transition === "roll" ? void 0 : transition,
266
+ ...props,
267
+ style,
268
+ children: [
269
+ /* @__PURE__ */ jsx("span", { className: "sfi-numbers-said", children: label ?? text }),
270
+ /* @__PURE__ */ jsxs("span", { className: "sfi-numbers-row", ref: row, "aria-hidden": "true", children: [
271
+ cells.map((cell) => /* @__PURE__ */ jsx(
272
+ Piece,
273
+ {
274
+ cell,
275
+ glyphs: numerals.glyphs,
276
+ transition,
277
+ from: cell.kind === "digit" ? outgoing.get(cell.key) : void 0,
278
+ generation
279
+ },
280
+ cell.key
281
+ )),
282
+ leaving.map((ghost) => /* @__PURE__ */ jsx(
283
+ Piece,
284
+ {
285
+ cell: ghost.cell,
286
+ glyphs: numerals.glyphs,
287
+ transition,
288
+ generation,
289
+ leaving: true,
290
+ left: ghost.left,
291
+ onDone: () => {
292
+ spun.current.delete(ghost.cell.key);
293
+ setLeaving((current) => current.filter((x) => x.cell.key !== ghost.cell.key));
294
+ }
295
+ },
296
+ `leaving:${ghost.cell.key}`
297
+ ))
298
+ ] })
299
+ ]
300
+ }
301
+ );
302
+ });
303
+ var at = (digit) => ({ "--sfi-numbers-at": HOME + digit });
304
+ function Piece({
305
+ cell,
306
+ glyphs,
307
+ transition,
308
+ from,
309
+ generation,
310
+ leaving,
311
+ left,
312
+ onDone
313
+ }) {
314
+ const identity = leaving ? {
315
+ "data-leaving": "",
316
+ "data-ghost": cell.key,
317
+ style: { left },
318
+ onAnimationEnd: onDone
319
+ } : {
320
+ "data-cell": cell.key,
321
+ onAnimationEnd: (event) => event.currentTarget.removeAttribute("data-arriving")
322
+ };
323
+ if (cell.kind === "mark") {
324
+ return /* @__PURE__ */ jsx("span", { className: "sfi-numbers-mark", ...identity, children: cell.text });
325
+ }
326
+ const swapping = from !== void 0 && from !== cell.digit;
327
+ return /* @__PURE__ */ jsx("span", { className: "sfi-numbers-slot", "data-digit": cell.digit, ...identity, children: /* @__PURE__ */ jsx("span", { className: "sfi-numbers-window", children: transition === "roll" ? /* @__PURE__ */ jsx("span", { className: "sfi-numbers-strip", style: at(cell.digit), children: CELLS.map((n, i) => /* @__PURE__ */ jsx("span", { className: "sfi-numbers-cell", children: glyphs[n] }, i)) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
328
+ /* @__PURE__ */ jsx(
329
+ "span",
330
+ {
331
+ className: "sfi-numbers-face",
332
+ "data-in": swapping ? "" : void 0,
333
+ children: glyphs[cell.digit]
334
+ },
335
+ `in:${cell.digit}:${swapping ? generation : "rest"}`
336
+ ),
337
+ swapping ? /* @__PURE__ */ jsx("span", { className: "sfi-numbers-face", "data-out": "", children: glyphs[from] }, `out:${from}:${generation}`) : null
338
+ ] }) }) });
339
+ }
340
+ export {
341
+ Numbers
342
+ };
343
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/Numbers.tsx","../src/utils/cx.ts","../src/utils/refs.ts"],"sourcesContent":["import * as React from \"react\";\nimport { cx } from \"./utils/cx\";\nimport { mergeRefs } from \"./utils/refs\";\n\nexport type NumbersTransition = \"roll\" | \"tick\" | \"blur\" | \"flip\" | \"scale\";\n\nexport interface NumbersProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, \"children\"> {\n value: number;\n locale?: string | string[];\n format?: Intl.NumberFormatOptions;\n trend?: \"auto\" | \"up\" | \"down\";\n transition?: NumbersTransition;\n blur?: boolean;\n fade?: number;\n duration?: number;\n label?: string;\n}\n\nconst CYCLE = 10;\nconst HOME = CYCLE;\n\nconst CELLS = Array.from({ length: CYCLE * 3 }, (_, i) => i % CYCLE);\n\nfunction numeralsOf(locale: NumbersProps[\"locale\"], format: NumbersProps[\"format\"]) {\n let glyphs = [\"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\"];\n try {\n const numberingSystem = new Intl.NumberFormat(locale, format).resolvedOptions().numberingSystem;\n const plain = new Intl.NumberFormat(locale, { numberingSystem, useGrouping: false });\n glyphs = Array.from({ length: CYCLE }, (_, i) => plain.format(i));\n } catch {\n }\n return { glyphs, index: new Map(glyphs.map((glyph, i) => [glyph, i])) };\n}\n\ntype Cell =\n | { key: string; kind: \"digit\"; digit: number }\n | { key: string; kind: \"mark\"; text: string };\n\nfunction cellsFor(\n value: number,\n locale: NumbersProps[\"locale\"],\n format: NumbersProps[\"format\"],\n index: Map<string, number>,\n): Cell[] {\n const parts = new Intl.NumberFormat(locale, format).formatToParts(value);\n let integers = 0;\n for (const part of parts) if (part.type === \"integer\") integers += part.value.length;\n\n const out: Cell[] = [];\n let seen = 0;\n let fraction = 0;\n let marks = 0;\n for (const part of parts) {\n if (part.type === \"integer\") {\n for (const glyph of part.value) {\n out.push({ key: `d${integers - 1 - seen}`, kind: \"digit\", digit: index.get(glyph) ?? 0 });\n seen += 1;\n }\n } else if (part.type === \"fraction\") {\n for (const glyph of part.value) {\n fraction += 1;\n out.push({ key: `f${fraction}`, kind: \"digit\", digit: index.get(glyph) ?? 0 });\n }\n } else if (part.type === \"group\") {\n out.push({ key: `g${integers - 1 - seen}`, kind: \"mark\", text: part.value });\n } else {\n marks += 1;\n\n out.push({ key: `${part.type}${marks}`, kind: \"mark\", text: part.value });\n }\n }\n return out;\n}\n\nfunction turn(from: number, to: number, dir: 1 | -1): number {\n if (from === to) return 0;\n const forward = ((to - from) * dir % CYCLE + CYCLE) % CYCLE;\n return dir * forward;\n}\n\nfunction ms(value: string, fallback: number): number {\n const n = parseFloat(value);\n if (!Number.isFinite(n)) return fallback;\n return value.trim().endsWith(\"s\") && !value.trim().endsWith(\"ms\") ? n * 1000 : n;\n}\n\nfunction px(value: string, fontSize: number, fallback: number): number {\n const n = parseFloat(value);\n if (!Number.isFinite(n)) return fallback;\n return value.includes(\"em\") ? n * fontSize : n;\n}\n\nconst reduced = () =>\n typeof window !== \"undefined\" && !!window.matchMedia?.(\"(prefers-reduced-motion: reduce)\").matches;\n\nexport const Numbers = React.forwardRef<HTMLSpanElement, NumbersProps>(function Numbers(\n { value, locale, format, transition = \"roll\", trend = \"auto\", blur = true, fade, duration, label, className, ...props },\n ref,\n) {\n const numerals = React.useMemo(() => numeralsOf(locale, format), [locale, format]);\n const cells = React.useMemo(\n () => cellsFor(value, locale, format, numerals.index),\n [value, locale, format, numerals],\n );\n const text = React.useMemo(() => new Intl.NumberFormat(locale, format).format(value), [value, locale, format]);\n\n const host = React.useRef<HTMLSpanElement | null>(null);\n const row = React.useRef<HTMLSpanElement | null>(null);\n\n const shown = React.useRef(new Map<string, Cell>());\n const seats = React.useRef(new Map<string, number>());\n const previous = React.useRef(value);\n\n const widthSeat = React.useRef<number | null>(null);\n const widthRun = React.useRef<Animation | null>(null);\n\n const [leaving, setLeaving] = React.useState<{ cell: Cell; left: number }[]>([]);\n\n const heading = React.useRef<1 | -1>(1);\n\n const spun = React.useRef(new Set<string>());\n\n const [outgoing, setOutgoing] = React.useState<Map<string, number>>(() => new Map());\n const outgoingTimer = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const [generation, setGeneration] = React.useState(0);\n React.useEffect(() => () => {\n if (outgoingTimer.current) clearTimeout(outgoingTimer.current);\n }, []);\n\n React.useLayoutEffect(() => {\n const rowEl = row.current;\n const hostEl = host.current;\n if (!rowEl || !hostEl) return;\n\n const from = previous.current;\n previous.current = value;\n const still = reduced();\n\n const cs = getComputedStyle(rowEl);\n const fontSize = parseFloat(cs.fontSize) || 16;\n\n const roll = duration ?? ms(cs.getPropertyValue(\"--sfi-numbers-roll\"), 520);\n const shift = ms(cs.getPropertyValue(\"--sfi-settle\"), 240);\n const ease = cs.getPropertyValue(\"--sfi-ease\").trim() || \"ease\";\n const deepest = px(cs.getPropertyValue(\"--sfi-numbers-blur\"), fontSize, fontSize * 0.1);\n\n const dir: 1 | -1 = trend === \"up\" ? 1 : trend === \"down\" ? -1 : value >= from ? 1 : -1;\n heading.current = dir;\n\n const nextSeats = new Map<string, number>();\n const gone = new Map(shown.current);\n const swapped = new Map<string, number>();\n\n rowEl.querySelectorAll<HTMLElement>(\"[data-cell]\").forEach((el) => {\n const key = el.dataset.cell as string;\n const seat = el.offsetLeft;\n nextSeats.set(key, seat);\n gone.delete(key);\n\n const was = seats.current.get(key);\n const before = shown.current.get(key);\n\n if (was === undefined) {\n if (!still && seats.current.size > 0) el.setAttribute(\"data-arriving\", \"\");\n return;\n }\n el.removeAttribute(\"data-arriving\");\n if (still) return;\n\n const dx = was - seat;\n if (Math.abs(dx) >= 1) {\n el.animate([{ transform: `translateX(${dx}px)` }, { transform: \"translateX(0)\" }], {\n duration: shift,\n easing: ease,\n });\n }\n\n const digit = el.dataset.digit;\n if (digit === undefined || !before || before.kind !== \"digit\") return;\n if (Number(digit) === before.digit) return;\n\n if (transition !== \"roll\") {\n swapped.set(key, before.digit);\n return;\n }\n\n const steps = turn(before.digit, Number(digit), dir);\n if (steps === 0) return;\n\n const strip = el.querySelector<HTMLElement>(\".sfi-numbers-strip\");\n const cell = strip?.firstElementChild;\n if (!strip || !cell) return;\n const height = cell.getBoundingClientRect().height;\n if (!height) return;\n\n const start = (HOME + before.digit) * height;\n const turning = strip.animate(\n [{ transform: `translateY(${-start}px)` }, { transform: `translateY(${-(start + steps * height)}px)` }],\n { duration: roll, easing: ease },\n );\n\n el.setAttribute(\"data-turning\", \"\");\n turning.finished.then(() => el.removeAttribute(\"data-turning\")).catch(() => {});\n\n if (blur && deepest > 0) {\n const depth = Math.min(1, Math.abs(steps) / 4) * deepest;\n el.animate(\n [\n { filter: \"blur(0px)\", offset: 0 },\n { filter: `blur(${depth.toFixed(2)}px)`, offset: 0.15 },\n { filter: \"blur(0px)\", offset: 0.62 },\n { filter: \"blur(0px)\", offset: 1 },\n ],\n { duration: roll, easing: \"linear\" },\n );\n }\n });\n\n if (gone.size && !still) {\n const ghosts: { cell: Cell; left: number }[] = [];\n gone.forEach((cell, key) => {\n const seat = seats.current.get(key);\n if (seat !== undefined) ghosts.push({ cell, left: seat });\n });\n if (ghosts.length) setLeaving((current) => [...current, ...ghosts]);\n }\n\n shown.current = new Map(cells.map((cell) => [cell.key, cell]));\n seats.current = nextSeats;\n\n if (swapped.size && !still) {\n hostEl.style.setProperty(\"--sfi-numbers-dir\", String(dir));\n setOutgoing(swapped);\n setGeneration((n) => n + 1);\n if (outgoingTimer.current) clearTimeout(outgoingTimer.current);\n outgoingTimer.current = setTimeout(() => {\n outgoingTimer.current = null;\n setOutgoing(new Map());\n }, roll + 40);\n }\n\n const running = widthRun.current;\n const visual = running ? hostEl.getBoundingClientRect().width : null;\n if (running) {\n running.cancel();\n widthRun.current = null;\n }\n const now = rowEl.offsetWidth;\n const start = visual ?? widthSeat.current;\n widthSeat.current = now;\n if (start === null || Math.abs(start - now) < 0.5 || still) return;\n\n const grow = hostEl.animate([{ width: `${start}px` }, { width: `${now}px` }], {\n duration: shift,\n easing: ease,\n });\n widthRun.current = grow;\n grow.finished\n .then(() => {\n if (widthRun.current === grow) widthRun.current = null;\n })\n .catch(() => {});\n }, [cells, value, trend, blur, duration, transition]);\n\n React.useLayoutEffect(() => {\n const rowEl = row.current;\n if (!rowEl || leaving.length === 0 || reduced()) return;\n\n const cs = getComputedStyle(rowEl);\n const fontSize = parseFloat(cs.fontSize) || 16;\n const going = ms(cs.getPropertyValue(\"--sfi-numbers-exit\"), 240);\n const ease = cs.getPropertyValue(\"--sfi-ease\").trim() || \"ease\";\n const deepest = px(cs.getPropertyValue(\"--sfi-numbers-blur\"), fontSize, fontSize * 0.1);\n const dir = heading.current;\n\n rowEl.querySelectorAll<HTMLElement>(\"[data-ghost]\").forEach((el) => {\n const key = el.dataset.ghost as string;\n if (spun.current.has(key)) return;\n spun.current.add(key);\n const strip = el.querySelector<HTMLElement>(\".sfi-numbers-strip\");\n const cell = strip?.firstElementChild;\n if (!strip || !cell) return;\n const height = cell.getBoundingClientRect().height;\n if (!height) return;\n\n const start = (HOME + Number(el.dataset.digit ?? 0)) * height;\n strip.animate(\n [{ transform: `translateY(${-start}px)` }, { transform: `translateY(${-(start + dir * 3 * height)}px)` }],\n { duration: going, easing: ease },\n );\n if (blur && deepest > 0) {\n el.animate([{ filter: \"blur(0px)\" }, { filter: `blur(${deepest.toFixed(2)}px)` }], {\n duration: going,\n easing: ease,\n });\n }\n });\n }, [leaving, blur, transition]);\n\n const setHost = React.useMemo(() => mergeRefs(ref, host), [ref]);\n\n const knobs: Record<string, string> = {};\n if (duration !== undefined) knobs[\"--sfi-numbers-roll\"] = `${duration}ms`;\n if (fade !== undefined) knobs[\"--sfi-numbers-fade\"] = `${fade}`;\n const style = Object.keys(knobs).length\n ? ({ ...knobs, ...props.style } as React.CSSProperties)\n : props.style;\n\n return (\n <span\n ref={setHost}\n className={cx(\"sfi-numbers\", className)}\n data-transition={transition === \"roll\" ? undefined : transition}\n {...props}\n style={style}\n >\n\n <span className=\"sfi-numbers-said\">{label ?? text}</span>\n <span className=\"sfi-numbers-row\" ref={row} aria-hidden=\"true\">\n {cells.map((cell) => (\n <Piece\n key={cell.key}\n cell={cell}\n glyphs={numerals.glyphs}\n transition={transition}\n from={cell.kind === \"digit\" ? outgoing.get(cell.key) : undefined}\n generation={generation}\n />\n ))}\n {leaving.map((ghost) => (\n <Piece\n key={`leaving:${ghost.cell.key}`}\n cell={ghost.cell}\n glyphs={numerals.glyphs}\n transition={transition}\n generation={generation}\n leaving\n left={ghost.left}\n onDone={() => {\n spun.current.delete(ghost.cell.key);\n setLeaving((current) => current.filter((x) => x.cell.key !== ghost.cell.key));\n }}\n />\n ))}\n </span>\n </span>\n );\n});\n\nconst at = (digit: number) => ({ \"--sfi-numbers-at\": HOME + digit }) as React.CSSProperties;\n\nfunction Piece({\n cell,\n glyphs,\n transition,\n from,\n generation,\n leaving,\n left,\n onDone,\n}: {\n cell: Cell;\n glyphs: string[];\n transition: NumbersTransition;\n from?: number;\n generation: number;\n leaving?: boolean;\n left?: number;\n onDone?: () => void;\n}) {\n const identity = leaving\n ? {\n \"data-leaving\": \"\",\n \"data-ghost\": cell.key,\n style: { left } as React.CSSProperties,\n onAnimationEnd: onDone,\n }\n : {\n \"data-cell\": cell.key,\n onAnimationEnd: (event: React.AnimationEvent<HTMLElement>) =>\n event.currentTarget.removeAttribute(\"data-arriving\"),\n };\n\n if (cell.kind === \"mark\") {\n return (\n <span className=\"sfi-numbers-mark\" {...identity}>\n {cell.text}\n </span>\n );\n }\n\n const swapping = from !== undefined && from !== cell.digit;\n\n return (\n <span className=\"sfi-numbers-slot\" data-digit={cell.digit} {...identity}>\n\n <span className=\"sfi-numbers-window\">\n {transition === \"roll\" ? (\n <span className=\"sfi-numbers-strip\" style={at(cell.digit)}>\n {CELLS.map((n, i) => (\n <span className=\"sfi-numbers-cell\" key={i}>\n {glyphs[n]}\n </span>\n ))}\n </span>\n ) : (\n <>\n <span\n className=\"sfi-numbers-face\"\n\n data-in={swapping ? \"\" : undefined}\n key={`in:${cell.digit}:${swapping ? generation : \"rest\"}`}\n >\n {glyphs[cell.digit]}\n </span>\n {swapping ? (\n <span className=\"sfi-numbers-face\" data-out=\"\" key={`out:${from}:${generation}`}>\n {glyphs[from as number]}\n </span>\n ) : null}\n </>\n )}\n </span>\n </span>\n );\n}\n","export type ClassValue = string | number | null | undefined | false | ClassValue[];\n\nexport function cx(...values: ClassValue[]): string {\n let out = \"\";\n for (const value of values) {\n if (!value && value !== 0) continue;\n const part = Array.isArray(value) ? cx(...value) : String(value);\n if (part) out += out ? \" \" + part : part;\n }\n return out;\n}\n","import type { Ref } from \"react\";\n\nexport function assignRef<T>(ref: Ref<T> | undefined, value: T | null): void {\n if (!ref) return;\n if (typeof ref === \"function\") ref(value);\n else (ref as { current: T | null }).current = value;\n}\n\nexport function mergeRefs<T>(...refs: Array<Ref<T> | undefined>): (value: T | null) => void {\n return (value) => {\n for (const ref of refs) assignRef(ref, value);\n };\n}\n"],"mappings":"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","names":["Numbers","start"]}