@w3ux/react-odometer 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cjs/index.css CHANGED
@@ -1 +1,221 @@
1
- .odometer{display:flex;align-items:flex-end;position:relative;overflow:hidden;height:inherit;line-height:inherit;width:fit-content}.odometer>.odometer-inner{display:inline-block;vertical-align:bottom}.odometer>.odometer-inner .odometer-digit{display:inline-block;position:relative;vertical-align:bottom;z-index:3}.odometer>.odometer-inner .odometer-digit.odometer-child{display:flex;align-items:flex-end;position:relative;left:0}@keyframes slide-up-1{from{top:0}to{top:100%}}@keyframes slide-up-2{from{top:0}to{top:200%}}@keyframes slide-up-3{from{top:0}to{top:300%}}@keyframes slide-up-4{from{top:0}to{top:400%}}@keyframes slide-up-5{from{top:0}to{top:500%}}@keyframes slide-up-6{from{top:0}to{top:600%}}@keyframes slide-up-7{from{top:0}to{top:700%}}@keyframes slide-up-8{from{top:0}to{top:800%}}@keyframes slide-up-9{from{top:0}to{top:900%}}@keyframes slide-up-10{from{top:0}to{top:1000%}}@keyframes slide-up-11{from{top:0}to{top:1100%}}@keyframes slide-up-12{from{top:0}to{top:1200%}}@keyframes slide-down-1{from{top:0}to{top:-100%}}@keyframes slide-down-2{from{top:0}to{top:-200%}}@keyframes slide-down-3{from{top:0}to{top:-300%}}@keyframes slide-down-4{from{top:0}to{top:-400%}}@keyframes slide-down-5{from{top:0}to{top:-500%}}@keyframes slide-down-6{from{top:0}to{top:-600%}}@keyframes slide-down-7{from{top:0}to{top:-700%}}@keyframes slide-down-8{from{top:0}to{top:-800%}}@keyframes slide-down-9{from{top:0}to{top:-900%}}@keyframes slide-down-10{from{top:0}to{top:-1000%}}@keyframes slide-down-11{from{top:0}to{top:-1100%}}@keyframes slide-down-12{from{top:0}to{top:-1200%}}
1
+ /* @license Copyright 2024 w3ux authors & contributors
2
+ SPDX-License-Identifier: GPL-3.0-only */
3
+ .odometer {
4
+ display: flex;
5
+ align-items: flex-end;
6
+ position: relative;
7
+ overflow: hidden;
8
+ height: inherit;
9
+ line-height: inherit;
10
+ width: fit-content;
11
+ }
12
+ .odometer > .odometer-inner {
13
+ display: inline-block;
14
+ vertical-align: bottom;
15
+ }
16
+ .odometer > .odometer-inner .odometer-digit {
17
+ display: inline-block;
18
+ position: relative;
19
+ vertical-align: bottom;
20
+ z-index: 3;
21
+ }
22
+ .odometer > .odometer-inner .odometer-digit.odometer-child {
23
+ display: flex;
24
+ align-items: flex-end;
25
+ position: relative;
26
+ left: 0;
27
+ }
28
+
29
+ /* NOTE: Stylelint does not like inline scss variables to generate these. */
30
+ @keyframes slide-up-1 {
31
+ from {
32
+ top: 0;
33
+ }
34
+ to {
35
+ top: 100%;
36
+ }
37
+ }
38
+ @keyframes slide-up-2 {
39
+ from {
40
+ top: 0;
41
+ }
42
+ to {
43
+ top: 200%;
44
+ }
45
+ }
46
+ @keyframes slide-up-3 {
47
+ from {
48
+ top: 0;
49
+ }
50
+ to {
51
+ top: 300%;
52
+ }
53
+ }
54
+ @keyframes slide-up-4 {
55
+ from {
56
+ top: 0;
57
+ }
58
+ to {
59
+ top: 400%;
60
+ }
61
+ }
62
+ @keyframes slide-up-5 {
63
+ from {
64
+ top: 0;
65
+ }
66
+ to {
67
+ top: 500%;
68
+ }
69
+ }
70
+ @keyframes slide-up-6 {
71
+ from {
72
+ top: 0;
73
+ }
74
+ to {
75
+ top: 600%;
76
+ }
77
+ }
78
+ @keyframes slide-up-7 {
79
+ from {
80
+ top: 0;
81
+ }
82
+ to {
83
+ top: 700%;
84
+ }
85
+ }
86
+ @keyframes slide-up-8 {
87
+ from {
88
+ top: 0;
89
+ }
90
+ to {
91
+ top: 800%;
92
+ }
93
+ }
94
+ @keyframes slide-up-9 {
95
+ from {
96
+ top: 0;
97
+ }
98
+ to {
99
+ top: 900%;
100
+ }
101
+ }
102
+ @keyframes slide-up-10 {
103
+ from {
104
+ top: 0;
105
+ }
106
+ to {
107
+ top: 1000%;
108
+ }
109
+ }
110
+ @keyframes slide-up-11 {
111
+ from {
112
+ top: 0;
113
+ }
114
+ to {
115
+ top: 1100%;
116
+ }
117
+ }
118
+ @keyframes slide-up-12 {
119
+ from {
120
+ top: 0;
121
+ }
122
+ to {
123
+ top: 1200%;
124
+ }
125
+ }
126
+ @keyframes slide-down-1 {
127
+ from {
128
+ top: 0;
129
+ }
130
+ to {
131
+ top: -100%;
132
+ }
133
+ }
134
+ @keyframes slide-down-2 {
135
+ from {
136
+ top: 0;
137
+ }
138
+ to {
139
+ top: -200%;
140
+ }
141
+ }
142
+ @keyframes slide-down-3 {
143
+ from {
144
+ top: 0;
145
+ }
146
+ to {
147
+ top: -300%;
148
+ }
149
+ }
150
+ @keyframes slide-down-4 {
151
+ from {
152
+ top: 0;
153
+ }
154
+ to {
155
+ top: -400%;
156
+ }
157
+ }
158
+ @keyframes slide-down-5 {
159
+ from {
160
+ top: 0;
161
+ }
162
+ to {
163
+ top: -500%;
164
+ }
165
+ }
166
+ @keyframes slide-down-6 {
167
+ from {
168
+ top: 0;
169
+ }
170
+ to {
171
+ top: -600%;
172
+ }
173
+ }
174
+ @keyframes slide-down-7 {
175
+ from {
176
+ top: 0;
177
+ }
178
+ to {
179
+ top: -700%;
180
+ }
181
+ }
182
+ @keyframes slide-down-8 {
183
+ from {
184
+ top: 0;
185
+ }
186
+ to {
187
+ top: -800%;
188
+ }
189
+ }
190
+ @keyframes slide-down-9 {
191
+ from {
192
+ top: 0;
193
+ }
194
+ to {
195
+ top: -900%;
196
+ }
197
+ }
198
+ @keyframes slide-down-10 {
199
+ from {
200
+ top: 0;
201
+ }
202
+ to {
203
+ top: -1000%;
204
+ }
205
+ }
206
+ @keyframes slide-down-11 {
207
+ from {
208
+ top: 0;
209
+ }
210
+ to {
211
+ top: -1100%;
212
+ }
213
+ }
214
+ @keyframes slide-down-12 {
215
+ from {
216
+ top: 0;
217
+ }
218
+ to {
219
+ top: -1200%;
220
+ }
221
+ }
package/cjs/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Props } from "./types";
2
- import "./index.css";
1
+ import './index.css';
2
+ import type { Props } from './types';
3
3
  export declare const Odometer: ({ value, spaceBefore, spaceAfter, wholeColor, decimalColor, zeroDecimals, }: Props) => import("react/jsx-runtime").JSX.Element;
