@w3ux/react-odometer 2.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # Odometer
2
+
3
+ An odometer effect used for number and balance transitions
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @w3ux/react-odometer
9
+ ```
10
+
11
+ or
12
+
13
+ ```bash
14
+ yarn add @w3ux/react-odometer
15
+ ```
16
+
17
+ or
18
+
19
+ ```bash
20
+ pnpm add @w3ux/react-odometer
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ```typescript
26
+ import { /* your imports */ } from '@w3ux/react-odometer'
27
+ ```
28
+
29
+ ### React Usage
30
+
31
+ This package provides React hooks and components. Here's a basic example:
32
+
33
+ ```tsx
34
+ import React from 'react'
35
+ import { /* specific hook or component */ } from '@w3ux/react-odometer'
36
+
37
+ function MyComponent() {
38
+ // Use the imported hooks or components here
39
+ return <div>Your component content</div>
40
+ }
41
+ ```
42
+
43
+ ## Documentation
44
+
45
+ For comprehensive documentation and examples, visit the [w3ux documentation](https://w3ux.org/library/react-odometer).
46
+
47
+ ## Keywords
48
+
49
+ `w3ux`, `polkadot`, `web3`, `react`, `odometer`, `animation`, `transitions`, `typescript`
50
+
51
+ ## Repository
52
+
53
+ - **Source**: [GitHub](https://github.com/w3ux/w3ux-library)
54
+ - **Package**: [npm](https://www.npmjs.com/package/@w3ux/react-odometer)
55
+ - **Issues**: [GitHub Issues](https://github.com/w3ux/w3ux-library/issues)
56
+
57
+ ## License
58
+
59
+ This package is licensed under the GPL-3.0-only.
60
+
61
+ ---
62
+
63
+ Part of the [w3ux library](https://github.com/w3ux/w3ux-library) - A collection of packages for building Web3 applications.
package/cjs/index.js CHANGED
@@ -65,7 +65,7 @@ const Odometer = ({ value, spaceBefore = 0, spaceAfter = '0.25rem', wholeColor =
65
65
  }, DURATION_MS);
66
66
  }
67
67
  }, [status, digitRefs]);
68
- const odometerCurrent = odometerRef === null || odometerRef === void 0 ? void 0 : odometerRef.current;
68
+ const odometerCurrent = odometerRef.current;
69
69
  let lineHeight = odometerCurrent
70
70
  ? window.getComputedStyle(odometerCurrent).lineHeight
71
71
  : 'inherit';
package/cjs/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/* @license Copyright 2024 w3ux authors & contributors\nSPDX-License-Identifier: GPL-3.0-only */\n\nimport { createRef, useEffect, useRef, useState } from 'react'\nimport './index.css'\nimport type { Digit, DigitRef, Direction, Props, Status } from './types'\n\nexport const Odometer = ({\n value,\n spaceBefore = 0,\n spaceAfter = '0.25rem',\n wholeColor = 'var(--text-color-primary)',\n decimalColor = 'var(--text-color-secondary)',\n zeroDecimals = 0,\n}: Props) => {\n // Store all possible digits.\n const [allDigits] = useState<Digit[]>([\n 'comma',\n 'dot',\n '0',\n '1',\n '2',\n '3',\n '4',\n '5',\n '6',\n '7',\n '8',\n '9',\n ])\n\n // Store the digits of the current value.\n const [digits, setDigits] = useState<Digit[]>([])\n\n // Store digits of the previous value.\n const [prevDigits, setPrevDigits] = useState<Digit[]>([])\n\n // Store the status of the odometer (transitioning or stable).\n const [status, setStatus] = useState<Status>('inactive')\n\n // Store whether component has iniiialized.\n const [initialized, setInitialized] = useState<boolean>(false)\n\n // Store ref of the odometer.\n const [odometerRef] = useState(createRef<HTMLSpanElement>())\n\n // Store refs of each digit.\n const [digitRefs, setDigitRefs] = useState<DigitRef[]>([])\n\n // Store refs of each `all` digit.\n const [allDigitRefs, setAllDigitRefs] = useState<Record<string, DigitRef>>({})\n\n // Keep track of active transitions.\n const activeTransitionCounter = useRef<number>(0)\n\n // Transition duration.\n const DURATION_MS = 750\n const DURATION_SECS = `${DURATION_MS / 1000}s`\n\n // Phase 0: populate `allDigitRefs`.\n useEffect(() => {\n const all: Record<string, DigitRef> = Object.fromEntries(\n Object.values(allDigits).map((v) => [`d_${v}`, createRef()])\n )\n\n setAllDigitRefs(all)\n }, [])\n\n // Phase 1: new digits and refs are added to the odometer.\n useEffect(() => {\n if (Object.keys(allDigitRefs)) {\n value =\n String(value) === '0' ? Number(value).toFixed(zeroDecimals) : value\n\n const newDigits = value\n .toString()\n .split('')\n .map((v) => (v === '.' ? 'dot' : v))\n .map((v) => (v === ',' ? 'comma' : v)) as Digit[]\n\n setDigits(newDigits)\n\n if (!initialized) {\n setInitialized(true)\n } else {\n setStatus('new')\n setPrevDigits(digits)\n }\n setDigitRefs(Array(newDigits.length).fill(createRef()))\n }\n }, [value])\n\n // Phase 2: set up digit transition.\n useEffect(() => {\n if (status === 'new' && !digitRefs.find((d) => d.current === null)) {\n setStatus('transition')\n activeTransitionCounter.current++\n\n setTimeout(() => {\n activeTransitionCounter.current--\n if (activeTransitionCounter.current === 0) {\n setStatus('inactive')\n }\n }, DURATION_MS)\n }\n }, [status, digitRefs])\n\n const odometerCurrent: Element = odometerRef?.current\n let lineHeight = odometerCurrent\n ? window.getComputedStyle(odometerCurrent).lineHeight\n : 'inherit'\n\n // Fallback line height to `1.1rem` if `normal`.\n lineHeight = lineHeight === 'normal' ? '1.1rem' : lineHeight\n\n // Track whether decimal point has been found.\n let foundDecimal = false\n\n return (\n <>\n {allDigits.map((d, i) => (\n <span\n key={`odometer_template_digit_${i}`}\n ref={allDigitRefs[`d_${d}`]}\n style={{\n opacity: 0,\n position: 'fixed',\n top: '-999%',\n left: '-999%',\n userSelect: 'none',\n }}\n >\n {d === 'dot' ? '.' : d === 'comma' ? ',' : d}\n </span>\n ))}\n <span className=\"odometer\">\n <span className=\"odometer-inner\" ref={odometerRef}>\n {spaceBefore ? <span style={{ paddingLeft: spaceBefore }} /> : null}\n {digits.map((d, i) => {\n if (d === 'dot') {\n foundDecimal = true\n }\n\n // If transitioning, get digits needed to animate.\n let childDigits = null\n if (status === 'transition') {\n const digitsToAnimate = []\n const digitIndex = allDigits.indexOf(digits[i])\n const prevDigitIndex = allDigits.indexOf(prevDigits[i])\n const difference = Math.abs(digitIndex - prevDigitIndex)\n const delay = `${0.01 * (digits.length - i - 1)}s`\n const direction: Direction =\n digitIndex === prevDigitIndex ? 'none' : 'down'\n const animClass = `slide-${direction}-${difference} `\n\n // Push current prev digit to stop of stack.\n digitsToAnimate.push(prevDigits[i])\n\n // If transitioning between two digits, animate all digits in between.\n if (digitIndex < prevDigitIndex) {\n digitsToAnimate.push(\n ...Array.from(\n { length: difference },\n (_, k) => allDigits[prevDigitIndex - k - 1]\n )\n )\n } else {\n digitsToAnimate.push(\n ...Array.from(\n { length: difference },\n (_, k) => allDigits[k + prevDigitIndex + 1]\n )\n )\n }\n\n childDigits = (\n <span\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n animationName: direction === 'none' ? undefined : animClass,\n animationDuration:\n direction === 'none' ? undefined : DURATION_SECS,\n animationFillMode: 'forwards',\n animationTimingFunction: 'cubic-bezier(0.1, 1, 0.2, 1)',\n animationDelay: delay,\n color: foundDecimal ? decimalColor : wholeColor,\n userSelect: 'none',\n }}\n >\n {digitsToAnimate.map((c, j) => (\n <span\n key={`child_digit_${j}`}\n className=\"odometer-digit odometer-child\"\n style={{\n top: j === 0 ? 0 : `${100 * j}%`,\n height: lineHeight,\n lineHeight,\n }}\n >\n {c === 'dot' ? '.' : c === 'comma' ? ',' : c}\n </span>\n ))}\n </span>\n )\n }\n\n return (\n <span\n key={`digit_${i}`}\n ref={digitRefs[i]}\n className=\"odometer-digit\"\n style={{\n color: foundDecimal ? decimalColor : wholeColor,\n height: lineHeight,\n lineHeight,\n paddingRight:\n status === 'transition'\n ? `${allDigitRefs[`d_${d}`]?.current?.offsetWidth}px`\n : '0',\n }}\n >\n {status === 'inactive' && (\n <span\n className=\"odometer-digit odometer-child\"\n style={{\n top: 0,\n height: lineHeight,\n lineHeight,\n width: `${\n allDigitRefs[`d_${d}`]?.current?.offsetWidth\n }px`,\n }}\n >\n {d === 'dot' ? '.' : d === 'comma' ? ',' : d}\n </span>\n )}\n {status === 'transition' && childDigits}\n </span>\n )\n })}\n {spaceAfter ? <span style={{ paddingRight: spaceAfter }} /> : null}\n </span>\n </span>\n </>\n )\n}\n\nexport default Odometer\n"]}
1
+ {"version":3,"sources":["../src/index.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/* @license Copyright 2024 w3ux authors & contributors\nSPDX-License-Identifier: GPL-3.0-only */\n\nimport type { RefObject } from 'react'\nimport { createRef, useEffect, useRef, useState } from 'react'\nimport './index.css'\nimport type { Digit, DigitRef, Direction, Props, Status } from './types'\n\nexport const Odometer = ({\n value,\n spaceBefore = 0,\n spaceAfter = '0.25rem',\n wholeColor = 'var(--text-color-primary)',\n decimalColor = 'var(--text-color-secondary)',\n zeroDecimals = 0,\n}: Props) => {\n // Store all possible digits.\n const [allDigits] = useState<Digit[]>([\n 'comma',\n 'dot',\n '0',\n '1',\n '2',\n '3',\n '4',\n '5',\n '6',\n '7',\n '8',\n '9',\n ])\n\n // Store the digits of the current value.\n const [digits, setDigits] = useState<Digit[]>([])\n\n // Store digits of the previous value.\n const [prevDigits, setPrevDigits] = useState<Digit[]>([])\n\n // Store the status of the odometer (transitioning or stable).\n const [status, setStatus] = useState<Status>('inactive')\n\n // Store whether component has iniiialized.\n const [initialized, setInitialized] = useState<boolean>(false)\n\n // Store ref of the odometer.\n const [odometerRef] = useState(createRef<HTMLSpanElement>())\n\n // Store refs of each digit.\n const [digitRefs, setDigitRefs] = useState<DigitRef[]>([])\n\n // Store refs of each `all` digit.\n const [allDigitRefs, setAllDigitRefs] = useState<\n Record<string, RefObject<HTMLSpanElement | null>>\n >({})\n\n // Keep track of active transitions.\n const activeTransitionCounter = useRef<number>(0)\n\n // Transition duration.\n const DURATION_MS = 750\n const DURATION_SECS = `${DURATION_MS / 1000}s`\n\n // Phase 0: populate `allDigitRefs`.\n useEffect(() => {\n const all: Record<\n string,\n RefObject<HTMLSpanElement | null>\n > = Object.fromEntries(\n Object.values(allDigits).map((v) => [`d_${v}`, createRef()])\n )\n\n setAllDigitRefs(all)\n }, [])\n\n // Phase 1: new digits and refs are added to the odometer.\n useEffect(() => {\n if (Object.keys(allDigitRefs)) {\n value =\n String(value) === '0' ? Number(value).toFixed(zeroDecimals) : value\n\n const newDigits = value\n .toString()\n .split('')\n .map((v) => (v === '.' ? 'dot' : v))\n .map((v) => (v === ',' ? 'comma' : v)) as Digit[]\n\n setDigits(newDigits)\n\n if (!initialized) {\n setInitialized(true)\n } else {\n setStatus('new')\n setPrevDigits(digits)\n }\n setDigitRefs(Array(newDigits.length).fill(createRef()))\n }\n }, [value])\n\n // Phase 2: set up digit transition.\n useEffect(() => {\n if (status === 'new' && !digitRefs.find((d) => d.current === null)) {\n setStatus('transition')\n activeTransitionCounter.current++\n\n setTimeout(() => {\n activeTransitionCounter.current--\n if (activeTransitionCounter.current === 0) {\n setStatus('inactive')\n }\n }, DURATION_MS)\n }\n }, [status, digitRefs])\n\n const odometerCurrent: HTMLSpanElement | null = odometerRef.current\n let lineHeight = odometerCurrent\n ? window.getComputedStyle(odometerCurrent).lineHeight\n : 'inherit'\n\n // Fallback line height to `1.1rem` if `normal`.\n lineHeight = lineHeight === 'normal' ? '1.1rem' : lineHeight\n\n // Track whether decimal point has been found.\n let foundDecimal = false\n\n return (\n <>\n {allDigits.map((d, i) => (\n <span\n key={`odometer_template_digit_${i}`}\n ref={allDigitRefs[`d_${d}`]}\n style={{\n opacity: 0,\n position: 'fixed',\n top: '-999%',\n left: '-999%',\n userSelect: 'none',\n }}\n >\n {d === 'dot' ? '.' : d === 'comma' ? ',' : d}\n </span>\n ))}\n <span className=\"odometer\">\n <span className=\"odometer-inner\" ref={odometerRef}>\n {spaceBefore ? <span style={{ paddingLeft: spaceBefore }} /> : null}\n {digits.map((d, i) => {\n if (d === 'dot') {\n foundDecimal = true\n }\n\n // If transitioning, get digits needed to animate.\n let childDigits = null\n if (status === 'transition') {\n const digitsToAnimate = []\n const digitIndex = allDigits.indexOf(digits[i])\n const prevDigitIndex = allDigits.indexOf(prevDigits[i])\n const difference = Math.abs(digitIndex - prevDigitIndex)\n const delay = `${0.01 * (digits.length - i - 1)}s`\n const direction: Direction =\n digitIndex === prevDigitIndex ? 'none' : 'down'\n const animClass = `slide-${direction}-${difference} `\n\n // Push current prev digit to stop of stack.\n digitsToAnimate.push(prevDigits[i])\n\n // If transitioning between two digits, animate all digits in between.\n if (digitIndex < prevDigitIndex) {\n digitsToAnimate.push(\n ...Array.from(\n { length: difference },\n (_, k) => allDigits[prevDigitIndex - k - 1]\n )\n )\n } else {\n digitsToAnimate.push(\n ...Array.from(\n { length: difference },\n (_, k) => allDigits[k + prevDigitIndex + 1]\n )\n )\n }\n\n childDigits = (\n <span\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n animationName: direction === 'none' ? undefined : animClass,\n animationDuration:\n direction === 'none' ? undefined : DURATION_SECS,\n animationFillMode: 'forwards',\n animationTimingFunction: 'cubic-bezier(0.1, 1, 0.2, 1)',\n animationDelay: delay,\n color: foundDecimal ? decimalColor : wholeColor,\n userSelect: 'none',\n }}\n >\n {digitsToAnimate.map((c, j) => (\n <span\n key={`child_digit_${j}`}\n className=\"odometer-digit odometer-child\"\n style={{\n top: j === 0 ? 0 : `${100 * j}%`,\n height: lineHeight,\n lineHeight,\n }}\n >\n {c === 'dot' ? '.' : c === 'comma' ? ',' : c}\n </span>\n ))}\n </span>\n )\n }\n\n return (\n <span\n key={`digit_${i}`}\n ref={digitRefs[i]}\n className=\"odometer-digit\"\n style={{\n color: foundDecimal ? decimalColor : wholeColor,\n height: lineHeight,\n lineHeight,\n paddingRight:\n status === 'transition'\n ? `${allDigitRefs[`d_${d}`]?.current?.offsetWidth}px`\n : '0',\n }}\n >\n {status === 'inactive' && (\n <span\n className=\"odometer-digit odometer-child\"\n style={{\n top: 0,\n height: lineHeight,\n lineHeight,\n width: `${\n allDigitRefs[`d_${d}`]?.current?.offsetWidth\n }px`,\n }}\n >\n {d === 'dot' ? '.' : d === 'comma' ? ',' : d}\n </span>\n )}\n {status === 'transition' && childDigits}\n </span>\n )\n })}\n {spaceAfter ? <span style={{ paddingRight: spaceAfter }} /> : null}\n </span>\n </span>\n </>\n )\n}\n\nexport default Odometer\n"]}
package/mjs/index.js CHANGED
@@ -62,7 +62,7 @@ export const Odometer = ({ value, spaceBefore = 0, spaceAfter = '0.25rem', whole
62
62
  }, DURATION_MS);
63
63
  }
64
64
  }, [status, digitRefs]);
