@w3ux/react-odometer 2.3.13 → 2.3.15

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 CHANGED
@@ -175,6 +175,7 @@ var Odometer = ({
175
175
  }
176
176
  );
177
177
  }
178
+ const offsetWidth = allDigitRefs[`d_${d}`]?.current?.getBoundingClientRect().width;
178
179
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
179
180
  "span",
180
181
  {
@@ -184,7 +185,7 @@ var Odometer = ({
184
185
  color: foundDecimal ? decimalColor : wholeColor,
185
186
  height: lineHeight,
186
187
  lineHeight,
187
- paddingRight: status === "transition" ? `${allDigitRefs[`d_${d}`]?.current?.offsetWidth}px` : "0"
188
+ paddingRight: status === "transition" ? `${offsetWidth}px` : void 0
188
189
  },
189
190
  children: [
190
191
  status === "inactive" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -195,7 +196,7 @@ var Odometer = ({
195
196
  top: 0,
196
197
  height: lineHeight,
197
198
  lineHeight,
198
- width: `${allDigitRefs[`d_${d}`]?.current?.offsetWidth}px`
199
+ width: offsetWidth ? `${offsetWidth}px` : void 0
199
200
  },
200
201
  children: d === "dot" ? "." : d === "comma" ? "," : d
201
202
  }
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\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'\n\t\t\t\t\t\t\t\t\t\t\t? `${allDigitRefs[`d_${d}`]?.current?.offsetWidth}px`\n\t\t\t\t\t\t\t\t\t\t\t: '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: `${\n\t\t\t\t\t\t\t\t\t\t\t\tallDigitRefs[`d_${d}`]?.current?.offsetWidth\n\t\t\t\t\t\t\t\t\t\t\t}px`,\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 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 =\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
@@ -150,6 +150,7 @@ var Odometer = ({
150
150
  }
151
151
  );
152
152
  }
153
+ const offsetWidth = allDigitRefs[`d_${d}`]?.current?.getBoundingClientRect().width;
153
154
  return /* @__PURE__ */ jsxs(
154
155
  "span",
155
156
  {
@@ -159,7 +160,7 @@ var Odometer = ({
159
160
  color: foundDecimal ? decimalColor : wholeColor,
160
161
  height: lineHeight,
161
162
  lineHeight,
162
- paddingRight: status === "transition" ? `${allDigitRefs[`d_${d}`]?.current?.offsetWidth}px` : "0"
163
+ paddingRight: status === "transition" ? `${offsetWidth}px` : void 0
163
164
  },
164
165
  children: [
165
166
  status === "inactive" && /* @__PURE__ */ jsx(
@@ -170,7 +171,7 @@ var Odometer = ({
170
171
  top: 0,
171
172
  height: lineHeight,
172
173
  lineHeight,
173
- width: `${allDigitRefs[`d_${d}`]?.current?.offsetWidth}px`
174
+ width: offsetWidth ? `${offsetWidth}px` : void 0
174
175
  },
175
176
  children: d === "dot" ? "." : d === "comma" ? "," : d
176
177
  }
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\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'\n\t\t\t\t\t\t\t\t\t\t\t? `${allDigitRefs[`d_${d}`]?.current?.offsetWidth}px`\n\t\t\t\t\t\t\t\t\t\t\t: '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: `${\n\t\t\t\t\t\t\t\t\t\t\t\tallDigitRefs[`d_${d}`]?.current?.offsetWidth\n\t\t\t\t\t\t\t\t\t\t\t}px`,\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 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 =\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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@w3ux/react-odometer",
3
- "version": "2.3.13",
3
+ "version": "2.3.15",
4
4
  "license": "GPL-3.0-only",
5
5
  "type": "module",
6
6
  "description": "An odometer effect used for number and balance transitions",