4
4
  export default Odometer;
package/cjs/index.js CHANGED
@@ -4,24 +4,24 @@ exports.Odometer = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  require("./index.css");
7
- const Odometer = ({ value, spaceBefore = 0, spaceAfter = "0.25rem", wholeColor = "var(--text-color-primary)", decimalColor = "var(--text-color-secondary)", zeroDecimals = 0, }) => {
7
+ const Odometer = ({ value, spaceBefore = 0, spaceAfter = '0.25rem', wholeColor = 'var(--text-color-primary)', decimalColor = 'var(--text-color-secondary)', zeroDecimals = 0, }) => {
8
8
  const [allDigits] = (0, react_1.useState)([
9
- "comma",
10
- "dot",
11
- "0",
12
- "1",
13
- "2",
14
- "3",
15
- "4",
16
- "5",
17
- "6",
18
- "7",
19
- "8",
20
- "9",
9
+ 'comma',
10
+ 'dot',
11
+ '0',
12
+ '1',
13
+ '2',
14
+ '3',
15
+ '4',
16
+ '5',
17
+ '6',
18
+ '7',
19
+ '8',
20
+ '9',
21
21
  ]);
22
22
  const [digits, setDigits] = (0, react_1.useState)([]);
23
23
  const [prevDigits, setPrevDigits] = (0, react_1.useState)([]);
24
- const [status, setStatus] = (0, react_1.useState)("inactive");
24
+ const [status, setStatus] = (0, react_1.useState)('inactive');
25
25
  const [initialized, setInitialized] = (0, react_1.useState)(false);
26
26
  const [odometerRef] = (0, react_1.useState)((0, react_1.createRef)());
27
27
  const [digitRefs, setDigitRefs] = (0, react_1.useState)([]);
@@ -36,31 +36,31 @@ const Odometer = ({ value, spaceBefore = 0, spaceAfter = "0.25rem", wholeColor =
36
36
  (0, react_1.useEffect)(() => {
37
37
  if (Object.keys(allDigitRefs)) {
38
38
  value =
39
- String(value) === "0" ? Number(value).toFixed(zeroDecimals) : value;
39
+ String(value) === '0' ? Number(value).toFixed(zeroDecimals) : value;
40
40
  const newDigits = value
41
41
  .toString()
42
- .split("")
43
- .map((v) => (v === "." ? "dot" : v))
44
- .map((v) => (v === "," ? "comma" : v));
42
+ .split('')
43
+ .map((v) => (v === '.' ? 'dot' : v))
44
+ .map((v) => (v === ',' ? 'comma' : v));
45
45
  setDigits(newDigits);
46
46
  if (!initialized) {
47
47
  setInitialized(true);
48
48
  }
49
49
  else {
50
- setStatus("new");
50
+ setStatus('new');
51
51
  setPrevDigits(digits);
52
52
  }
53
53
  setDigitRefs(Array(newDigits.length).fill((0, react_1.createRef)()));
54
54
  }
55
55
  }, [value]);
56
56
  (0, react_1.useEffect)(() => {
57
- if (status === "new" && !digitRefs.find((d) => d.current === null)) {
58
- setStatus("transition");
57
+ if (status === 'new' && !digitRefs.find((d) => d.current === null)) {
58
+ setStatus('transition');
59
59
  activeTransitionCounter.current++;
60
60
  setTimeout(() => {
61
61
  activeTransitionCounter.current--;
62
62
  if (activeTransitionCounter.current === 0) {
63
- setStatus("inactive");
63
+ setStatus('inactive');
64
64
  }
65
65
  }, DURATION_MS);
66
66
  }
@@ -68,28 +68,28 @@ const Odometer = ({ value, spaceBefore = 0, spaceAfter = "0.25rem", wholeColor =
68
68
  const odometerCurrent = odometerRef === null || odometerRef === void 0 ? void 0 : odometerRef.current;
69
69
  let lineHeight = odometerCurrent
70
70
  ? window.getComputedStyle(odometerCurrent).lineHeight
71
- : "inherit";
72
- lineHeight = lineHeight === "normal" ? "1.1rem" : lineHeight;
71
+ : 'inherit';
72
+ lineHeight = lineHeight === 'normal' ? '1.1rem' : lineHeight;
73
73
  let foundDecimal = false;
74
74
  return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [allDigits.map((d, i) => ((0, jsx_runtime_1.jsx)("span", { ref: allDigitRefs[`d_${d}`], style: {
75
75
  opacity: 0,
76
- position: "fixed",
77
- top: "-999%",
78
- left: "-999%",
79
- userSelect: "none",
80
- }, children: d === "dot" ? "." : d === "comma" ? "," : d }, `odometer_template_digit_${i}`))), (0, jsx_runtime_1.jsx)("span", { className: "odometer", children: (0, jsx_runtime_1.jsxs)("span", { className: "odometer-inner", ref: odometerRef, children: [spaceBefore ? (0, jsx_runtime_1.jsx)("span", { style: { paddingLeft: spaceBefore } }) : null, digits.map((d, i) => {
76
+ position: 'fixed',
77
+ top: '-999%',
78
+ left: '-999%',
79
+ userSelect: 'none',
80
+ }, children: d === 'dot' ? '.' : d === 'comma' ? ',' : d }, `odometer_template_digit_${i}`))), (0, jsx_runtime_1.jsx)("span", { className: "odometer", children: (0, jsx_runtime_1.jsxs)("span", { className: "odometer-inner", ref: odometerRef, children: [spaceBefore ? (0, jsx_runtime_1.jsx)("span", { style: { paddingLeft: spaceBefore } }) : null, digits.map((d, i) => {
81
81
  var _a, _b, _c, _d;
82
- if (d === "dot") {
82
+ if (d === 'dot') {
83
83
  foundDecimal = true;
84
84
  }
85
85
  let childDigits = null;
86
- if (status === "transition") {
86
+ if (status === 'transition') {
87
87
  const digitsToAnimate = [];
88
88
  const digitIndex = allDigits.indexOf(digits[i]);
89
89
  const prevDigitIndex = allDigits.indexOf(prevDigits[i]);
90
90
  const difference = Math.abs(digitIndex - prevDigitIndex);
91
91
  const delay = `${0.01 * (digits.length - i - 1)}s`;
92
- const direction = digitIndex === prevDigitIndex ? "none" : "down";
92
+ const direction = digitIndex === prevDigitIndex ? 'none' : 'down';
93
93
  const animClass = `slide-${direction}-${difference} `;
94
94
  digitsToAnimate.push(prevDigits[i]);
95
95
  if (digitIndex < prevDigitIndex) {
@@ -99,35 +99,35 @@ const Odometer = ({ value, spaceBefore = 0, spaceAfter = "0.25rem", wholeColor =
99
99
  digitsToAnimate.push(...Array.from({ length: difference }, (_, k) => allDigits[k + prevDigitIndex + 1]));
100
100
  }
101
101
  childDigits = ((0, jsx_runtime_1.jsx)("span", { style: {
102
- position: "absolute",
102
+ position: 'absolute',
103
103
  top: 0,
104
104
  left: 0,
105
- animationName: direction === "none" ? undefined : animClass,
106
- animationDuration: direction === "none" ? undefined : DURATION_SECS,
107
- animationFillMode: "forwards",
108
- animationTimingFunction: "cubic-bezier(0.1, 1, 0.2, 1)",
105
+ animationName: direction === 'none' ? undefined : animClass,
106
+ animationDuration: direction === 'none' ? undefined : DURATION_SECS,
107
+ animationFillMode: 'forwards',
108
+ animationTimingFunction: 'cubic-bezier(0.1, 1, 0.2, 1)',
109
109
  animationDelay: delay,
110
110
  color: foundDecimal ? decimalColor : wholeColor,
111
- userSelect: "none",
111
+ userSelect: 'none',
112
112
  }, children: digitsToAnimate.map((c, j) => ((0, jsx_runtime_1.jsx)("span", { className: "odometer-digit odometer-child", style: {
113
113
  top: j === 0 ? 0 : `${100 * j}%`,
114
114
  height: lineHeight,
115
115
  lineHeight,
116
- }, children: c === "dot" ? "." : c === "comma" ? "," : c }, `child_digit_${j}`))) }));
116
+ }, children: c === 'dot' ? '.' : c === 'comma' ? ',' : c }, `child_digit_${j}`))) }));
117
117
  }
118
118
  return ((0, jsx_runtime_1.jsxs)("span", { ref: digitRefs[i], className: "odometer-digit", style: {
119
119
  color: foundDecimal ? decimalColor : wholeColor,
120
120
  height: lineHeight,
121
121
  lineHeight,
122
- paddingRight: status === "transition"
122
+ paddingRight: status === 'transition'
123
123
  ? `${(_b = (_a = allDigitRefs[`d_${d}`]) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.offsetWidth}px`
124
- : "0",
125
- }, children: [status === "inactive" && ((0, jsx_runtime_1.jsx)("span", { className: "odometer-digit odometer-child", style: {
124
+ : '0',
125
+ }, children: [status === 'inactive' && ((0, jsx_runtime_1.jsx)("span", { className: "odometer-digit odometer-child", style: {
126
126
  top: 0,
127
127
  height: lineHeight,
128
128
  lineHeight,
129
129
  width: `${(_d = (_c = allDigitRefs[`d_${d}`]) === null || _c === void 0 ? void 0 : _c.current) === null || _d === void 0 ? void 0 : _d.offsetWidth}px`,
130
- }, children: d === "dot" ? "." : d === "comma" ? "," : d })), status === "transition" && childDigits] }, `digit_${i}`));
130
+ }, children: d === 'dot' ? '.' : d === 'comma' ? ',' : d })), status === 'transition' && childDigits] }, `digit_${i}`));
131
131
  }), spaceAfter ? (0, jsx_runtime_1.jsx)("span", { style: { paddingRight: spaceAfter } }) : null] }) })] }));