65
- const odometerCurrent = odometerRef?.current;
65
+ const odometerCurrent = odometerRef.current;
66
66
  let lineHeight = odometerCurrent
67
67
  ? window.getComputedStyle(odometerCurrent).lineHeight
68
68
  : 'inherit';
package/mjs/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/* @license Copyright 2024 w3ux authors & contributors\nSPDX-License-Identifier: GPL-3.0-only */\n\nimport { createRef, useEffect, useRef, useState } from 'react'\nimport './index.css'\nimport type { Digit, DigitRef, Direction, Props, Status } from './types'\n\nexport const Odometer = ({\n value,\n spaceBefore = 0,\n spaceAfter = '0.25rem',\n wholeColor = 'var(--text-color-primary)',\n decimalColor = 'var(--text-color-secondary)',\n zeroDecimals = 0,\n}: Props) => {\n // Store all possible digits.\n const [allDigits] = useState<Digit[]>([\n 'comma',\n 'dot',\n '0',\n '1',\n '2',\n '3',\n '4',\n '5',\n '6',\n '7',\n '8',\n '9',\n ])\n\n // Store the digits of the current value.\n const [digits, setDigits] = useState<Digit[]>([])\n\n // Store digits of the previous value.\n const [prevDigits, setPrevDigits] = useState<Digit[]>([])\n\n // Store the status of the odometer (transitioning or stable).\n const [status, setStatus] = useState<Status>('inactive')\n\n // Store whether component has iniiialized.\n const [initialized, setInitialized] = useState<boolean>(false)\n\n // Store ref of the odometer.\n const [odometerRef] = useState(createRef<HTMLSpanElement>())\n\n // Store refs of each digit.\n const [digitRefs, setDigitRefs] = useState<DigitRef[]>([])\n\n // Store refs of each `all` digit.\n const [allDigitRefs, setAllDigitRefs] = useState<Record<string, DigitRef>>({})\n\n // Keep track of active transitions.\n const activeTransitionCounter = useRef<number>(0)\n\n // Transition duration.\n const DURATION_MS = 750\n const DURATION_SECS = `${DURATION_MS / 1000}s`\n\n // Phase 0: populate `allDigitRefs`.\n useEffect(() => {\n const all: Record<string, DigitRef> = Object.fromEntries(\n Object.values(allDigits).map((v) => [`d_${v}`, createRef()])\n )\n\n setAllDigitRefs(all)\n }, [])\n\n // Phase 1: new digits and refs are added to the odometer.\n useEffect(() => {\n if (Object.keys(allDigitRefs)) {\n value =\n String(value) === '0' ? Number(value).toFixed(zeroDecimals) : value\n\n const newDigits = value\n .toString()\n .split('')\n .map((v) => (v === '.' ? 'dot' : v))\n .map((v) => (v === ',' ? 'comma' : v)) as Digit[]\n\n setDigits(newDigits)\n\n if (!initialized) {\n setInitialized(true)\n } else {\n setStatus('new')\n setPrevDigits(digits)\n }\n setDigitRefs(Array(newDigits.length).fill(createRef()))\n }\n }, [value])\n\n // Phase 2: set up digit transition.\n useEffect(() => {\n if (status === 'new' && !digitRefs.find((d) => d.current === null)) {\n setStatus('transition')\n activeTransitionCounter.current++\n\n setTimeout(() => {\n activeTransitionCounter.current--\n if (activeTransitionCounter.current === 0) {\n setStatus('inactive')\n }\n }, DURATION_MS)\n }\n }, [status, digitRefs])\n\n const odometerCurrent: Element = odometerRef?.current\n let lineHeight = odometerCurrent\n ? window.getComputedStyle(odometerCurrent).lineHeight\n : 'inherit'\n\n // Fallback line height to `1.1rem` if `normal`.\n lineHeight = lineHeight === 'normal' ? '1.1rem' : lineHeight\n\n // Track whether decimal point has been found.\n let foundDecimal = false\n\n return (\n <>\n {allDigits.map((d, i) => (\n <span\n key={`odometer_template_digit_${i}`}\n ref={allDigitRefs[`d_${d}`]}\n style={{\n opacity: 0,\n position: 'fixed',\n top: '-999%',\n left: '-999%',\n userSelect: 'none',\n }}\n >\n {d === 'dot' ? '.' : d === 'comma' ? ',' : d}\n </span>\n ))}\n <span className=\"odometer\">\n <span className=\"odometer-inner\" ref={odometerRef}>\n {spaceBefore ? <span style={{ paddingLeft: spaceBefore }} /> : null}\n {digits.map((d, i) => {\n if (d === 'dot') {\n foundDecimal = true\n }\n\n // If transitioning, get digits needed to animate.\n let childDigits = null\n if (status === 'transition') {\n const digitsToAnimate = []\n const digitIndex = allDigits.indexOf(digits[i])\n const prevDigitIndex = allDigits.indexOf(prevDigits[i])\n const difference = Math.abs(digitIndex - prevDigitIndex)\n const delay = `${0.01 * (digits.length - i - 1)}s`\n const direction: Direction =\n digitIndex === prevDigitIndex ? 'none' : 'down'\n const animClass = `slide-${direction}-${difference} `\n\n // Push current prev digit to stop of stack.\n digitsToAnimate.push(prevDigits[i])\n\n // If transitioning between two digits, animate all digits in between.\n if (digitIndex < prevDigitIndex) {\n digitsToAnimate.push(\n ...Array.from(\n { length: difference },\n (_, k) => allDigits[prevDigitIndex - k - 1]\n )\n )\n } else {\n digitsToAnimate.push(\n ...Array.from(\n { length: difference },\n (_, k) => allDigits[k + prevDigitIndex + 1]\n )\n )\n }\n\n childDigits = (\n <span\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n animationName: direction === 'none' ? undefined : animClass,\n animationDuration:\n direction === 'none' ? undefined : DURATION_SECS,\n animationFillMode: 'forwards',\n animationTimingFunction: 'cubic-bezier(0.1, 1, 0.2, 1)',\n animationDelay: delay,\n color: foundDecimal ? decimalColor : wholeColor,\n userSelect: 'none',\n }}\n >\n {digitsToAnimate.map((c, j) => (\n <span\n key={`child_digit_${j}`}\n className=\"odometer-digit odometer-child\"\n style={{\n top: j === 0 ? 0 : `${100 * j}%`,\n height: lineHeight,\n lineHeight,\n }}\n >\n {c === 'dot' ? '.' : c === 'comma' ? ',' : c}\n </span>\n ))}\n </span>\n )\n }\n\n return (\n <span\n key={`digit_${i}`}\n ref={digitRefs[i]}\n className=\"odometer-digit\"\n style={{\n color: foundDecimal ? decimalColor : wholeColor,\n height: lineHeight,\n lineHeight,\n paddingRight:\n status === 'transition'\n ? `${allDigitRefs[`d_${d}`]?.current?.offsetWidth}px`\n : '0',\n }}\n >\n {status === 'inactive' && (\n <span\n className=\"odometer-digit odometer-child\"\n style={{\n top: 0,\n height: lineHeight,\n lineHeight,\n width: `${\n allDigitRefs[`d_${d}`]?.current?.offsetWidth\n }px`,\n }}\n >\n {d === 'dot' ? '.' : d === 'comma' ? ',' : d}\n </span>\n )}\n {status === 'transition' && childDigits}\n </span>\n )\n })}\n {spaceAfter ? <span style={{ paddingRight: spaceAfter }} /> : null}\n </span>\n </span>\n </>\n )\n}\n\nexport default Odometer\n"]}
1
+ {"version":3,"sources":["../src/index.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/* @license Copyright 2024 w3ux authors & contributors\nSPDX-License-Identifier: GPL-3.0-only */\n\nimport type { RefObject } from 'react'\nimport { createRef, useEffect, useRef, useState } from 'react'\nimport './index.css'\nimport type { Digit, DigitRef, Direction, Props, Status } from './types'\n\nexport const Odometer = ({\n value,\n spaceBefore = 0,\n spaceAfter = '0.25rem',\n wholeColor = 'var(--text-color-primary)',\n decimalColor = 'var(--text-color-secondary)',\n zeroDecimals = 0,\n}: Props) => {\n // Store all possible digits.\n const [allDigits] = useState<Digit[]>([\n 'comma',\n 'dot',\n '0',\n '1',\n '2',\n '3',\n '4',\n '5',\n '6',\n '7',\n '8',\n '9',\n ])\n\n // Store the digits of the current value.\n const [digits, setDigits] = useState<Digit[]>([])\n\n // Store digits of the previous value.\n const [prevDigits, setPrevDigits] = useState<Digit[]>([])\n\n // Store the status of the odometer (transitioning or stable).\n const [status, setStatus] = useState<Status>('inactive')\n\n // Store whether component has iniiialized.\n const [initialized, setInitialized] = useState<boolean>(false)\n\n // Store ref of the odometer.\n const [odometerRef] = useState(createRef<HTMLSpanElement>())\n\n // Store refs of each digit.\n const [digitRefs, setDigitRefs] = useState<DigitRef[]>([])\n\n // Store refs of each `all` digit.\n const [allDigitRefs, setAllDigitRefs] = useState<\n Record<string, RefObject<HTMLSpanElement | null>>\n >({})\n\n // Keep track of active transitions.\n const activeTransitionCounter = useRef<number>(0)\n\n // Transition duration.\n const DURATION_MS = 750\n const DURATION_SECS = `${DURATION_MS / 1000}s`\n\n // Phase 0: populate `allDigitRefs`.\n useEffect(() => {\n const all: Record<\n string,\n RefObject<HTMLSpanElement | null>\n > = Object.fromEntries(\n Object.values(allDigits).map((v) => [`d_${v}`, createRef()])\n )\n\n setAllDigitRefs(all)\n }, [])\n\n // Phase 1: new digits and refs are added to the odometer.\n useEffect(() => {\n if (Object.keys(allDigitRefs)) {\n value =\n String(value) === '0' ? Number(value).toFixed(zeroDecimals) : value\n\n const newDigits = value\n .toString()\n .split('')\n .map((v) => (v === '.' ? 'dot' : v))\n .map((v) => (v === ',' ? 'comma' : v)) as Digit[]\n\n setDigits(newDigits)\n\n if (!initialized) {\n setInitialized(true)\n } else {\n setStatus('new')\n setPrevDigits(digits)\n }\n setDigitRefs(Array(newDigits.length).fill(createRef()))\n }\n }, [value])\n\n // Phase 2: set up digit transition.\n useEffect(() => {\n if (status === 'new' && !digitRefs.find((d) => d.current === null)) {\n setStatus('transition')\n activeTransitionCounter.current++\n\n setTimeout(() => {\n activeTransitionCounter.current--\n if (activeTransitionCounter.current === 0) {\n setStatus('inactive')\n }\n }, DURATION_MS)\n }\n }, [status, digitRefs])\n\n const odometerCurrent: HTMLSpanElement | null = odometerRef.current\n let lineHeight = odometerCurrent\n ? window.getComputedStyle(odometerCurrent).lineHeight\n : 'inherit'\n\n // Fallback line height to `1.1rem` if `normal`.\n lineHeight = lineHeight === 'normal' ? '1.1rem' : lineHeight\n\n // Track whether decimal point has been found.\n let foundDecimal = false\n\n return (\n <>\n {allDigits.map((d, i) => (\n <span\n key={`odometer_template_digit_${i}`}\n ref={allDigitRefs[`d_${d}`]}\n style={{\n opacity: 0,\n position: 'fixed',\n top: '-999%',\n left: '-999%',\n userSelect: 'none',\n }}\n >\n {d === 'dot' ? '.' : d === 'comma' ? ',' : d}\n </span>\n ))}\n <span className=\"odometer\">\n <span className=\"odometer-inner\" ref={odometerRef}>\n {spaceBefore ? <span style={{ paddingLeft: spaceBefore }} /> : null}\n {digits.map((d, i) => {\n if (d === 'dot') {\n foundDecimal = true\n }\n\n // If transitioning, get digits needed to animate.\n let childDigits = null\n if (status === 'transition') {\n const digitsToAnimate = []\n const digitIndex = allDigits.indexOf(digits[i])\n const prevDigitIndex = allDigits.indexOf(prevDigits[i])\n const difference = Math.abs(digitIndex - prevDigitIndex)\n const delay = `${0.01 * (digits.length - i - 1)}s`\n const direction: Direction =\n digitIndex === prevDigitIndex ? 'none' : 'down'\n const animClass = `slide-${direction}-${difference} `\n\n // Push current prev digit to stop of stack.\n digitsToAnimate.push(prevDigits[i])\n\n // If transitioning between two digits, animate all digits in between.\n if (digitIndex < prevDigitIndex) {\n digitsToAnimate.push(\n ...Array.from(\n { length: difference },\n (_, k) => allDigits[prevDigitIndex - k - 1]\n )\n )\n } else {\n digitsToAnimate.push(\n ...Array.from(\n { length: difference },\n (_, k) => allDigits[k + prevDigitIndex + 1]\n )\n )\n }\n\n childDigits = (\n <span\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n animationName: direction === 'none' ? undefined : animClass,\n animationDuration:\n direction === 'none' ? undefined : DURATION_SECS,\n animationFillMode: 'forwards',\n animationTimingFunction: 'cubic-bezier(0.1, 1, 0.2, 1)',\n animationDelay: delay,\n color: foundDecimal ? decimalColor : wholeColor,\n userSelect: 'none',\n }}\n >\n {digitsToAnimate.map((c, j) => (\n <span\n key={`child_digit_${j}`}\n className=\"odometer-digit odometer-child\"\n style={{\n top: j === 0 ? 0 : `${100 * j}%`,\n height: lineHeight,\n lineHeight,\n }}\n >\n {c === 'dot' ? '.' : c === 'comma' ? ',' : c}\n </span>\n ))}\n </span>\n )\n }\n\n return (\n <span\n key={`digit_${i}`}\n ref={digitRefs[i]}\n className=\"odometer-digit\"\n style={{\n color: foundDecimal ? decimalColor : wholeColor,\n height: lineHeight,\n lineHeight,\n paddingRight:\n status === 'transition'\n ? `${allDigitRefs[`d_${d}`]?.current?.offsetWidth}px`\n : '0',\n }}\n >\n {status === 'inactive' && (\n <span\n className=\"odometer-digit odometer-child\"\n style={{\n top: 0,\n height: lineHeight,\n lineHeight,\n width: `${\n allDigitRefs[`d_${d}`]?.current?.offsetWidth\n }px`,\n }}\n >\n {d === 'dot' ? '.' : d === 'comma' ? ',' : d}\n </span>\n )}\n {status === 'transition' && childDigits}\n </span>\n )\n })}\n {spaceAfter ? <span style={{ paddingRight: spaceAfter }} /> : null}\n </span>\n </span>\n </>\n )\n}\n\nexport default Odometer\n"]}
package/package.json CHANGED
@@ -1,8 +1,28 @@
1
1
  {
2
2
  "name": "@w3ux/react-odometer",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "license": "GPL-3.0-only",
5
5
  "type": "module",
6
+ "description": "An odometer effect used for number and balance transitions",
7
+ "keywords": [
8
+ "w3ux",
9
+ "polkadot",
10
+ "web3",
11
+ "react",
12
+ "odometer",
13
+ "animation",
14
+ "transitions",
15
+ "typescript"
16
+ ],
17
+ "homepage": "https://w3ux.org/library/react-odometer",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/w3ux/w3ux-library.git",
21
+ "directory": "library/react-odometer"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/w3ux/w3ux-library/issues"
25
+ },
6
26
  "exports": {
7
27
  ".": {
8
28
  "import": "./mjs/index.js",