@sfinterface/numbers 0.1.0 → 0.2.1

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/README.md CHANGED
@@ -35,8 +35,8 @@ Each column is a strip of digits behind a window. Changing the value turns only
35
35
  the columns whose digit actually moved, and the box grows or shrinks to fit what
36
36
  it now says.
37
37
 
38
- Face, size, weight, colour and tracking are inherited the stylesheet describes
39
- motion and geometry and nothing else. A readout in a heading is the heading's
38
+ Face, size, weight, colour and tracking are inherited. The stylesheet describes
39
+ motion and geometry and nothing else, so a readout in a heading is the heading's
40
40
  type.
41
41
 
42
42
  The digits are `aria-hidden`; one formatted string sits behind them, so a screen
@@ -77,8 +77,8 @@ come from the platform.
77
77
 
78
78
  ## Fade
79
79
 
80
- How far the soft edge of the window reaches. While a column turns it deepens, so
81
- the passing digits go ghostly and settle back.
80
+ `fade` is how far the soft edge of the window reaches. While a column turns it
81
+ deepens, so the passing digits go ghostly and settle back.
82
82
 
83
83
  ```tsx
84
84
  <Numbers value={n} fade={0} />
@@ -86,6 +86,15 @@ the passing digits go ghostly and settle back.
86
86
  <Numbers value={n} fade={2.4} />
87
87
  ```
88
88
 
89
+ `softness` is the shape of that edge rather than its reach. At 0 a digit stays
90
+ crisp almost to the edge and then lets go quickly; at 1 the same reach is spread
91
+ into an even, eased ramp with no knee in it.
92
+
93
+ ```tsx
94
+ <Numbers value={n} softness={1} />
95
+ <Numbers value={n} fade={2} softness={0.6} />
96
+ ```
97
+
89
98
  <br>
90
99
 
91
100
  ## Props
@@ -93,12 +102,13 @@ the passing digits go ghostly and settle back.
93
102
  | prop | type | default |
94
103
  | --- | --- | --- |
95
104
  | `value` | `number` | required |
96
- | `format` | `Intl.NumberFormatOptions` | |
105
+ | `format` | `Intl.NumberFormatOptions` | none |
97
106
  | `locale` | `string \| string[]` | the runtime's |
98
107
  | `transition` | `"roll" \| "tick" \| "blur" \| "flip" \| "scale"` | `"roll"` |
99
108
  | `trend` | `"auto" \| "up" \| "down"` | `"auto"` |
100
109
  | `blur` | `boolean` | `true` |
101
110
  | `fade` | `number` | `1` |
111
+ | `softness` | `number` | `0` |
102
112
  | `duration` | `number` ms | `520` |
103
113
  | `label` | `string` | the formatted value |
104
114
 
@@ -108,7 +118,7 @@ Everything else is spread onto the root `<span>`.
108
118
 
109
119
  ## Theming
110
120
 
111
- Every value is a custom property set one on `:root`, on a wrapper, or inline.
121
+ Every value is a custom property. Set one on `:root`, on a wrapper, or inline.
112
122
 
113
123
  | token | default | |
114
124
  | --- | --- | --- |
@@ -118,6 +128,7 @@ Every value is a custom property — set one on `:root`, on a wrapper, or inline
118
128
  | `--sfi-numbers-blur` | `0.09em` | the deepest a fast column smears |
119
129
  | `--sfi-numbers-fade` | `1` | how far the window's soft edge reaches |
120
130
  | `--sfi-numbers-fade-roll` | `1.9` | how much further it reaches mid-turn |
131
+ | `--sfi-numbers-softness` | `0` | how gradually that edge arrives, 0 crisp to 1 even |
121
132
  | `--sfi-numbers-ease` | `cubic-bezier(0.32, 0.72, 0, 1)` | the curve a column turns on |
122
133
  | `--sfi-numbers-ease-exit` | `cubic-bezier(0.4, 0, 1, 1)` | and the one it leaves on |
123
134
 
@@ -143,7 +154,8 @@ number changes, and nothing animates.
143
154
  ## Requirements