132
132
  };
133
133
  exports.Odometer = Odometer;
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 { useEffect, useState, createRef, useRef } from \"react\";\nimport { Digit, DigitRef, Direction, Props, Status } from \"./types\";\nimport \"./index.css\";\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 );\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 { 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"]}
package/cjs/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { MutableRefObject } from "react";
1
+ import type { RefObject } from 'react';
2
2
  export interface Props {
3
3
  value: number | string;
4
4
  wholeColor?: string;
@@ -7,7 +7,7 @@ export interface Props {
7
7
  spaceAfter?: string | number;
8
8
  zeroDecimals?: number;
9
9
  }
10
- export type Digit = "comma" | "dot" | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
11
- export type DigitRef = MutableRefObject<HTMLSpanElement>;
12
- export type Status = "new" | "inactive" | "transition" | "finished";
13
- export type Direction = "down" | "none";
10
+ export type Digit = 'comma' | 'dot' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
11
+ export type DigitRef = RefObject<HTMLSpanElement>;
12
+ export type Status = 'new' | 'inactive' | 'transition' | 'finished';
13
+ export type Direction = 'down' | 'none';
package/cjs/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"names":[],"mappings":"","file":"types.js","sourcesContent":["/* @license Copyright 2024 w3ux authors & contributors\nSPDX-License-Identifier: GPL-3.0-only */\n\nimport { MutableRefObject } from \"react\";\n\nexport interface Props {\n value: number | string;\n wholeColor?: string;\n decimalColor?: string;\n spaceBefore?: string | number;\n spaceAfter?: string | number;\n zeroDecimals?: number;\n}\n\nexport type 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\nexport type DigitRef = MutableRefObject<HTMLSpanElement>;\n\nexport type Status = \"new\" | \"inactive\" | \"transition\" | \"finished\";\n\nexport type Direction = \"down\" | \"none\";\n"]}
1
+ {"version":3,"sources":["../src/types.ts"],"names":[],"mappings":"","file":"types.js","sourcesContent":["/* @license Copyright 2024 w3ux authors & contributors\nSPDX-License-Identifier: GPL-3.0-only */\n\nimport type { RefObject } from 'react'\n\nexport interface Props {\n value: number | string\n wholeColor?: string\n decimalColor?: string\n spaceBefore?: string | number\n spaceAfter?: string | number\n zeroDecimals?: number\n}\n\nexport type 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\nexport type DigitRef = RefObject<HTMLSpanElement>\n\nexport type Status = 'new' | 'inactive' | 'transition' | 'finished'\n\nexport type Direction = 'down' | 'none'\n"]}
package/mjs/index.css CHANGED
@@ -1 +1,221 @@
1
- .odometer{display:flex;align-items:flex-end;position:relative;overflow:hidden;height:inherit;line-height:inherit;width:fit-content}.odometer>.odometer-inner{display:inline-block;vertical-align:bottom}.odometer>.odometer-inner .odometer-digit{display:inline-block;position:relative;vertical-align:bottom;z-index:3}.odometer>.odometer-inner .odometer-digit.odometer-child{display:flex;align-items:flex-end;position:relative;left:0}@keyframes slide-up-1{from{top:0}to{top:100%}}@keyframes slide-up-2{from{top:0}to{top:200%}}@keyframes slide-up-3{from{top:0}to{top:300%}}@keyframes slide-up-4{from{top:0}to{top:400%}}@keyframes slide-up-5{from{top:0}to{top:500%}}@keyframes slide-up-6{from{top:0}to{top:600%}}@keyframes slide-up-7{from{top:0}to{top:700%}}@keyframes slide-up-8{from{top:0}to{top:800%}}@keyframes slide-up-9{from{top:0}to{top:900%}}@keyframes slide-up-10{from{top:0}to{top:1000%}}@keyframes slide-up-11{from{top:0}to{top:1100%}}@keyframes slide-up-12{from{top:0}to{top:1200%}}@keyframes slide-down-1{from{top:0}to{top:-100%}}@keyframes slide-down-2{from{top:0}to{top:-200%}}@keyframes slide-down-3{from{top:0}to{top:-300%}}@keyframes slide-down-4{from{top:0}to{top:-400%}}@keyframes slide-down-5{from{top:0}to{top:-500%}}@keyframes slide-down-6{from{top:0}to{top:-600%}}@keyframes slide-down-7{from{top:0}to{top:-700%}}@keyframes slide-down-8{from{top:0}to{top:-800%}}@keyframes slide-down-9{from{top:0}to{top:-900%}}@keyframes slide-down-10{from{top:0}to{top:-1000%}}@keyframes slide-down-11{from{top:0}to{top:-1100%}}@keyframes slide-down-12{from{top:0}to{top:-1200%}}
1
+ /* @license Copyright 2024 w3ux authors & contributors
2
+ SPDX-License-Identifier: GPL-3.0-only */
3
+ .odometer {
4
+ display: flex;
5
+ align-items: flex-end;
6
+ position: relative;
7
+ overflow: hidden;
8
+ height: inherit;
9
+ line-height: inherit;
10
+ width: fit-content;
11
+ }
12
+ .odometer > .odometer-inner {
13
+ display: inline-block;
14
+ vertical-align: bottom;
15
+ }
16
+ .odometer > .odometer-inner .odometer-digit {
17
+ display: inline-block;
18
+ position: relative;
19
+ vertical-align: bottom;
20
+ z-index: 3;
21
+ }
22
+ .odometer > .odometer-inner .odometer-digit.odometer-child {
23
+ display: flex;
24
+ align-items: flex-end;
25
+ position: relative;
26
+ left: 0;
27
+ }
28
+
29
+ /* NOTE: Stylelint does not like inline scss variables to generate these. */
30
+ @keyframes slide-up-1 {
31
+ from {
32
+ top: 0;
33
+ }
34
+ to {
35
+ top: 100%;
36
+ }
37
+ }
38
+ @keyframes slide-up-2 {
39
+ from {
40
+ top: 0;
41
+ }
42
+ to {
43
+ top: 200%;
44
+ }
45
+ }
46
+ @keyframes slide-up-3 {
47
+ from {
48
+ top: 0;
49
+ }
50
+ to {
51
+ top: 300%;
52
+ }
53
+ }
54
+ @keyframes slide-up-4 {
55
+ from {
56
+ top: 0;
57
+ }
58
+ to {
59
+ top: 400%;
60
+ }
61
+ }
62
+ @keyframes slide-up-5 {
63
+ from {
64
+ top: 0;
65
+ }
66
+ to {
67
+ top: 500%;
68
+ }
69
+ }
70
+ @keyframes slide-up-6 {
71
+ from {
72
+ top: 0;
73
+ }
74
+ to {
75
+ top: 600%;
76
+ }
77
+ }
78
+ @keyframes slide-up-7 {
79
+ from {
80
+ top: 0;
81
+ }
82
+ to {
83
+ top: 700%;
84
+ }
85
+ }
86
+ @keyframes slide-up-8 {
87
+ from {
88
+ top: 0;
89
+ }
90
+ to {
91
+ top: 800%;
92
+ }
93
+ }
94
+ @keyframes slide-up-9 {
95
+ from {
96
+ top: 0;
97
+ }
98
+ to {
99
+ top: 900%;
100
+ }
101
+ }
102
+ @keyframes slide-up-10 {
103
+ from {
104
+ top: 0;
105
+ }
106
+ to {
107
+ top: 1000%;
108
+ }
109
+ }
110
+ @keyframes slide-up-11 {
111
+ from {
112
+ top: 0;
113
+ }
114
+ to {
115
+ top: 1100%;
116
+ }
117
+ }
118
+ @keyframes slide-up-12 {
119
+ from {
120
+ top: 0;
121
+ }
122
+ to {
123
+ top: 1200%;
124
+ }
125
+ }
126
+ @keyframes slide-down-1 {
127
+ from {
128
+ top: 0;
129
+ }
130
+ to {
131
+ top: -100%;
132
+ }
133
+ }
134
+ @keyframes slide-down-2 {
135
+ from {
136
+ top: 0;
137
+ }
138
+ to {
139
+ top: -200%;
140
+ }
141
+ }
142
+ @keyframes slide-down-3 {
143
+ from {
144
+ top: 0;
145
+ }
146
+ to {
147
+ top: -300%;
148
+ }
149
+ }
150
+ @keyframes slide-down-4 {
151
+ from {
152
+ top: 0;
153
+ }
154
+ to {
155
+ top: -400%;
156
+ }
157
+ }
158
+ @keyframes slide-down-5 {
159
+ from {
160
+ top: 0;
161
+ }
162
+ to {
163
+ top: -500%;
164
+ }
165
+ }
166
+ @keyframes slide-down-6 {
167
+ from {
168
+ top: 0;
169
+ }
170
+ to {
171
+ top: -600%;
172
+ }
173
+ }
174
+ @keyframes slide-down-7 {
175
+ from {
176
+ top: 0;
177
+ }
178
+ to {
179
+ top: -700%;
180
+ }
181
+ }
182
+ @keyframes slide-down-8 {
183
+ from {
184
+ top: 0;
185
+ }
186
+ to {
187
+ top: -800%;
188
+ }
189
+ }
190
+ @keyframes slide-down-9 {
191
+ from {
192
+ top: 0;
193
+ }
194
+ to {
195
+ top: -900%;
196
+ }
197
+ }
198
+ @keyframes slide-down-10 {
199
+ from {
200
+ top: 0;
201
+ }
202
+ to {
203
+ top: -1000%;
204
+ }
205
+ }
206
+ @keyframes slide-down-11 {
207
+ from {
208
+ top: 0;
209
+ }
210
+ to {
211
+ top: -1100%;
212
+ }
213
+ }
214
+ @keyframes slide-down-12 {
215
+ from {
216
+ top: 0;
217
+ }
218
+ to {
219
+ top: -1200%;
220
+ }
221
+ }
package/mjs/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Props } from "./types";
2
- import "./index.css";
1
+ import './index.css';
2
+ import type { Props } from './types';
3
3
  export declare const Odometer: ({ value, spaceBefore, spaceAfter, wholeColor, decimalColor, zeroDecimals, }: Props) => import("react/jsx-runtime").JSX.Element;
