@w3ux/react-odometer 2.3.14 → 2.3.16
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/index.cjs +4 -4
- package/index.cjs.map +1 -1
- package/index.js +4 -4
- package/index.js.map +1 -1
- package/package.json +1 -1
package/index.cjs
CHANGED
|
@@ -125,7 +125,7 @@ var Odometer = ({
|
|
|
125
125
|
const digitIndex = allDigits.indexOf(digits[i]);
|
|
126
126
|
const prevDigitIndex = allDigits.indexOf(prevDigits[i]);
|
|
127
127
|
const difference = Math.abs(digitIndex - prevDigitIndex);
|
|
128
|
-
const delay = `${0.
|
|
128
|
+
const delay = `${0.05 * (digits.length - i - 1)}s`;
|
|
129
129
|
const direction = digitIndex === prevDigitIndex ? "none" : "down";
|
|
130
130
|
const animClass = `slide-${direction}-${difference} `;
|
|
131
131
|
digitsToAnimate.push(prevDigits[i]);
|
|
@@ -154,7 +154,7 @@ var Odometer = ({
|
|
|
154
154
|
animationName: direction === "none" ? void 0 : animClass,
|
|
155
155
|
animationDuration: direction === "none" ? void 0 : DURATION_SECS,
|
|
156
156
|
animationFillMode: "forwards",
|
|
157
|
-
animationTimingFunction: "cubic-bezier(0.
|
|
157
|
+
animationTimingFunction: "cubic-bezier(0.34, 1.56, 0.64, 1)",
|
|
158
158
|
animationDelay: delay,
|
|
159
159
|
color: foundDecimal ? decimalColor : wholeColor,
|
|
160
160
|
userSelect: "none"
|
|
@@ -175,7 +175,7 @@ var Odometer = ({
|
|
|
175
175
|
}
|
|
176
176
|
);
|
|
177
177
|
}
|
|
178
|
-
const offsetWidth = allDigitRefs[`d_${d}`]?.current?.
|
|
178
|
+
const offsetWidth = allDigitRefs[`d_${d}`]?.current?.getBoundingClientRect().width;
|
|
179
179
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
180
180
|
"span",
|
|
181
181
|
{
|
|
@@ -185,7 +185,7 @@ var Odometer = ({
|
|
|
185
185
|
color: foundDecimal ? decimalColor : wholeColor,
|
|
186
186
|
height: lineHeight,
|
|
187
187
|
lineHeight,
|
|
188
|
-
paddingRight: status === "transition" ? `${offsetWidth}px` :
|
|
188
|
+
paddingRight: status === "transition" ? `${offsetWidth}px` : void 0
|
|
189
189
|
},
|
|
190
190
|
children: [
|
|
191
191
|
status === "inactive" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
package/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.tsx"],"sourcesContent":["/* @license Copyright 2024 w3ux authors & contributors\nSPDX-License-Identifier: GPL-3.0-only */\n\nimport type { RefObject } from 'react'\nimport { createRef, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport './index.css'\nimport type { Digit, DigitRef, Direction, Props, Status } from './types'\n\nexport const Odometer = ({\n\tvalue,\n\tspaceBefore = 0,\n\tspaceAfter = '0.25rem',\n\twholeColor = 'var(--text-color-primary)',\n\tdecimalColor = 'var(--text-color-secondary)',\n\tzeroDecimals = 0,\n}: Props) => {\n\t// Store all possible digits.\n\tconst [allDigits] = useState<Digit[]>([\n\t\t'comma',\n\t\t'dot',\n\t\t'0',\n\t\t'1',\n\t\t'2',\n\t\t'3',\n\t\t'4',\n\t\t'5',\n\t\t'6',\n\t\t'7',\n\t\t'8',\n\t\t'9',\n\t])\n\n\t// Store the digits of the current value.\n\tconst [digits, setDigits] = useState<Digit[]>([])\n\n\t// Store digits of the previous value.\n\tconst [prevDigits, setPrevDigits] = useState<Digit[]>([])\n\n\t// Store the status of the odometer (transitioning or stable).\n\tconst [status, setStatus] = useState<Status>('inactive')\n\n\t// Store whether component has iniiialized.\n\tconst [initialized, setInitialized] = useState<boolean>(false)\n\n\t// Store ref of the odometer.\n\tconst [odometerRef] = useState(createRef<HTMLSpanElement>())\n\n\t// Store refs of each digit.\n\tconst [digitRefs, setDigitRefs] = useState<DigitRef[]>([])\n\n\t// Store refs of each `all` digit.\n\tconst [allDigitRefs, setAllDigitRefs] = useState<\n\t\tRecord<string, RefObject<HTMLSpanElement | null>>\n\t>({})\n\n\t// Keep track of active transitions.\n\tconst activeTransitionCounter = useRef<number>(0)\n\n\t// Transition duration.\n\tconst DURATION_MS = 750\n\tconst DURATION_SECS = `${DURATION_MS / 1000}s`\n\n\t// Phase 0: populate `allDigitRefs`.\n\tuseEffect(() => {\n\t\tconst all: Record<\n\t\t\tstring,\n\t\t\tRefObject<HTMLSpanElement | null>\n\t\t> = Object.fromEntries(\n\t\t\tObject.values(allDigits).map((v) => [`d_${v}`, createRef()]),\n\t\t)\n\n\t\tsetAllDigitRefs(all)\n\t}, [])\n\n\t// Phase 1: new digits and refs are added to the odometer.\n\tuseLayoutEffect(() => {\n\t\tif (Object.keys(allDigitRefs)) {\n\t\t\tvalue =\n\t\t\t\tString(value) === '0' ? Number(value).toFixed(zeroDecimals) : value\n\n\t\t\tconst newDigits = value\n\t\t\t\t.toString()\n\t\t\t\t.split('')\n\t\t\t\t.map((v) => (v === '.' ? 'dot' : v))\n\t\t\t\t.map((v) => (v === ',' ? 'comma' : v)) as Digit[]\n\n\t\t\tsetDigits(newDigits)\n\n\t\t\tif (!initialized) {\n\t\t\t\tsetInitialized(true)\n\t\t\t} else {\n\t\t\t\tsetStatus('new')\n\t\t\t\tsetPrevDigits(digits)\n\t\t\t}\n\t\t\tsetDigitRefs(\n\t\t\t\tArray.from({ length: newDigits.length }, () => createRef() as DigitRef),\n\t\t\t)\n\t\t}\n\t}, [value])\n\n\t// Phase 2: set up digit transition.\n\tuseLayoutEffect(() => {\n\t\tif (status === 'new' && !digitRefs.find((d) => d.current === null)) {\n\t\t\tsetStatus('transition')\n\t\t\tactiveTransitionCounter.current++\n\n\t\t\tsetTimeout(() => {\n\t\t\t\tactiveTransitionCounter.current--\n\t\t\t\tif (activeTransitionCounter.current === 0) {\n\t\t\t\t\tsetStatus('inactive')\n\t\t\t\t}\n\t\t\t}, DURATION_MS)\n\t\t}\n\t}, [status, digitRefs])\n\n\tconst odometerCurrent: HTMLSpanElement | null = odometerRef.current\n\tlet lineHeight = odometerCurrent\n\t\t? window.getComputedStyle(odometerCurrent).lineHeight\n\t\t: 'inherit'\n\n\t// Fallback line height to `1.1rem` if `normal`.\n\tlineHeight = lineHeight === 'normal' ? '1.1rem' : lineHeight\n\n\t// Track whether decimal point has been found.\n\tlet foundDecimal = false\n\n\treturn (\n\t\t<>\n\t\t\t{allDigits.map((d, i) => (\n\t\t\t\t<span\n\t\t\t\t\tkey={`odometer_template_digit_${i}`}\n\t\t\t\t\tref={allDigitRefs[`d_${d}`]}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\tposition: 'fixed',\n\t\t\t\t\t\tdisplay: 'inline-block',\n\t\t\t\t\t\ttop: '-999%',\n\t\t\t\t\t\tleft: '-999%',\n\t\t\t\t\t\tuserSelect: 'none',\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{d === 'dot' ? '.' : d === 'comma' ? ',' : d}\n\t\t\t\t</span>\n\t\t\t))}\n\t\t\t<span className=\"odometer\">\n\t\t\t\t<span className=\"odometer-inner\" ref={odometerRef}>\n\t\t\t\t\t{spaceBefore ? <span style={{ paddingLeft: spaceBefore }} /> : null}\n\t\t\t\t\t{digits.map((d, i) => {\n\t\t\t\t\t\tif (d === 'dot') {\n\t\t\t\t\t\t\tfoundDecimal = true\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If transitioning, get digits needed to animate.\n\t\t\t\t\t\tlet childDigits = null\n\t\t\t\t\t\tif (status === 'transition') {\n\t\t\t\t\t\t\tconst digitsToAnimate = []\n\t\t\t\t\t\t\tconst digitIndex = allDigits.indexOf(digits[i])\n\t\t\t\t\t\t\tconst prevDigitIndex = allDigits.indexOf(prevDigits[i])\n\t\t\t\t\t\t\tconst difference = Math.abs(digitIndex - prevDigitIndex)\n\t\t\t\t\t\t\tconst delay = `${0.01 * (digits.length - i - 1)}s`\n\t\t\t\t\t\t\tconst direction: Direction =\n\t\t\t\t\t\t\t\tdigitIndex === prevDigitIndex ? 'none' : 'down'\n\t\t\t\t\t\t\tconst animClass = `slide-${direction}-${difference} `\n\n\t\t\t\t\t\t\t// Push current prev digit to stop of stack.\n\t\t\t\t\t\t\tdigitsToAnimate.push(prevDigits[i])\n\n\t\t\t\t\t\t\t// If transitioning between two digits, animate all digits in between.\n\t\t\t\t\t\t\tif (digitIndex < prevDigitIndex) {\n\t\t\t\t\t\t\t\tdigitsToAnimate.push(\n\t\t\t\t\t\t\t\t\t...Array.from(\n\t\t\t\t\t\t\t\t\t\t{ length: difference },\n\t\t\t\t\t\t\t\t\t\t(_, k) => allDigits[prevDigitIndex - k - 1],\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tdigitsToAnimate.push(\n\t\t\t\t\t\t\t\t\t...Array.from(\n\t\t\t\t\t\t\t\t\t\t{ length: difference },\n\t\t\t\t\t\t\t\t\t\t(_, k) => allDigits[k + prevDigitIndex + 1],\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tchildDigits = (\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\tposition: 'absolute',\n\t\t\t\t\t\t\t\t\t\ttop: 0,\n\t\t\t\t\t\t\t\t\t\tleft: 0,\n\t\t\t\t\t\t\t\t\t\tanimationName: direction === 'none' ? undefined : animClass,\n\t\t\t\t\t\t\t\t\t\tanimationDuration:\n\t\t\t\t\t\t\t\t\t\t\tdirection === 'none' ? undefined : DURATION_SECS,\n\t\t\t\t\t\t\t\t\t\tanimationFillMode: 'forwards',\n\t\t\t\t\t\t\t\t\t\tanimationTimingFunction: 'cubic-bezier(0.1, 1, 0.2, 1)',\n\t\t\t\t\t\t\t\t\t\tanimationDelay: delay,\n\t\t\t\t\t\t\t\t\t\tcolor: foundDecimal ? decimalColor : wholeColor,\n\t\t\t\t\t\t\t\t\t\tuserSelect: 'none',\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{digitsToAnimate.map((c, j) => (\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tkey={`child_digit_${j}`}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"odometer-digit odometer-child\"\n\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\ttop: j === 0 ? 0 : `${100 * j}%`,\n\t\t\t\t\t\t\t\t\t\t\t\theight: lineHeight,\n\t\t\t\t\t\t\t\t\t\t\t\tlineHeight,\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{c === 'dot' ? '.' : c === 'comma' ? ',' : c}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst offsetWidth = allDigitRefs[`d_${d}`]?.current?.offsetWidth\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tkey={`digit_${i}`}\n\t\t\t\t\t\t\t\tref={digitRefs[i]}\n\t\t\t\t\t\t\t\tclassName=\"odometer-digit\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tcolor: foundDecimal ? decimalColor : wholeColor,\n\t\t\t\t\t\t\t\t\theight: lineHeight,\n\t\t\t\t\t\t\t\t\tlineHeight,\n\t\t\t\t\t\t\t\t\tpaddingRight:\n\t\t\t\t\t\t\t\t\t\tstatus === 'transition' ? `${offsetWidth}px` : '0',\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{status === 'inactive' && (\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\tclassName=\"odometer-digit odometer-child\"\n\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\ttop: 0,\n\t\t\t\t\t\t\t\t\t\t\theight: lineHeight,\n\t\t\t\t\t\t\t\t\t\t\tlineHeight,\n\t\t\t\t\t\t\t\t\t\t\twidth: offsetWidth ? `${offsetWidth}px` : undefined,\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{d === 'dot' ? '.' : d === 'comma' ? ',' : d}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t{status === 'transition' && childDigits}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t)\n\t\t\t\t\t})}\n\t\t\t\t\t{spaceAfter ? <span style={{ paddingRight: spaceAfter }} /> : null}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t</>\n\t)\n}\n\nexport default Odometer\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,mBAAwE;AA2HtE;AAvHK,IAAM,WAAW,CAAC;AAAA,EACxB;AAAA,EACA,cAAc;AAAA,EACd,aAAa;AAAA,EACb,aAAa;AAAA,EACb,eAAe;AAAA,EACf,eAAe;AAChB,MAAa;AAEZ,QAAM,CAAC,SAAS,QAAI,uBAAkB;AAAA,IACrC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD,CAAC;AAGD,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAkB,CAAC,CAAC;AAGhD,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAkB,CAAC,CAAC;AAGxD,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAiB,UAAU;AAGvD,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAkB,KAAK;AAG7D,QAAM,CAAC,WAAW,QAAI,2BAAS,wBAA2B,CAAC;AAG3D,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAqB,CAAC,CAAC;AAGzD,QAAM,CAAC,cAAc,eAAe,QAAI,uBAEtC,CAAC,CAAC;AAGJ,QAAM,8BAA0B,qBAAe,CAAC;AAGhD,QAAM,cAAc;AACpB,QAAM,gBAAgB,GAAG,cAAc,GAAI;AAG3C,8BAAU,MAAM;AACf,UAAM,MAGF,OAAO;AAAA,MACV,OAAO,OAAO,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAI,wBAAU,CAAC,CAAC;AAAA,IAC5D;AAEA,oBAAgB,GAAG;AAAA,EACpB,GAAG,CAAC,CAAC;AAGL,oCAAgB,MAAM;AACrB,QAAI,OAAO,KAAK,YAAY,GAAG;AAC9B,cACC,OAAO,KAAK,MAAM,MAAM,OAAO,KAAK,EAAE,QAAQ,YAAY,IAAI;AAE/D,YAAM,YAAY,MAChB,SAAS,EACT,MAAM,EAAE,EACR,IAAI,CAAC,MAAO,MAAM,MAAM,QAAQ,CAAE,EAClC,IAAI,CAAC,MAAO,MAAM,MAAM,UAAU,CAAE;AAEtC,gBAAU,SAAS;AAEnB,UAAI,CAAC,aAAa;AACjB,uBAAe,IAAI;AAAA,MACpB,OAAO;AACN,kBAAU,KAAK;AACf,sBAAc,MAAM;AAAA,MACrB;AACA;AAAA,QACC,MAAM,KAAK,EAAE,QAAQ,UAAU,OAAO,GAAG,UAAM,wBAAU,CAAa;AAAA,MACvE;AAAA,IACD;AAAA,EACD,GAAG,CAAC,KAAK,CAAC;AAGV,oCAAgB,MAAM;AACrB,QAAI,WAAW,SAAS,CAAC,UAAU,KAAK,CAAC,MAAM,EAAE,YAAY,IAAI,GAAG;AACnE,gBAAU,YAAY;AACtB,8BAAwB;AAExB,iBAAW,MAAM;AAChB,gCAAwB;AACxB,YAAI,wBAAwB,YAAY,GAAG;AAC1C,oBAAU,UAAU;AAAA,QACrB;AAAA,MACD,GAAG,WAAW;AAAA,IACf;AAAA,EACD,GAAG,CAAC,QAAQ,SAAS,CAAC;AAEtB,QAAM,kBAA0C,YAAY;AAC5D,MAAI,aAAa,kBACd,OAAO,iBAAiB,eAAe,EAAE,aACzC;AAGH,eAAa,eAAe,WAAW,WAAW;AAGlD,MAAI,eAAe;AAEnB,SACC,4EACE;AAAA,cAAU,IAAI,CAAC,GAAG,MAClB;AAAA,MAAC;AAAA;AAAA,QAEA,KAAK,aAAa,KAAK,CAAC,EAAE;AAAA,QAC1B,OAAO;AAAA,UACN,SAAS;AAAA,UACT,UAAU;AAAA,UACV,SAAS;AAAA,UACT,KAAK;AAAA,UACL,MAAM;AAAA,UACN,YAAY;AAAA,QACb;AAAA,QAEC,gBAAM,QAAQ,MAAM,MAAM,UAAU,MAAM;AAAA;AAAA,MAXtC,2BAA2B,CAAC;AAAA,IAYlC,CACA;AAAA,IACD,4CAAC,UAAK,WAAU,YACf,uDAAC,UAAK,WAAU,kBAAiB,KAAK,aACpC;AAAA,oBAAc,4CAAC,UAAK,OAAO,EAAE,aAAa,YAAY,GAAG,IAAK;AAAA,MAC9D,OAAO,IAAI,CAAC,GAAG,MAAM;AACrB,YAAI,MAAM,OAAO;AAChB,yBAAe;AAAA,QAChB;AAGA,YAAI,cAAc;AAClB,YAAI,WAAW,cAAc;AAC5B,gBAAM,kBAAkB,CAAC;AACzB,gBAAM,aAAa,UAAU,QAAQ,OAAO,CAAC,CAAC;AAC9C,gBAAM,iBAAiB,UAAU,QAAQ,WAAW,CAAC,CAAC;AACtD,gBAAM,aAAa,KAAK,IAAI,aAAa,cAAc;AACvD,gBAAM,QAAQ,GAAG,QAAQ,OAAO,SAAS,IAAI,EAAE;AAC/C,gBAAM,YACL,eAAe,iBAAiB,SAAS;AAC1C,gBAAM,YAAY,SAAS,SAAS,IAAI,UAAU;AAGlD,0BAAgB,KAAK,WAAW,CAAC,CAAC;AAGlC,cAAI,aAAa,gBAAgB;AAChC,4BAAgB;AAAA,cACf,GAAG,MAAM;AAAA,gBACR,EAAE,QAAQ,WAAW;AAAA,gBACrB,CAAC,GAAG,MAAM,UAAU,iBAAiB,IAAI,CAAC;AAAA,cAC3C;AAAA,YACD;AAAA,UACD,OAAO;AACN,4BAAgB;AAAA,cACf,GAAG,MAAM;AAAA,gBACR,EAAE,QAAQ,WAAW;AAAA,gBACrB,CAAC,GAAG,MAAM,UAAU,IAAI,iBAAiB,CAAC;AAAA,cAC3C;AAAA,YACD;AAAA,UACD;AAEA,wBACC;AAAA,YAAC;AAAA;AAAA,cACA,OAAO;AAAA,gBACN,UAAU;AAAA,gBACV,KAAK;AAAA,gBACL,MAAM;AAAA,gBACN,eAAe,cAAc,SAAS,SAAY;AAAA,gBAClD,mBACC,cAAc,SAAS,SAAY;AAAA,gBACpC,mBAAmB;AAAA,gBACnB,yBAAyB;AAAA,gBACzB,gBAAgB;AAAA,gBAChB,OAAO,eAAe,eAAe;AAAA,gBACrC,YAAY;AAAA,cACb;AAAA,cAEC,0BAAgB,IAAI,CAAC,GAAG,MACxB;AAAA,gBAAC;AAAA;AAAA,kBAEA,WAAU;AAAA,kBACV,OAAO;AAAA,oBACN,KAAK,MAAM,IAAI,IAAI,GAAG,MAAM,CAAC;AAAA,oBAC7B,QAAQ;AAAA,oBACR;AAAA,kBACD;AAAA,kBAEC,gBAAM,QAAQ,MAAM,MAAM,UAAU,MAAM;AAAA;AAAA,gBARtC,eAAe,CAAC;AAAA,cAStB,CACA;AAAA;AAAA,UACF;AAAA,QAEF;AAEA,cAAM,cAAc,aAAa,KAAK,CAAC,EAAE,GAAG,SAAS;AAErD,eACC;AAAA,UAAC;AAAA;AAAA,YAEA,KAAK,UAAU,CAAC;AAAA,YAChB,WAAU;AAAA,YACV,OAAO;AAAA,cACN,OAAO,eAAe,eAAe;AAAA,cACrC,QAAQ;AAAA,cACR;AAAA,cACA,cACC,WAAW,eAAe,GAAG,WAAW,OAAO;AAAA,YACjD;AAAA,YAEC;AAAA,yBAAW,cACX;AAAA,gBAAC;AAAA;AAAA,kBACA,WAAU;AAAA,kBACV,OAAO;AAAA,oBACN,KAAK;AAAA,oBACL,QAAQ;AAAA,oBACR;AAAA,oBACA,OAAO,cAAc,GAAG,WAAW,OAAO;AAAA,kBAC3C;AAAA,kBAEC,gBAAM,QAAQ,MAAM,MAAM,UAAU,MAAM;AAAA;AAAA,cAC5C;AAAA,cAEA,WAAW,gBAAgB;AAAA;AAAA;AAAA,UAxBvB,SAAS,CAAC;AAAA,QAyBhB;AAAA,MAEF,CAAC;AAAA,MACA,aAAa,4CAAC,UAAK,OAAO,EAAE,cAAc,WAAW,GAAG,IAAK;AAAA,OAC/D,GACD;AAAA,KACD;AAEF;AAEA,IAAO,gBAAQ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"sourcesContent":["/* @license Copyright 2024 w3ux authors & contributors\nSPDX-License-Identifier: GPL-3.0-only */\n\nimport type { RefObject } from 'react'\nimport { createRef, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport './index.css'\nimport type { Digit, DigitRef, Direction, Props, Status } from './types'\n\nexport const Odometer = ({\n\tvalue,\n\tspaceBefore = 0,\n\tspaceAfter = '0.25rem',\n\twholeColor = 'var(--text-color-primary)',\n\tdecimalColor = 'var(--text-color-secondary)',\n\tzeroDecimals = 0,\n}: Props) => {\n\t// Store all possible digits.\n\tconst [allDigits] = useState<Digit[]>([\n\t\t'comma',\n\t\t'dot',\n\t\t'0',\n\t\t'1',\n\t\t'2',\n\t\t'3',\n\t\t'4',\n\t\t'5',\n\t\t'6',\n\t\t'7',\n\t\t'8',\n\t\t'9',\n\t])\n\n\t// Store the digits of the current value.\n\tconst [digits, setDigits] = useState<Digit[]>([])\n\n\t// Store digits of the previous value.\n\tconst [prevDigits, setPrevDigits] = useState<Digit[]>([])\n\n\t// Store the status of the odometer (transitioning or stable).\n\tconst [status, setStatus] = useState<Status>('inactive')\n\n\t// Store whether component has initialized.\n\tconst [initialized, setInitialized] = useState<boolean>(false)\n\n\t// Store ref of the odometer.\n\tconst [odometerRef] = useState(createRef<HTMLSpanElement>())\n\n\t// Store refs of each digit.\n\tconst [digitRefs, setDigitRefs] = useState<DigitRef[]>([])\n\n\t// Store refs of each `all` digit.\n\tconst [allDigitRefs, setAllDigitRefs] = useState<\n\t\tRecord<string, RefObject<HTMLSpanElement | null>>\n\t>({})\n\n\t// Keep track of active transitions.\n\tconst activeTransitionCounter = useRef<number>(0)\n\n\t// Transition duration.\n\tconst DURATION_MS = 750\n\tconst DURATION_SECS = `${DURATION_MS / 1000}s`\n\n\t// Phase 0: populate `allDigitRefs`.\n\tuseEffect(() => {\n\t\tconst all: Record<\n\t\t\tstring,\n\t\t\tRefObject<HTMLSpanElement | null>\n\t\t> = Object.fromEntries(\n\t\t\tObject.values(allDigits).map((v) => [`d_${v}`, createRef()]),\n\t\t)\n\n\t\tsetAllDigitRefs(all)\n\t}, [])\n\n\t// Phase 1: new digits and refs are added to the odometer.\n\tuseLayoutEffect(() => {\n\t\tif (Object.keys(allDigitRefs)) {\n\t\t\tvalue =\n\t\t\t\tString(value) === '0' ? Number(value).toFixed(zeroDecimals) : value\n\n\t\t\tconst newDigits = value\n\t\t\t\t.toString()\n\t\t\t\t.split('')\n\t\t\t\t.map((v) => (v === '.' ? 'dot' : v))\n\t\t\t\t.map((v) => (v === ',' ? 'comma' : v)) as Digit[]\n\n\t\t\tsetDigits(newDigits)\n\n\t\t\tif (!initialized) {\n\t\t\t\tsetInitialized(true)\n\t\t\t} else {\n\t\t\t\tsetStatus('new')\n\t\t\t\tsetPrevDigits(digits)\n\t\t\t}\n\t\t\tsetDigitRefs(\n\t\t\t\tArray.from({ length: newDigits.length }, () => createRef() as DigitRef),\n\t\t\t)\n\t\t}\n\t}, [value])\n\n\t// Phase 2: set up digit transition.\n\tuseLayoutEffect(() => {\n\t\tif (status === 'new' && !digitRefs.find((d) => d.current === null)) {\n\t\t\tsetStatus('transition')\n\t\t\tactiveTransitionCounter.current++\n\n\t\t\tsetTimeout(() => {\n\t\t\t\tactiveTransitionCounter.current--\n\t\t\t\tif (activeTransitionCounter.current === 0) {\n\t\t\t\t\tsetStatus('inactive')\n\t\t\t\t}\n\t\t\t}, DURATION_MS)\n\t\t}\n\t}, [status, digitRefs])\n\n\tconst odometerCurrent: HTMLSpanElement | null = odometerRef.current\n\tlet lineHeight = odometerCurrent\n\t\t? window.getComputedStyle(odometerCurrent).lineHeight\n\t\t: 'inherit'\n\n\t// Fallback line height to `1.1rem` if `normal`.\n\tlineHeight = lineHeight === 'normal' ? '1.1rem' : lineHeight\n\n\t// Track whether decimal point has been found.\n\tlet foundDecimal = false\n\n\treturn (\n\t\t<>\n\t\t\t{allDigits.map((d, i) => (\n\t\t\t\t<span\n\t\t\t\t\tkey={`odometer_template_digit_${i}`}\n\t\t\t\t\tref={allDigitRefs[`d_${d}`]}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\tposition: 'fixed',\n\t\t\t\t\t\tdisplay: 'inline-block',\n\t\t\t\t\t\ttop: '-999%',\n\t\t\t\t\t\tleft: '-999%',\n\t\t\t\t\t\tuserSelect: 'none',\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{d === 'dot' ? '.' : d === 'comma' ? ',' : d}\n\t\t\t\t</span>\n\t\t\t))}\n\t\t\t<span className=\"odometer\">\n\t\t\t\t<span className=\"odometer-inner\" ref={odometerRef}>\n\t\t\t\t\t{spaceBefore ? <span style={{ paddingLeft: spaceBefore }} /> : null}\n\t\t\t\t\t{digits.map((d, i) => {\n\t\t\t\t\t\tif (d === 'dot') {\n\t\t\t\t\t\t\tfoundDecimal = true\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If transitioning, get digits needed to animate.\n\t\t\t\t\t\tlet childDigits = null\n\t\t\t\t\t\tif (status === 'transition') {\n\t\t\t\t\t\t\tconst digitsToAnimate = []\n\t\t\t\t\t\t\tconst digitIndex = allDigits.indexOf(digits[i])\n\t\t\t\t\t\t\tconst prevDigitIndex = allDigits.indexOf(prevDigits[i])\n\t\t\t\t\t\t\tconst difference = Math.abs(digitIndex - prevDigitIndex)\n\t\t\t\t\t\t\tconst delay = `${0.05 * (digits.length - i - 1)}s`\n\t\t\t\t\t\t\tconst direction: Direction =\n\t\t\t\t\t\t\t\tdigitIndex === prevDigitIndex ? 'none' : 'down'\n\t\t\t\t\t\t\tconst animClass = `slide-${direction}-${difference} `\n\n\t\t\t\t\t\t\t// Push current prev digit to stop of stack.\n\t\t\t\t\t\t\tdigitsToAnimate.push(prevDigits[i])\n\n\t\t\t\t\t\t\t// If transitioning between two digits, animate all digits in between.\n\t\t\t\t\t\t\tif (digitIndex < prevDigitIndex) {\n\t\t\t\t\t\t\t\tdigitsToAnimate.push(\n\t\t\t\t\t\t\t\t\t...Array.from(\n\t\t\t\t\t\t\t\t\t\t{ length: difference },\n\t\t\t\t\t\t\t\t\t\t(_, k) => allDigits[prevDigitIndex - k - 1],\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tdigitsToAnimate.push(\n\t\t\t\t\t\t\t\t\t...Array.from(\n\t\t\t\t\t\t\t\t\t\t{ length: difference },\n\t\t\t\t\t\t\t\t\t\t(_, k) => allDigits[k + prevDigitIndex + 1],\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tchildDigits = (\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\tposition: 'absolute',\n\t\t\t\t\t\t\t\t\t\ttop: 0,\n\t\t\t\t\t\t\t\t\t\tleft: 0,\n\t\t\t\t\t\t\t\t\t\tanimationName: direction === 'none' ? undefined : animClass,\n\t\t\t\t\t\t\t\t\t\tanimationDuration:\n\t\t\t\t\t\t\t\t\t\t\tdirection === 'none' ? undefined : DURATION_SECS,\n\t\t\t\t\t\t\t\t\t\tanimationFillMode: 'forwards',\n\t\t\t\t\t\t\t\t\t\tanimationTimingFunction:\n\t\t\t\t\t\t\t\t\t\t\t'cubic-bezier(0.34, 1.56, 0.64, 1)',\n\t\t\t\t\t\t\t\t\t\tanimationDelay: delay,\n\t\t\t\t\t\t\t\t\t\tcolor: foundDecimal ? decimalColor : wholeColor,\n\t\t\t\t\t\t\t\t\t\tuserSelect: 'none',\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{digitsToAnimate.map((c, j) => (\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tkey={`child_digit_${j}`}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"odometer-digit odometer-child\"\n\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\ttop: j === 0 ? 0 : `${100 * j}%`,\n\t\t\t\t\t\t\t\t\t\t\t\theight: lineHeight,\n\t\t\t\t\t\t\t\t\t\t\t\tlineHeight,\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{c === 'dot' ? '.' : c === 'comma' ? ',' : c}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst offsetWidth =\n\t\t\t\t\t\t\tallDigitRefs[`d_${d}`]?.current?.getBoundingClientRect().width\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tkey={`digit_${i}`}\n\t\t\t\t\t\t\t\tref={digitRefs[i]}\n\t\t\t\t\t\t\t\tclassName=\"odometer-digit\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tcolor: foundDecimal ? decimalColor : wholeColor,\n\t\t\t\t\t\t\t\t\theight: lineHeight,\n\t\t\t\t\t\t\t\t\tlineHeight,\n\t\t\t\t\t\t\t\t\tpaddingRight:\n\t\t\t\t\t\t\t\t\t\tstatus === 'transition' ? `${offsetWidth}px` : undefined,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{status === 'inactive' && (\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\tclassName=\"odometer-digit odometer-child\"\n\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\ttop: 0,\n\t\t\t\t\t\t\t\t\t\t\theight: lineHeight,\n\t\t\t\t\t\t\t\t\t\t\tlineHeight,\n\t\t\t\t\t\t\t\t\t\t\twidth: offsetWidth ? `${offsetWidth}px` : undefined,\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{d === 'dot' ? '.' : d === 'comma' ? ',' : d}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t{status === 'transition' && childDigits}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t)\n\t\t\t\t\t})}\n\t\t\t\t\t{spaceAfter ? <span style={{ paddingRight: spaceAfter }} /> : null}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t</>\n\t)\n}\n\nexport default Odometer\n"],"mappings":"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","names":[]}
|
package/index.js
CHANGED
|
@@ -100,7 +100,7 @@ var Odometer = ({
|
|
|
100
100
|
const digitIndex = allDigits.indexOf(digits[i]);
|
|
101
101
|
const prevDigitIndex = allDigits.indexOf(prevDigits[i]);
|
|
102
102
|
const difference = Math.abs(digitIndex - prevDigitIndex);
|
|
103
|
-
const delay = `${0.
|
|
103
|
+
const delay = `${0.05 * (digits.length - i - 1)}s`;
|
|
104
104
|
const direction = digitIndex === prevDigitIndex ? "none" : "down";
|
|
105
105
|
const animClass = `slide-${direction}-${difference} `;
|
|
106
106
|
digitsToAnimate.push(prevDigits[i]);
|
|
@@ -129,7 +129,7 @@ var Odometer = ({
|
|
|
129
129
|
animationName: direction === "none" ? void 0 : animClass,
|
|
130
130
|
animationDuration: direction === "none" ? void 0 : DURATION_SECS,
|
|
131
131
|
animationFillMode: "forwards",
|
|
132
|
-
animationTimingFunction: "cubic-bezier(0.
|
|
132
|
+
animationTimingFunction: "cubic-bezier(0.34, 1.56, 0.64, 1)",
|
|
133
133
|
animationDelay: delay,
|
|
134
134
|
color: foundDecimal ? decimalColor : wholeColor,
|
|
135
135
|
userSelect: "none"
|
|
@@ -150,7 +150,7 @@ var Odometer = ({
|
|
|
150
150
|
}
|
|
151
151
|
);
|
|
152
152
|
}
|
|
153
|
-
const offsetWidth = allDigitRefs[`d_${d}`]?.current?.
|
|
153
|
+
const offsetWidth = allDigitRefs[`d_${d}`]?.current?.getBoundingClientRect().width;
|
|
154
154
|
return /* @__PURE__ */ jsxs(
|
|
155
155
|
"span",
|
|
156
156
|
{
|
|
@@ -160,7 +160,7 @@ var Odometer = ({
|
|
|
160
160
|
color: foundDecimal ? decimalColor : wholeColor,
|
|
161
161
|
height: lineHeight,
|
|
162
162
|
lineHeight,
|
|
163
|
-
paddingRight: status === "transition" ? `${offsetWidth}px` :
|
|
163
|
+
paddingRight: status === "transition" ? `${offsetWidth}px` : void 0
|
|
164
164
|
},
|
|
165
165
|
children: [
|
|
166
166
|
status === "inactive" && /* @__PURE__ */ jsx(
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.tsx"],"sourcesContent":["/* @license Copyright 2024 w3ux authors & contributors\nSPDX-License-Identifier: GPL-3.0-only */\n\nimport type { RefObject } from 'react'\nimport { createRef, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport './index.css'\nimport type { Digit, DigitRef, Direction, Props, Status } from './types'\n\nexport const Odometer = ({\n\tvalue,\n\tspaceBefore = 0,\n\tspaceAfter = '0.25rem',\n\twholeColor = 'var(--text-color-primary)',\n\tdecimalColor = 'var(--text-color-secondary)',\n\tzeroDecimals = 0,\n}: Props) => {\n\t// Store all possible digits.\n\tconst [allDigits] = useState<Digit[]>([\n\t\t'comma',\n\t\t'dot',\n\t\t'0',\n\t\t'1',\n\t\t'2',\n\t\t'3',\n\t\t'4',\n\t\t'5',\n\t\t'6',\n\t\t'7',\n\t\t'8',\n\t\t'9',\n\t])\n\n\t// Store the digits of the current value.\n\tconst [digits, setDigits] = useState<Digit[]>([])\n\n\t// Store digits of the previous value.\n\tconst [prevDigits, setPrevDigits] = useState<Digit[]>([])\n\n\t// Store the status of the odometer (transitioning or stable).\n\tconst [status, setStatus] = useState<Status>('inactive')\n\n\t// Store whether component has iniiialized.\n\tconst [initialized, setInitialized] = useState<boolean>(false)\n\n\t// Store ref of the odometer.\n\tconst [odometerRef] = useState(createRef<HTMLSpanElement>())\n\n\t// Store refs of each digit.\n\tconst [digitRefs, setDigitRefs] = useState<DigitRef[]>([])\n\n\t// Store refs of each `all` digit.\n\tconst [allDigitRefs, setAllDigitRefs] = useState<\n\t\tRecord<string, RefObject<HTMLSpanElement | null>>\n\t>({})\n\n\t// Keep track of active transitions.\n\tconst activeTransitionCounter = useRef<number>(0)\n\n\t// Transition duration.\n\tconst DURATION_MS = 750\n\tconst DURATION_SECS = `${DURATION_MS / 1000}s`\n\n\t// Phase 0: populate `allDigitRefs`.\n\tuseEffect(() => {\n\t\tconst all: Record<\n\t\t\tstring,\n\t\t\tRefObject<HTMLSpanElement | null>\n\t\t> = Object.fromEntries(\n\t\t\tObject.values(allDigits).map((v) => [`d_${v}`, createRef()]),\n\t\t)\n\n\t\tsetAllDigitRefs(all)\n\t}, [])\n\n\t// Phase 1: new digits and refs are added to the odometer.\n\tuseLayoutEffect(() => {\n\t\tif (Object.keys(allDigitRefs)) {\n\t\t\tvalue =\n\t\t\t\tString(value) === '0' ? Number(value).toFixed(zeroDecimals) : value\n\n\t\t\tconst newDigits = value\n\t\t\t\t.toString()\n\t\t\t\t.split('')\n\t\t\t\t.map((v) => (v === '.' ? 'dot' : v))\n\t\t\t\t.map((v) => (v === ',' ? 'comma' : v)) as Digit[]\n\n\t\t\tsetDigits(newDigits)\n\n\t\t\tif (!initialized) {\n\t\t\t\tsetInitialized(true)\n\t\t\t} else {\n\t\t\t\tsetStatus('new')\n\t\t\t\tsetPrevDigits(digits)\n\t\t\t}\n\t\t\tsetDigitRefs(\n\t\t\t\tArray.from({ length: newDigits.length }, () => createRef() as DigitRef),\n\t\t\t)\n\t\t}\n\t}, [value])\n\n\t// Phase 2: set up digit transition.\n\tuseLayoutEffect(() => {\n\t\tif (status === 'new' && !digitRefs.find((d) => d.current === null)) {\n\t\t\tsetStatus('transition')\n\t\t\tactiveTransitionCounter.current++\n\n\t\t\tsetTimeout(() => {\n\t\t\t\tactiveTransitionCounter.current--\n\t\t\t\tif (activeTransitionCounter.current === 0) {\n\t\t\t\t\tsetStatus('inactive')\n\t\t\t\t}\n\t\t\t}, DURATION_MS)\n\t\t}\n\t}, [status, digitRefs])\n\n\tconst odometerCurrent: HTMLSpanElement | null = odometerRef.current\n\tlet lineHeight = odometerCurrent\n\t\t? window.getComputedStyle(odometerCurrent).lineHeight\n\t\t: 'inherit'\n\n\t// Fallback line height to `1.1rem` if `normal`.\n\tlineHeight = lineHeight === 'normal' ? '1.1rem' : lineHeight\n\n\t// Track whether decimal point has been found.\n\tlet foundDecimal = false\n\n\treturn (\n\t\t<>\n\t\t\t{allDigits.map((d, i) => (\n\t\t\t\t<span\n\t\t\t\t\tkey={`odometer_template_digit_${i}`}\n\t\t\t\t\tref={allDigitRefs[`d_${d}`]}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\tposition: 'fixed',\n\t\t\t\t\t\tdisplay: 'inline-block',\n\t\t\t\t\t\ttop: '-999%',\n\t\t\t\t\t\tleft: '-999%',\n\t\t\t\t\t\tuserSelect: 'none',\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{d === 'dot' ? '.' : d === 'comma' ? ',' : d}\n\t\t\t\t</span>\n\t\t\t))}\n\t\t\t<span className=\"odometer\">\n\t\t\t\t<span className=\"odometer-inner\" ref={odometerRef}>\n\t\t\t\t\t{spaceBefore ? <span style={{ paddingLeft: spaceBefore }} /> : null}\n\t\t\t\t\t{digits.map((d, i) => {\n\t\t\t\t\t\tif (d === 'dot') {\n\t\t\t\t\t\t\tfoundDecimal = true\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If transitioning, get digits needed to animate.\n\t\t\t\t\t\tlet childDigits = null\n\t\t\t\t\t\tif (status === 'transition') {\n\t\t\t\t\t\t\tconst digitsToAnimate = []\n\t\t\t\t\t\t\tconst digitIndex = allDigits.indexOf(digits[i])\n\t\t\t\t\t\t\tconst prevDigitIndex = allDigits.indexOf(prevDigits[i])\n\t\t\t\t\t\t\tconst difference = Math.abs(digitIndex - prevDigitIndex)\n\t\t\t\t\t\t\tconst delay = `${0.01 * (digits.length - i - 1)}s`\n\t\t\t\t\t\t\tconst direction: Direction =\n\t\t\t\t\t\t\t\tdigitIndex === prevDigitIndex ? 'none' : 'down'\n\t\t\t\t\t\t\tconst animClass = `slide-${direction}-${difference} `\n\n\t\t\t\t\t\t\t// Push current prev digit to stop of stack.\n\t\t\t\t\t\t\tdigitsToAnimate.push(prevDigits[i])\n\n\t\t\t\t\t\t\t// If transitioning between two digits, animate all digits in between.\n\t\t\t\t\t\t\tif (digitIndex < prevDigitIndex) {\n\t\t\t\t\t\t\t\tdigitsToAnimate.push(\n\t\t\t\t\t\t\t\t\t...Array.from(\n\t\t\t\t\t\t\t\t\t\t{ length: difference },\n\t\t\t\t\t\t\t\t\t\t(_, k) => allDigits[prevDigitIndex - k - 1],\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tdigitsToAnimate.push(\n\t\t\t\t\t\t\t\t\t...Array.from(\n\t\t\t\t\t\t\t\t\t\t{ length: difference },\n\t\t\t\t\t\t\t\t\t\t(_, k) => allDigits[k + prevDigitIndex + 1],\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tchildDigits = (\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\tposition: 'absolute',\n\t\t\t\t\t\t\t\t\t\ttop: 0,\n\t\t\t\t\t\t\t\t\t\tleft: 0,\n\t\t\t\t\t\t\t\t\t\tanimationName: direction === 'none' ? undefined : animClass,\n\t\t\t\t\t\t\t\t\t\tanimationDuration:\n\t\t\t\t\t\t\t\t\t\t\tdirection === 'none' ? undefined : DURATION_SECS,\n\t\t\t\t\t\t\t\t\t\tanimationFillMode: 'forwards',\n\t\t\t\t\t\t\t\t\t\tanimationTimingFunction: 'cubic-bezier(0.1, 1, 0.2, 1)',\n\t\t\t\t\t\t\t\t\t\tanimationDelay: delay,\n\t\t\t\t\t\t\t\t\t\tcolor: foundDecimal ? decimalColor : wholeColor,\n\t\t\t\t\t\t\t\t\t\tuserSelect: 'none',\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{digitsToAnimate.map((c, j) => (\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tkey={`child_digit_${j}`}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"odometer-digit odometer-child\"\n\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\ttop: j === 0 ? 0 : `${100 * j}%`,\n\t\t\t\t\t\t\t\t\t\t\t\theight: lineHeight,\n\t\t\t\t\t\t\t\t\t\t\t\tlineHeight,\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{c === 'dot' ? '.' : c === 'comma' ? ',' : c}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst offsetWidth = allDigitRefs[`d_${d}`]?.current?.offsetWidth\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tkey={`digit_${i}`}\n\t\t\t\t\t\t\t\tref={digitRefs[i]}\n\t\t\t\t\t\t\t\tclassName=\"odometer-digit\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tcolor: foundDecimal ? decimalColor : wholeColor,\n\t\t\t\t\t\t\t\t\theight: lineHeight,\n\t\t\t\t\t\t\t\t\tlineHeight,\n\t\t\t\t\t\t\t\t\tpaddingRight:\n\t\t\t\t\t\t\t\t\t\tstatus === 'transition' ? `${offsetWidth}px` : '0',\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{status === 'inactive' && (\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\tclassName=\"odometer-digit odometer-child\"\n\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\ttop: 0,\n\t\t\t\t\t\t\t\t\t\t\theight: lineHeight,\n\t\t\t\t\t\t\t\t\t\t\tlineHeight,\n\t\t\t\t\t\t\t\t\t\t\twidth: offsetWidth ? `${offsetWidth}px` : undefined,\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{d === 'dot' ? '.' : d === 'comma' ? ',' : d}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t{status === 'transition' && childDigits}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t)\n\t\t\t\t\t})}\n\t\t\t\t\t{spaceAfter ? <span style={{ paddingRight: spaceAfter }} /> : null}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t</>\n\t)\n}\n\nexport default Odometer\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"sourcesContent":["/* @license Copyright 2024 w3ux authors & contributors\nSPDX-License-Identifier: GPL-3.0-only */\n\nimport type { RefObject } from 'react'\nimport { createRef, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport './index.css'\nimport type { Digit, DigitRef, Direction, Props, Status } from './types'\n\nexport const Odometer = ({\n\tvalue,\n\tspaceBefore = 0,\n\tspaceAfter = '0.25rem',\n\twholeColor = 'var(--text-color-primary)',\n\tdecimalColor = 'var(--text-color-secondary)',\n\tzeroDecimals = 0,\n}: Props) => {\n\t// Store all possible digits.\n\tconst [allDigits] = useState<Digit[]>([\n\t\t'comma',\n\t\t'dot',\n\t\t'0',\n\t\t'1',\n\t\t'2',\n\t\t'3',\n\t\t'4',\n\t\t'5',\n\t\t'6',\n\t\t'7',\n\t\t'8',\n\t\t'9',\n\t])\n\n\t// Store the digits of the current value.\n\tconst [digits, setDigits] = useState<Digit[]>([])\n\n\t// Store digits of the previous value.\n\tconst [prevDigits, setPrevDigits] = useState<Digit[]>([])\n\n\t// Store the status of the odometer (transitioning or stable).\n\tconst [status, setStatus] = useState<Status>('inactive')\n\n\t// Store whether component has initialized.\n\tconst [initialized, setInitialized] = useState<boolean>(false)\n\n\t// Store ref of the odometer.\n\tconst [odometerRef] = useState(createRef<HTMLSpanElement>())\n\n\t// Store refs of each digit.\n\tconst [digitRefs, setDigitRefs] = useState<DigitRef[]>([])\n\n\t// Store refs of each `all` digit.\n\tconst [allDigitRefs, setAllDigitRefs] = useState<\n\t\tRecord<string, RefObject<HTMLSpanElement | null>>\n\t>({})\n\n\t// Keep track of active transitions.\n\tconst activeTransitionCounter = useRef<number>(0)\n\n\t// Transition duration.\n\tconst DURATION_MS = 750\n\tconst DURATION_SECS = `${DURATION_MS / 1000}s`\n\n\t// Phase 0: populate `allDigitRefs`.\n\tuseEffect(() => {\n\t\tconst all: Record<\n\t\t\tstring,\n\t\t\tRefObject<HTMLSpanElement | null>\n\t\t> = Object.fromEntries(\n\t\t\tObject.values(allDigits).map((v) => [`d_${v}`, createRef()]),\n\t\t)\n\n\t\tsetAllDigitRefs(all)\n\t}, [])\n\n\t// Phase 1: new digits and refs are added to the odometer.\n\tuseLayoutEffect(() => {\n\t\tif (Object.keys(allDigitRefs)) {\n\t\t\tvalue =\n\t\t\t\tString(value) === '0' ? Number(value).toFixed(zeroDecimals) : value\n\n\t\t\tconst newDigits = value\n\t\t\t\t.toString()\n\t\t\t\t.split('')\n\t\t\t\t.map((v) => (v === '.' ? 'dot' : v))\n\t\t\t\t.map((v) => (v === ',' ? 'comma' : v)) as Digit[]\n\n\t\t\tsetDigits(newDigits)\n\n\t\t\tif (!initialized) {\n\t\t\t\tsetInitialized(true)\n\t\t\t} else {\n\t\t\t\tsetStatus('new')\n\t\t\t\tsetPrevDigits(digits)\n\t\t\t}\n\t\t\tsetDigitRefs(\n\t\t\t\tArray.from({ length: newDigits.length }, () => createRef() as DigitRef),\n\t\t\t)\n\t\t}\n\t}, [value])\n\n\t// Phase 2: set up digit transition.\n\tuseLayoutEffect(() => {\n\t\tif (status === 'new' && !digitRefs.find((d) => d.current === null)) {\n\t\t\tsetStatus('transition')\n\t\t\tactiveTransitionCounter.current++\n\n\t\t\tsetTimeout(() => {\n\t\t\t\tactiveTransitionCounter.current--\n\t\t\t\tif (activeTransitionCounter.current === 0) {\n\t\t\t\t\tsetStatus('inactive')\n\t\t\t\t}\n\t\t\t}, DURATION_MS)\n\t\t}\n\t}, [status, digitRefs])\n\n\tconst odometerCurrent: HTMLSpanElement | null = odometerRef.current\n\tlet lineHeight = odometerCurrent\n\t\t? window.getComputedStyle(odometerCurrent).lineHeight\n\t\t: 'inherit'\n\n\t// Fallback line height to `1.1rem` if `normal`.\n\tlineHeight = lineHeight === 'normal' ? '1.1rem' : lineHeight\n\n\t// Track whether decimal point has been found.\n\tlet foundDecimal = false\n\n\treturn (\n\t\t<>\n\t\t\t{allDigits.map((d, i) => (\n\t\t\t\t<span\n\t\t\t\t\tkey={`odometer_template_digit_${i}`}\n\t\t\t\t\tref={allDigitRefs[`d_${d}`]}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\tposition: 'fixed',\n\t\t\t\t\t\tdisplay: 'inline-block',\n\t\t\t\t\t\ttop: '-999%',\n\t\t\t\t\t\tleft: '-999%',\n\t\t\t\t\t\tuserSelect: 'none',\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{d === 'dot' ? '.' : d === 'comma' ? ',' : d}\n\t\t\t\t</span>\n\t\t\t))}\n\t\t\t<span className=\"odometer\">\n\t\t\t\t<span className=\"odometer-inner\" ref={odometerRef}>\n\t\t\t\t\t{spaceBefore ? <span style={{ paddingLeft: spaceBefore }} /> : null}\n\t\t\t\t\t{digits.map((d, i) => {\n\t\t\t\t\t\tif (d === 'dot') {\n\t\t\t\t\t\t\tfoundDecimal = true\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If transitioning, get digits needed to animate.\n\t\t\t\t\t\tlet childDigits = null\n\t\t\t\t\t\tif (status === 'transition') {\n\t\t\t\t\t\t\tconst digitsToAnimate = []\n\t\t\t\t\t\t\tconst digitIndex = allDigits.indexOf(digits[i])\n\t\t\t\t\t\t\tconst prevDigitIndex = allDigits.indexOf(prevDigits[i])\n\t\t\t\t\t\t\tconst difference = Math.abs(digitIndex - prevDigitIndex)\n\t\t\t\t\t\t\tconst delay = `${0.05 * (digits.length - i - 1)}s`\n\t\t\t\t\t\t\tconst direction: Direction =\n\t\t\t\t\t\t\t\tdigitIndex === prevDigitIndex ? 'none' : 'down'\n\t\t\t\t\t\t\tconst animClass = `slide-${direction}-${difference} `\n\n\t\t\t\t\t\t\t// Push current prev digit to stop of stack.\n\t\t\t\t\t\t\tdigitsToAnimate.push(prevDigits[i])\n\n\t\t\t\t\t\t\t// If transitioning between two digits, animate all digits in between.\n\t\t\t\t\t\t\tif (digitIndex < prevDigitIndex) {\n\t\t\t\t\t\t\t\tdigitsToAnimate.push(\n\t\t\t\t\t\t\t\t\t...Array.from(\n\t\t\t\t\t\t\t\t\t\t{ length: difference },\n\t\t\t\t\t\t\t\t\t\t(_, k) => allDigits[prevDigitIndex - k - 1],\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tdigitsToAnimate.push(\n\t\t\t\t\t\t\t\t\t...Array.from(\n\t\t\t\t\t\t\t\t\t\t{ length: difference },\n\t\t\t\t\t\t\t\t\t\t(_, k) => allDigits[k + prevDigitIndex + 1],\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tchildDigits = (\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\tposition: 'absolute',\n\t\t\t\t\t\t\t\t\t\ttop: 0,\n\t\t\t\t\t\t\t\t\t\tleft: 0,\n\t\t\t\t\t\t\t\t\t\tanimationName: direction === 'none' ? undefined : animClass,\n\t\t\t\t\t\t\t\t\t\tanimationDuration:\n\t\t\t\t\t\t\t\t\t\t\tdirection === 'none' ? undefined : DURATION_SECS,\n\t\t\t\t\t\t\t\t\t\tanimationFillMode: 'forwards',\n\t\t\t\t\t\t\t\t\t\tanimationTimingFunction:\n\t\t\t\t\t\t\t\t\t\t\t'cubic-bezier(0.34, 1.56, 0.64, 1)',\n\t\t\t\t\t\t\t\t\t\tanimationDelay: delay,\n\t\t\t\t\t\t\t\t\t\tcolor: foundDecimal ? decimalColor : wholeColor,\n\t\t\t\t\t\t\t\t\t\tuserSelect: 'none',\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{digitsToAnimate.map((c, j) => (\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tkey={`child_digit_${j}`}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"odometer-digit odometer-child\"\n\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\ttop: j === 0 ? 0 : `${100 * j}%`,\n\t\t\t\t\t\t\t\t\t\t\t\theight: lineHeight,\n\t\t\t\t\t\t\t\t\t\t\t\tlineHeight,\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{c === 'dot' ? '.' : c === 'comma' ? ',' : c}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst offsetWidth =\n\t\t\t\t\t\t\tallDigitRefs[`d_${d}`]?.current?.getBoundingClientRect().width\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tkey={`digit_${i}`}\n\t\t\t\t\t\t\t\tref={digitRefs[i]}\n\t\t\t\t\t\t\t\tclassName=\"odometer-digit\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tcolor: foundDecimal ? decimalColor : wholeColor,\n\t\t\t\t\t\t\t\t\theight: lineHeight,\n\t\t\t\t\t\t\t\t\tlineHeight,\n\t\t\t\t\t\t\t\t\tpaddingRight:\n\t\t\t\t\t\t\t\t\t\tstatus === 'transition' ? `${offsetWidth}px` : undefined,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{status === 'inactive' && (\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\tclassName=\"odometer-digit odometer-child\"\n\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\ttop: 0,\n\t\t\t\t\t\t\t\t\t\t\theight: lineHeight,\n\t\t\t\t\t\t\t\t\t\t\tlineHeight,\n\t\t\t\t\t\t\t\t\t\t\twidth: offsetWidth ? `${offsetWidth}px` : undefined,\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{d === 'dot' ? '.' : d === 'comma' ? ',' : d}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t{status === 'transition' && childDigits}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t)\n\t\t\t\t\t})}\n\t\t\t\t\t{spaceAfter ? <span style={{ paddingRight: spaceAfter }} /> : null}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t</>\n\t)\n}\n\nexport default Odometer\n"],"mappings":"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","names":[]}
|