144
155
 
145
156
  React 18 or 19. No dependencies, types bundled. It renders on the server with
146
- the formatted value already in the markup no empty box, nothing to guard.
157
+ the formatted value already in the markup, so there is no empty box and nothing
158
+ to guard.
147
159
 
148
160
  <br>
149
161
 
package/dist/index.cjs CHANGED
@@ -63,6 +63,7 @@ function mergeRefs(...refs) {
63
63
 
64
64
  // src/Numbers.tsx
65
65
  var import_jsx_runtime = require("react/jsx-runtime");
66
+ var useSeated = typeof window === "undefined" ? React.useEffect : React.useLayoutEffect;
66
67
  var CYCLE = 10;
67
68
  var HOME = CYCLE;
68
69
  var CELLS = Array.from({ length: CYCLE * 3 }, (_, i) => i % CYCLE);
@@ -120,7 +121,7 @@ function px(value, fontSize, fallback) {
120
121
  return value.includes("em") ? n * fontSize : n;
121
122
  }
122
123
  var reduced = () => typeof window !== "undefined" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
123
- var Numbers = React.forwardRef(function Numbers2({ value, locale, format, transition = "roll", trend = "auto", blur = true, fade, duration, label, className, ...props }, ref) {
124
+ var Numbers = React.forwardRef(function Numbers2({ value, locale, format, transition = "roll", trend = "auto", blur = true, fade, softness, duration, label, className, ...props }, ref) {
124
125
  const numerals = React.useMemo(() => numeralsOf(locale, format), [locale, format]);
125
126
  const cells = React.useMemo(
126
127
  () => cellsFor(value, locale, format, numerals.index),
@@ -143,7 +144,7 @@ var Numbers = React.forwardRef(function Numbers2({ value, locale, format, transi
143
144
  React.useEffect(() => () => {
144
145
  if (outgoingTimer.current) clearTimeout(outgoingTimer.current);
145
146
  }, []);
146
- React.useLayoutEffect(() => {
147
+ useSeated(() => {
147
148
  const rowEl = row.current;
148
149
  const hostEl = host.current;
149
150
  if (!rowEl || !hostEl) return;
@@ -256,7 +257,7 @@ var Numbers = React.forwardRef(function Numbers2({ value, locale, format, transi
256
257
  }).catch(() => {
257
258
  });
258
259
  }, [cells, value, trend, blur, duration, transition]);
259
- React.useLayoutEffect(() => {
260
+ useSeated(() => {
260
261
  const rowEl = row.current;
261
262
  if (!rowEl || leaving.length === 0 || reduced()) return;
262
263
  const cs = getComputedStyle(rowEl);
@@ -291,6 +292,7 @@ var Numbers = React.forwardRef(function Numbers2({ value, locale, format, transi
291
292
  const knobs = {};
292
293
  if (duration !== void 0) knobs["--sfi-numbers-roll"] = `${duration}ms`;
293
294
  if (fade !== void 0) knobs["--sfi-numbers-fade"] = `${fade}`;
295
+ if (softness !== void 0) knobs["--sfi-numbers-softness"] = `${softness}`;
294
296
  const style = Object.keys(knobs).length ? { ...knobs, ...props.style } : props.style;
295
297
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
296
298
  "span",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/Numbers.tsx","../src/utils/cx.ts","../src/utils/refs.ts"],"sourcesContent":["export { Numbers, type NumbersProps, type NumbersTransition } from \"./Numbers\";\n","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"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/Numbers.tsx","../src/utils/cx.ts","../src/utils/refs.ts"],"sourcesContent":["export { Numbers, type NumbersProps, type NumbersTransition } from \"./Numbers\";\n","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 softness?: number;\n duration?: number;\n label?: string;\n}\n\nconst useSeated = typeof window === \"undefined\" ? React.useEffect : React.useLayoutEffect;\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, softness, 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 useSeated(() => {\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 useSeated(() => {\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 if (softness !== undefined) knobs[\"--sfi-numbers-softness\"] = `${softness}`;\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"]}
package/dist/index.d.cts CHANGED
@@ -9,6 +9,7 @@ interface NumbersProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "chil
9
9
  transition?: NumbersTransition;
10
10
  blur?: boolean;
11
11
  fade?: number;
12
+ softness?: number;
12
13
  duration?: number;
13
14
  label?: string;
14
15
  }
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ interface NumbersProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "chil
9
9
  transition?: NumbersTransition;
10
10
  blur?: boolean;
11
11
  fade?: number;
12
+ softness?: number;
12
13
  duration?: number;
13
14
  label?: string;
14
15
  }
package/dist/index.js CHANGED
@@ -28,6 +28,7 @@ function mergeRefs(...refs) {
28
28
 
29
29
  // src/Numbers.tsx
30
30
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
31
+ var useSeated = typeof window === "undefined" ? React.useEffect : React.useLayoutEffect;
31
32
  var CYCLE = 10;
32
33
  var HOME = CYCLE;
33
34
  var CELLS = Array.from({ length: CYCLE * 3 }, (_, i) => i % CYCLE);
@@ -85,7 +86,7 @@ function px(value, fontSize, fallback) {
85
86
  return value.includes("em") ? n * fontSize : n;
86
87
  }
87
88
  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
+ var Numbers = React.forwardRef(function Numbers2({ value, locale, format, transition = "roll", trend = "auto", blur = true, fade, softness, duration, label, className, ...props }, ref) {
89
90
  const numerals = React.useMemo(() => numeralsOf(locale, format), [locale, format]);
90
91
  const cells = React.useMemo(
91
92
  () => cellsFor(value, locale, format, numerals.index),
@@ -108,7 +109,7 @@ var Numbers = React.forwardRef(function Numbers2({ value, locale, format, transi
108
109
  React.useEffect(() => () => {
109
110
  if (outgoingTimer.current) clearTimeout(outgoingTimer.current);
110
111
  }, []);
111
- React.useLayoutEffect(() => {
112
+ useSeated(() => {
112
113
  const rowEl = row.current;
113
114
  const hostEl = host.current;
114
115
  if (!rowEl || !hostEl) return;
@@ -221,7 +222,7 @@ var Numbers = React.forwardRef(function Numbers2({ value, locale, format, transi
221
222
  }).catch(() => {
222
223
  });
223
224
  }, [cells, value, trend, blur, duration, transition]);
224
- React.useLayoutEffect(() => {
225
+ useSeated(() => {
225
226
  const rowEl = row.current;
226
227
  if (!rowEl || leaving.length === 0 || reduced()) return;
227
228
  const cs = getComputedStyle(rowEl);
@@ -256,6 +257,7 @@ var Numbers = React.forwardRef(function Numbers2({ value, locale, format, transi
256
257
  const knobs = {};
257
258
  if (duration !== void 0) knobs["--sfi-numbers-roll"] = `${duration}ms`;
258
259
  if (fade !== void 0) knobs["--sfi-numbers-fade"] = `${fade}`;
260
+ if (softness !== void 0) knobs["--sfi-numbers-softness"] = `${softness}`;
259
261
  const style = Object.keys(knobs).length ? { ...knobs, ...props.style } : props.style;
260
262
  return /* @__PURE__ */ jsxs(
261
263
  "span",
package/dist/index.js.map CHANGED
@@ -1 +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"]}
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 softness?: number;\n duration?: number;\n label?: string;\n}\n\nconst useSeated = typeof window === \"undefined\" ? React.useEffect : React.useLayoutEffect;\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, softness, 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 useSeated(() => {\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 useSeated(() => {\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 if (softness !== undefined) knobs[\"--sfi-numbers-softness\"] = `${softness}`;\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"]}
package/dist/numbers.css CHANGED
@@ -57,6 +57,7 @@
57
57
 
58
58
  --sfi-numbers-fade: 1;
59
59
  --sfi-numbers-fade-roll: 1.9;
60
+ --sfi-numbers-softness: 0;
60
61
  --sfi-numbers-veil: calc(var(--sfi-numbers-bleed) * var(--sfi-numbers-fade));
61
62
  --sfi-numbers-veil-deep: calc(var(--sfi-numbers-bleed) * var(--sfi-numbers-fade) * var(--sfi-numbers-fade-roll));
62
63
 
@@ -106,14 +107,20 @@
106
107
  mask-image: linear-gradient(
107
108
  to bottom,
108
109
  rgb(0 0 0 / 0) 0,
109
- rgb(0 0 0 / 0.06) calc(var(--sfi-numbers-veil) * 0.35),
110
- rgb(0 0 0 / 0.4) calc(var(--sfi-numbers-veil) * 0.68),
111
- rgb(0 0 0 / 0.86) calc(var(--sfi-numbers-veil) * 0.9),
110
+ rgb(0 0 0 / clamp(0, calc(0.0343 + var(--sfi-numbers-softness) * 0.0697), 1)) calc(var(--sfi-numbers-veil) * 0.2),
111
+ rgb(0 0 0 / clamp(0, calc(0.06 + var(--sfi-numbers-softness) * 0.22175), 1)) calc(var(--sfi-numbers-veil) * 0.35),
112
+ rgb(0 0 0 / clamp(0, calc(0.2145 + var(--sfi-numbers-softness) * 0.2855), 1)) calc(var(--sfi-numbers-veil) * 0.5),
113
+ rgb(0 0 0 / clamp(0, calc(0.4 + var(--sfi-numbers-softness) * 0.358), 1)) calc(var(--sfi-numbers-veil) * 0.68),
114
+ rgb(0 0 0 / clamp(0, calc(0.651 + var(--sfi-numbers-softness) * 0.245), 1)) calc(var(--sfi-numbers-veil) * 0.8),
115
+ rgb(0 0 0 / clamp(0, calc(0.86 + var(--sfi-numbers-softness) * 0.112), 1)) calc(var(--sfi-numbers-veil) * 0.9),
112
116
  rgb(0 0 0 / 1) var(--sfi-numbers-veil),
113
117
  rgb(0 0 0 / 1) calc(100% - var(--sfi-numbers-veil)),
114
- rgb(0 0 0 / 0.86) calc(100% - var(--sfi-numbers-veil) * 0.9),
115
- rgb(0 0 0 / 0.4) calc(100% - var(--sfi-numbers-veil) * 0.68),
116
- rgb(0 0 0 / 0.06) calc(100% - var(--sfi-numbers-veil) * 0.35),
118
+ rgb(0 0 0 / clamp(0, calc(0.86 + var(--sfi-numbers-softness) * 0.112), 1)) calc(100% - var(--sfi-numbers-veil) * 0.9),
119
+ rgb(0 0 0 / clamp(0, calc(0.651 + var(--sfi-numbers-softness) * 0.245), 1)) calc(100% - var(--sfi-numbers-veil) * 0.8),
120
+ rgb(0 0 0 / clamp(0, calc(0.4 + var(--sfi-numbers-softness) * 0.358), 1)) calc(100% - var(--sfi-numbers-veil) * 0.68),
121
+ rgb(0 0 0 / clamp(0, calc(0.2145 + var(--sfi-numbers-softness) * 0.2855), 1)) calc(100% - var(--sfi-numbers-veil) * 0.5),
122
+ rgb(0 0 0 / clamp(0, calc(0.06 + var(--sfi-numbers-softness) * 0.22175), 1)) calc(100% - var(--sfi-numbers-veil) * 0.35),
123
+ rgb(0 0 0 / clamp(0, calc(0.0343 + var(--sfi-numbers-softness) * 0.0697), 1)) calc(100% - var(--sfi-numbers-veil) * 0.2),
117
124
  rgb(0 0 0 / 0) 100%
118
125
  );
119
126
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sfinterface/numbers",
3
3
  "author": "cz",
4
- "version": "0.1.0",
4
+ "version": "0.2.1",
5
5
  "description": "Numbers that move the way they should. A React component from The San Francisco Interface.",
6
6
  "license": "MIT",
7
7
  "type": "module",