4
4
  export default Odometer;
package/mjs/index.js CHANGED
@@ -1,24 +1,24 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useEffect, useState, createRef, useRef } from "react";
3
- import "./index.css";
4
- export const Odometer = ({ value, spaceBefore = 0, spaceAfter = "0.25rem", wholeColor = "var(--text-color-primary)", decimalColor = "var(--text-color-secondary)", zeroDecimals = 0, }) => {
2
+ import { createRef, useEffect, useRef, useState } from 'react';
3
+ import './index.css';
4
+ export const Odometer = ({ value, spaceBefore = 0, spaceAfter = '0.25rem', wholeColor = 'var(--text-color-primary)', decimalColor = 'var(--text-color-secondary)', zeroDecimals = 0, }) => {
5
5
  const [allDigits] = useState([
6
- "comma",
7
- "dot",
8
- "0",
9
- "1",
10
- "2",
11
- "3",
12
- "4",
13
- "5",
14
- "6",
15
- "7",
16
- "8",
17
- "9",
6
+ 'comma',
7
+ 'dot',
8
+ '0',
9
+ '1',
10
+ '2',
11
+ '3',
12
+ '4',
13
+ '5',
14
+ '6',
15
+ '7',
16
+ '8',
17
+ '9',
18
18
  ]);
19
19
  const [digits, setDigits] = useState([]);
20
20
  const [prevDigits, setPrevDigits] = useState([]);
21
- const [status, setStatus] = useState("inactive");
21
+ const [status, setStatus] = useState('inactive');
22
22
  const [initialized, setInitialized] = useState(false);
23
23
  const [odometerRef] = useState(createRef());
24
24
  const [digitRefs, setDigitRefs] = useState([]);
@@ -33,31 +33,31 @@ export const Odometer = ({ value, spaceBefore = 0, spaceAfter = "0.25rem", whole
33
33
  useEffect(() => {
34
34
  if (Object.keys(allDigitRefs)) {
35
35
  value =
36
- String(value) === "0" ? Number(value).toFixed(zeroDecimals) : value;
36
+ String(value) === '0' ? Number(value).toFixed(zeroDecimals) : value;
37
37
  const newDigits = value
38
38
  .toString()
39
- .split("")
40
- .map((v) => (v === "." ? "dot" : v))
41
- .map((v) => (v === "," ? "comma" : v));
39
+ .split('')
40
+ .map((v) => (v === '.' ? 'dot' : v))
41
+ .map((v) => (v === ',' ? 'comma' : v));
42
42
  setDigits(newDigits);
43
43
  if (!initialized) {
44
44
  setInitialized(true);
45
45
  }
46
46
  else {
47
- setStatus("new");
47
+ setStatus('new');
48
48
  setPrevDigits(digits);
49
49
  }
50
50
  setDigitRefs(Array(newDigits.length).fill(createRef()));
51
51
  }
52
52
  }, [value]);
53
53
  useEffect(() => {
54
- if (status === "new" && !digitRefs.find((d) => d.current === null)) {
55
- setStatus("transition");
54
+ if (status === 'new' && !digitRefs.find((d) => d.current === null)) {
55
+ setStatus('transition');
56
56
  activeTransitionCounter.current++;
57
57
  setTimeout(() => {
58
58
  activeTransitionCounter.current--;
59
59
  if (activeTransitionCounter.current === 0) {
60
- setStatus("inactive");
60
+ setStatus('inactive');
61
61
  }
62
62
  }, DURATION_MS);
63
63
  }
@@ -65,27 +65,27 @@ export const Odometer = ({ value, spaceBefore = 0, spaceAfter = "0.25rem", whole
65
65
  const odometerCurrent = odometerRef?.current;
66
66
  let lineHeight = odometerCurrent
67
67
  ? window.getComputedStyle(odometerCurrent).lineHeight
68
- : "inherit";
69
- lineHeight = lineHeight === "normal" ? "1.1rem" : lineHeight;
68
+ : 'inherit';
69
+ lineHeight = lineHeight === 'normal' ? '1.1rem' : lineHeight;
70
70
  let foundDecimal = false;
71
71
  return (_jsxs(_Fragment, { children: [allDigits.map((d, i) => (_jsx("span", { ref: allDigitRefs[`d_${d}`], style: {
72
72
  opacity: 0,
73
- position: "fixed",
74
- top: "-999%",
75
- left: "-999%",
76
- userSelect: "none",
77
- }, children: d === "dot" ? "." : d === "comma" ? "," : d }, `odometer_template_digit_${i}`))), _jsx("span", { className: "odometer", children: _jsxs("span", { className: "odometer-inner", ref: odometerRef, children: [spaceBefore ? _jsx("span", { style: { paddingLeft: spaceBefore } }) : null, digits.map((d, i) => {
78
- if (d === "dot") {
73
+ position: 'fixed',
74
+ top: '-999%',
75
+ left: '-999%',
76
+ userSelect: 'none',
77
+ }, children: d === 'dot' ? '.' : d === 'comma' ? ',' : d }, `odometer_template_digit_${i}`))), _jsx("span", { className: "odometer", children: _jsxs("span", { className: "odometer-inner", ref: odometerRef, children: [spaceBefore ? _jsx("span", { style: { paddingLeft: spaceBefore } }) : null, digits.map((d, i) => {
78
+ if (d === 'dot') {
79
79
  foundDecimal = true;
80
80
  }
81
81
  let childDigits = null;
82
- if (status === "transition") {
82
+ if (status === 'transition') {
83
83
  const digitsToAnimate = [];
84
84
  const digitIndex = allDigits.indexOf(digits[i]);
85
85
  const prevDigitIndex = allDigits.indexOf(prevDigits[i]);
86
86
  const difference = Math.abs(digitIndex - prevDigitIndex);
87
87
  const delay = `${0.01 * (digits.length - i - 1)}s`;
88
- const direction = digitIndex === prevDigitIndex ? "none" : "down";
88
+ const direction = digitIndex === prevDigitIndex ? 'none' : 'down';
89
89
  const animClass = `slide-${direction}-${difference} `;
90
90
  digitsToAnimate.push(prevDigits[i]);
91
91
  if (digitIndex < prevDigitIndex) {
@@ -95,35 +95,35 @@ export const Odometer = ({ value, spaceBefore = 0, spaceAfter = "0.25rem", whole
95
95
  digitsToAnimate.push(...Array.from({ length: difference }, (_, k) => allDigits[k + prevDigitIndex + 1]));
96
96
  }
97
97
  childDigits = (_jsx("span", { style: {
98
- position: "absolute",
98
+ position: 'absolute',
99
99
  top: 0,
100
100
  left: 0,
101
- animationName: direction === "none" ? undefined : animClass,
102
- animationDuration: direction === "none" ? undefined : DURATION_SECS,
103
- animationFillMode: "forwards",
104
- animationTimingFunction: "cubic-bezier(0.1, 1, 0.2, 1)",
101
+ animationName: direction === 'none' ? undefined : animClass,
102
+ animationDuration: direction === 'none' ? undefined : DURATION_SECS,
103
+ animationFillMode: 'forwards',
104
+ animationTimingFunction: 'cubic-bezier(0.1, 1, 0.2, 1)',
105
105
  animationDelay: delay,
106
106
  color: foundDecimal ? decimalColor : wholeColor,
107
- userSelect: "none",
107
+ userSelect: 'none',
108
108
  }, children: digitsToAnimate.map((c, j) => (_jsx("span", { className: "odometer-digit odometer-child", style: {
109
109
  top: j === 0 ? 0 : `${100 * j}%`,
110
110
  height: lineHeight,
111
111
  lineHeight,
112
- }, children: c === "dot" ? "." : c === "comma" ? "," : c }, `child_digit_${j}`))) }));
112
+ }, children: c === 'dot' ? '.' : c === 'comma' ? ',' : c }, `child_digit_${j}`))) }));
113
113
  }
114
114
  return (_jsxs("span", { ref: digitRefs[i], className: "odometer-digit", style: {
115
115
  color: foundDecimal ? decimalColor : wholeColor,
116
116
  height: lineHeight,
117
117
  lineHeight,
118
- paddingRight: status === "transition"
118
+ paddingRight: status === 'transition'
119
119
  ? `${allDigitRefs[`d_${d}`]?.current?.offsetWidth}px`
120
- : "0",
121
- }, children: [status === "inactive" && (_jsx("span", { className: "odometer-digit odometer-child", style: {
120
+ : '0',
121
+ }, children: [status === 'inactive' && (_jsx("span", { className: "odometer-digit odometer-child", style: {
122
122
  top: 0,
123
123
  height: lineHeight,
124
124
  lineHeight,
125
125
  width: `${allDigitRefs[`d_${d}`]?.current?.offsetWidth}px`,
126
- }, children: d === "dot" ? "." : d === "comma" ? "," : d })), status === "transition" && childDigits] }, `digit_${i}`));
126
+ }, children: d === 'dot' ? '.' : d === 'comma' ? ',' : d })), status === 'transition' && childDigits] }, `digit_${i}`));
127
127
  }), spaceAfter ? _jsx("span", { style: { paddingRight: spaceAfter } }) : null] }) })] }));
128
128
  };
129
129
  export default Odometer;
package/mjs/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx"],"names":[],"mappings":";AAGA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAE/D,OAAO,aAAa,CAAC;AAErB,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,EACvB,KAAK,EACL,WAAW,GAAG,CAAC,EACf,UAAU,GAAG,SAAS,EACtB,UAAU,GAAG,2BAA2B,EACxC,YAAY,GAAG,6BAA6B,EAC5C,YAAY,GAAG,CAAC,GACV,EAAE,EAAE;IAEV,MAAM,CAAC,SAAS,CAAC,GAAG,QAAQ,CAAU;QACpC,OAAO;QACP,KAAK;QACL,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;KACJ,CAAC,CAAC;IAGH,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAU,EAAE,CAAC,CAAC;IAGlD,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAU,EAAE,CAAC,CAAC;IAG1D,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAS,UAAU,CAAC,CAAC;IAGzD,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC,CAAC;IAG/D,MAAM,CAAC,WAAW,CAAC,GAAG,QAAQ,CAAC,SAAS,EAAmB,CAAC,CAAC;IAG7D,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAa,EAAE,CAAC,CAAC;IAG3D,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAC9C,EAAE,CACH,CAAC;IAGF,MAAM,uBAAuB,GAAG,MAAM,CAAS,CAAC,CAAC,CAAC;IAGlD,MAAM,WAAW,GAAG,GAAG,CAAC;IACxB,MAAM,aAAa,GAAG,GAAG,WAAW,GAAG,IAAI,GAAG,CAAC;IAG/C,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,GAAG,GAA6B,MAAM,CAAC,WAAW,CACtD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,CAC7D,CAAC;QAEF,eAAe,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC,EAAE,EAAE,CAAC,CAAC;IAGP,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;YAC9B,KAAK;gBACH,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YAEtE,MAAM,SAAS,GAAG,KAAK;iBACpB,QAAQ,EAAE;iBACV,KAAK,CAAC,EAAE,CAAC;iBACT,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;iBACnC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAY,CAAC;YAEpD,SAAS,CAAC,SAAS,CAAC,CAAC;YAErB,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,cAAc,CAAC,IAAI,CAAC,CAAC;YACvB,CAAC;iBAAM,CAAC;gBACN,SAAS,CAAC,KAAK,CAAC,CAAC;gBACjB,aAAa,CAAC,MAAM,CAAC,CAAC;YACxB,CAAC;YACD,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAGZ,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,KAAK,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,EAAE,CAAC;YACnE,SAAS,CAAC,YAAY,CAAC,CAAC;YACxB,uBAAuB,CAAC,OAAO,EAAE,CAAC;YAElC,UAAU,CAAC,GAAG,EAAE;gBACd,uBAAuB,CAAC,OAAO,EAAE,CAAC;gBAClC,IAAI,uBAAuB,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;oBAC1C,SAAS,CAAC,UAAU,CAAC,CAAC;gBACxB,CAAC;YACH,CAAC,EAAE,WAAW,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IAExB,MAAM,eAAe,GAAY,WAAW,EAAE,OAAO,CAAC;IACtD,IAAI,UAAU,GAAG,eAAe;QAC9B,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAC,UAAU;QACrD,CAAC,CAAC,SAAS,CAAC;IAGd,UAAU,GAAG,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC;IAG7D,IAAI,YAAY,GAAG,KAAK,CAAC;IAEzB,OAAO,CACL,8BACG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CACvB,eAEE,GAAG,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,EAC3B,KAAK,EAAE;oBACL,OAAO,EAAE,CAAC;oBACV,QAAQ,EAAE,OAAO;oBACjB,GAAG,EAAE,OAAO;oBACZ,IAAI,EAAE,OAAO;oBACb,UAAU,EAAE,MAAM;iBACnB,YAEA,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAVvC,2BAA2B,CAAC,EAAE,CAW9B,CACR,CAAC,EACF,eAAM,SAAS,EAAC,UAAU,YACxB,gBAAM,SAAS,EAAC,gBAAgB,EAAC,GAAG,EAAE,WAAW,aAC9C,WAAW,CAAC,CAAC,CAAC,eAAM,KAAK,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,GAAI,CAAC,CAAC,CAAC,IAAI,EAClE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;4BACnB,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC;gCAChB,YAAY,GAAG,IAAI,CAAC;4BACtB,CAAC;4BAGD,IAAI,WAAW,GAAG,IAAI,CAAC;4BACvB,IAAI,MAAM,KAAK,YAAY,EAAE,CAAC;gCAC5B,MAAM,eAAe,GAAG,EAAE,CAAC;gCAC3B,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;gCAChD,MAAM,cAAc,GAAG,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;gCACxD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,cAAc,CAAC,CAAC;gCACzD,MAAM,KAAK,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;gCACnD,MAAM,SAAS,GACb,UAAU,KAAK,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;gCAClD,MAAM,SAAS,GAAG,SAAS,SAAS,IAAI,UAAU,GAAG,CAAC;gCAGtD,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;gCAGpC,IAAI,UAAU,GAAG,cAAc,EAAE,CAAC;oCAChC,eAAe,CAAC,IAAI,CAClB,GAAG,KAAK,CAAC,IAAI,CACX,EAAE,MAAM,EAAE,UAAU,EAAE,EACtB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,cAAc,GAAG,CAAC,GAAG,CAAC,CAAC,CAC5C,CACF,CAAC;gCACJ,CAAC;qCAAM,CAAC;oCACN,eAAe,CAAC,IAAI,CAClB,GAAG,KAAK,CAAC,IAAI,CACX,EAAE,MAAM,EAAE,UAAU,EAAE,EACtB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,GAAG,cAAc,GAAG,CAAC,CAAC,CAC5C,CACF,CAAC;gCACJ,CAAC;gCAED,WAAW,GAAG,CACZ,eACE,KAAK,EAAE;wCACL,QAAQ,EAAE,UAAU;wCACpB,GAAG,EAAE,CAAC;wCACN,IAAI,EAAE,CAAC;wCACP,aAAa,EAAE,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;wCAC3D,iBAAiB,EACf,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa;wCAClD,iBAAiB,EAAE,UAAU;wCAC7B,uBAAuB,EAAE,8BAA8B;wCACvD,cAAc,EAAE,KAAK;wCACrB,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU;wCAC/C,UAAU,EAAE,MAAM;qCACnB,YAEA,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAC7B,eAEE,SAAS,EAAC,+BAA+B,EACzC,KAAK,EAAE;4CACL,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG;4CAChC,MAAM,EAAE,UAAU;4CAClB,UAAU;yCACX,YAEA,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IARvC,eAAe,CAAC,EAAE,CASlB,CACR,CAAC,GACG,CACR,CAAC;4BACJ,CAAC;4BAED,OAAO,CACL,gBAEE,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,EACjB,SAAS,EAAC,gBAAgB,EAC1B,KAAK,EAAE;oCACL,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU;oCAC/C,MAAM,EAAE,UAAU;oCAClB,UAAU;oCACV,YAAY,EACV,MAAM,KAAK,YAAY;wCACrB,CAAC,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,WAAW,IAAI;wCACrD,CAAC,CAAC,GAAG;iCACV,aAEA,MAAM,KAAK,UAAU,IAAI,CACxB,eACE,SAAS,EAAC,+BAA+B,EACzC,KAAK,EAAE;4CACL,GAAG,EAAE,CAAC;4CACN,MAAM,EAAE,UAAU;4CAClB,UAAU;4CACV,KAAK,EAAE,GACL,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,WACnC,IAAI;yCACL,YAEA,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GACvC,CACR,EACA,MAAM,KAAK,YAAY,IAAI,WAAW,KA5BlC,SAAS,CAAC,EAAE,CA6BZ,CACR,CAAC;wBACJ,CAAC,CAAC,EACD,UAAU,CAAC,CAAC,CAAC,eAAM,KAAK,EAAE,EAAE,YAAY,EAAE,UAAU,EAAE,GAAI,CAAC,CAAC,CAAC,IAAI,IAC7D,GACF,IACN,CACJ,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,QAAQ,CAAC","file":"index.js","sourcesContent":["/* @license Copyright 2024 w3ux authors & contributors\nSPDX-License-Identifier: GPL-3.0-only */\n\nimport { useEffect, useState, createRef, useRef } from \"react\";\nimport { Digit, DigitRef, Direction, Props, Status } from \"./types\";\nimport \"./index.css\";\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 );\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 { 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"]}
package/mjs/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { MutableRefObject } from "react";
1
+ import type { RefObject } from 'react';
2
2
  export interface Props {
3
3
  value: number | string;
4
4
  wholeColor?: string;
@@ -7,7 +7,7 @@ export interface Props {
7
7
  spaceAfter?: string | number;
8
8
  zeroDecimals?: number;
9
9
  }
10
- export type Digit = "comma" | "dot" | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
11
- export type DigitRef = MutableRefObject<HTMLSpanElement>;
12
- export type Status = "new" | "inactive" | "transition" | "finished";
13
- export type Direction = "down" | "none";
10
+ export type Digit = 'comma' | 'dot' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
11
+ export type DigitRef = RefObject<HTMLSpanElement>;
12
+ export type Status = 'new' | 'inactive' | 'transition' | 'finished';
13
+ export type Direction = 'down' | 'none';
package/mjs/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"names":[],"mappings":"","file":"types.js","sourcesContent":["/* @license Copyright 2024 w3ux authors & contributors\nSPDX-License-Identifier: GPL-3.0-only */\n\nimport { MutableRefObject } from \"react\";\n\nexport interface Props {\n value: number | string;\n wholeColor?: string;\n decimalColor?: string;\n spaceBefore?: string | number;\n spaceAfter?: string | number;\n zeroDecimals?: number;\n}\n\nexport type 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\nexport type DigitRef = MutableRefObject<HTMLSpanElement>;\n\nexport type Status = \"new\" | \"inactive\" | \"transition\" | \"finished\";\n\nexport type Direction = \"down\" | \"none\";\n"]}
1
+ {"version":3,"sources":["../src/types.ts"],"names":[],"mappings":"","file":"types.js","sourcesContent":["/* @license Copyright 2024 w3ux authors & contributors\nSPDX-License-Identifier: GPL-3.0-only */\n\nimport type { RefObject } from 'react'\n\nexport interface Props {\n value: number | string\n wholeColor?: string\n decimalColor?: string\n spaceBefore?: string | number\n spaceAfter?: string | number\n zeroDecimals?: number\n}\n\nexport type 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\nexport type DigitRef = RefObject<HTMLSpanElement>\n\nexport type Status = 'new' | 'inactive' | 'transition' | 'finished'\n\nexport type Direction = 'down' | 'none'\n"]}
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@w3ux/react-odometer",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "license": "GPL-3.0-only",
5
- "main": "cjs/index.js",
6
- "module": "mjs/index.js",
5
+ "type": "module",
7
6
  "exports": {
8
7
  ".": {
9
8
  "import": "./mjs/index.js",