@ledgerhq/lumen-utils-shared 0.0.19 → 0.0.20
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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +115 -92
- package/dist/lib/components/getStepperCalculations/getStepperCalculations.d.ts +61 -0
- package/dist/lib/components/getStepperCalculations/getStepperCalculations.d.ts.map +1 -0
- package/dist/lib/components/getStepperCalculations/index.d.ts +2 -0
- package/dist/lib/components/getStepperCalculations/index.d.ts.map +1 -0
- package/dist/lib/components/index.d.ts +2 -0
- package/dist/lib/components/index.d.ts.map +1 -0
- package/dist/lib/getStepperCalculations/getStepperCalculations.d.ts +61 -0
- package/dist/lib/getStepperCalculations/getStepperCalculations.d.ts.map +1 -0
- package/dist/lib/getStepperCalculations/index.d.ts +2 -0
- package/dist/lib/getStepperCalculations/index.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/lib/components/getStepperCalculations/getStepperCalculations.test.ts +63 -0
- package/src/lib/components/getStepperCalculations/getStepperCalculations.ts +104 -0
- package/src/lib/components/getStepperCalculations/index.ts +5 -0
- package/src/lib/components/index.ts +1 -0
- package/src/lib/getStepperCalculations/getStepperCalculations.test.ts +139 -0
- package/src/lib/getStepperCalculations/getStepperCalculations.ts +103 -0
- package/src/lib/getStepperCalculations/index.ts +5 -0
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from './lib/cn';
|
|
|
5
5
|
export * from './lib/isTextChildren';
|
|
6
6
|
export * from './lib/useMergeRef';
|
|
7
7
|
export * from './lib/getObjectPath';
|
|
8
|
+
export * from './lib/components';
|
|
8
9
|
export * from './lib/throttle';
|
|
9
10
|
export * from './lib/debounce';
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,160 +1,183 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { createContext as
|
|
1
|
+
import { jsx as k } from "react/jsx-runtime";
|
|
2
|
+
import { createContext as W, useMemo as v, use as F } from "react";
|
|
3
3
|
import { clsx as _ } from "clsx";
|
|
4
4
|
import { twMerge as j } from "tailwind-merge";
|
|
5
5
|
function q(t) {
|
|
6
6
|
return t ? t.split(/[-_ ]+/).map((n) => n.charAt(0).toUpperCase() + n.slice(1).toLowerCase()).join("") : "";
|
|
7
7
|
}
|
|
8
|
-
const
|
|
8
|
+
const N = 48, R = 17, w = 2;
|
|
9
9
|
function G(t) {
|
|
10
10
|
const n = t.replace(/\D/g, "").length;
|
|
11
11
|
return Math.max(
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
R,
|
|
13
|
+
N - n * w
|
|
14
14
|
);
|
|
15
15
|
}
|
|
16
|
-
function
|
|
16
|
+
function C(t, n = !0) {
|
|
17
17
|
if (!t) return "";
|
|
18
|
-
const
|
|
19
|
-
return
|
|
18
|
+
const i = t.includes("."), [l, o] = t.split("."), u = l.replace(/\B(?=(\d{3})+(?!\d))/g, " ");
|
|
19
|
+
return i && n ? o ? `${u}.${o}` : `${u}.` : u;
|
|
20
20
|
}
|
|
21
21
|
function H(t, n = {}) {
|
|
22
22
|
const {
|
|
23
|
-
allowDecimals:
|
|
24
|
-
thousandsSeparator:
|
|
25
|
-
maxIntegerLength:
|
|
26
|
-
maxDecimalLength:
|
|
27
|
-
} = n,
|
|
28
|
-
let e =
|
|
29
|
-
if (e = e.replace(/^0+(?=\d)/, ""), !
|
|
30
|
-
return e = e.replace(/\D/g, ""),
|
|
23
|
+
allowDecimals: i = !0,
|
|
24
|
+
thousandsSeparator: l = !0,
|
|
25
|
+
maxIntegerLength: o = 9,
|
|
26
|
+
maxDecimalLength: u = 9
|
|
27
|
+
} = n, r = t.replace(",", ".").replace(/[^\d.]/g, "");
|
|
28
|
+
let e = r;
|
|
29
|
+
if (e = e.replace(/^0+(?=\d)/, ""), !i)
|
|
30
|
+
return e = e.replace(/\D/g, ""), o > 0 && e.length > o && (e = e.slice(0, o)), l ? C(e) : e;
|
|
31
31
|
e === "." && (e = "0.");
|
|
32
|
-
const
|
|
33
|
-
if (
|
|
34
|
-
let
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
e =
|
|
32
|
+
const a = e.indexOf(".");
|
|
33
|
+
if (a !== -1) {
|
|
34
|
+
let c = e.slice(0, a), f = e.slice(a + 1).replace(/\./g, "");
|
|
35
|
+
o > 0 && c.length > o && (c = c.slice(0, o)), f = f.slice(0, u), c === "" && (c = "0");
|
|
36
|
+
const p = r.endsWith(".") || e.endsWith(".");
|
|
37
|
+
e = f.length > 0 ? `${c}.${f}` : p ? `${c}.` : c;
|
|
38
38
|
} else
|
|
39
|
-
|
|
40
|
-
return
|
|
39
|
+
o > 0 && e.length > o && (e = e.slice(0, o));
|
|
40
|
+
return l ? C(e) : e;
|
|
41
41
|
}
|
|
42
42
|
function J(t, n) {
|
|
43
|
-
const
|
|
44
|
-
const e =
|
|
45
|
-
() =>
|
|
46
|
-
Object.values(
|
|
43
|
+
const i = W(n), l = ({ children: u, value: r }) => {
|
|
44
|
+
const e = v(
|
|
45
|
+
() => r,
|
|
46
|
+
Object.values(r ?? {})
|
|
47
47
|
);
|
|
48
|
-
return /* @__PURE__ */
|
|
48
|
+
return /* @__PURE__ */ k(i, { value: e, children: u });
|
|
49
49
|
};
|
|
50
|
-
|
|
51
|
-
function
|
|
52
|
-
consumerName:
|
|
53
|
-
contextRequired:
|
|
50
|
+
l.displayName = t + "Provider";
|
|
51
|
+
function o({
|
|
52
|
+
consumerName: u,
|
|
53
|
+
contextRequired: r
|
|
54
54
|
}) {
|
|
55
|
-
const e = F(
|
|
55
|
+
const e = F(i);
|
|
56
56
|
if (e)
|
|
57
57
|
return e;
|
|
58
|
-
if (
|
|
58
|
+
if (r)
|
|
59
59
|
throw new Error(
|
|
60
|
-
`${
|
|
60
|
+
`${u} must be used within ${t}`
|
|
61
61
|
);
|
|
62
62
|
return n || {};
|
|
63
63
|
}
|
|
64
|
-
return [
|
|
64
|
+
return [l, o];
|
|
65
65
|
}
|
|
66
66
|
function K(...t) {
|
|
67
67
|
return j(_(t));
|
|
68
68
|
}
|
|
69
69
|
const I = (t) => typeof t == "string" || typeof t == "number", Q = (t) => Array.isArray(t) ? t.every(I) : I(t);
|
|
70
|
-
function
|
|
70
|
+
function S(t, n) {
|
|
71
71
|
if (typeof t == "function")
|
|
72
72
|
return t(n);
|
|
73
73
|
typeof t == "object" && t !== null && "current" in t && (t.current = n);
|
|
74
74
|
}
|
|
75
|
-
function
|
|
75
|
+
function z(...t) {
|
|
76
76
|
const n = /* @__PURE__ */ new Map();
|
|
77
|
-
return (
|
|
78
|
-
if (
|
|
79
|
-
t.forEach((
|
|
80
|
-
const
|
|
81
|
-
typeof
|
|
77
|
+
return (i) => {
|
|
78
|
+
if (i === null) {
|
|
79
|
+
t.forEach((l) => {
|
|
80
|
+
const o = n.get(l);
|
|
81
|
+
typeof o == "function" && o(), S(l, null);
|
|
82
82
|
}), n.clear();
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
|
-
t.forEach((
|
|
86
|
-
const
|
|
87
|
-
typeof
|
|
85
|
+
t.forEach((l) => {
|
|
86
|
+
const o = S(l, i);
|
|
87
|
+
typeof o == "function" && n.set(l, o);
|
|
88
88
|
});
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
91
|
function Y(...t) {
|
|
92
|
-
return
|
|
92
|
+
return v(() => z(...t), t);
|
|
93
93
|
}
|
|
94
94
|
const tt = (t, n) => {
|
|
95
|
-
let
|
|
96
|
-
for (const
|
|
97
|
-
if (
|
|
95
|
+
let i = t;
|
|
96
|
+
for (const l of n) {
|
|
97
|
+
if (i == null)
|
|
98
98
|
return;
|
|
99
|
-
|
|
99
|
+
i = i[l];
|
|
100
100
|
}
|
|
101
|
-
return
|
|
101
|
+
return i;
|
|
102
|
+
}, nt = ({
|
|
103
|
+
currentStep: t,
|
|
104
|
+
totalSteps: n,
|
|
105
|
+
size: i,
|
|
106
|
+
label: l,
|
|
107
|
+
strokeWidth: o = 4,
|
|
108
|
+
arcPercentage: u = 0.75
|
|
109
|
+
}) => {
|
|
110
|
+
const r = Math.min(Math.max(t, 0), n), e = l ?? `${r}/${n}`, a = n <= 0 ? 0 : r / n, c = (i - o) / 2, f = i / 2, p = i / 2, m = 2 * Math.PI * c, d = m * u, y = d * (1 - a), h = r <= 0, $ = `${d} ${m}`, D = h ? d - 2 : y;
|
|
111
|
+
return {
|
|
112
|
+
displayLabel: e,
|
|
113
|
+
progress: a,
|
|
114
|
+
r: c,
|
|
115
|
+
cx: f,
|
|
116
|
+
cy: p,
|
|
117
|
+
circumference: m,
|
|
118
|
+
trackArcLength: d,
|
|
119
|
+
trackDashArray: `${d} ${m}`,
|
|
120
|
+
progressDashArray: $,
|
|
121
|
+
progressDashOffset: D,
|
|
122
|
+
showMinimalDot: h
|
|
123
|
+
};
|
|
102
124
|
};
|
|
103
|
-
function V(t, n,
|
|
104
|
-
const { leading:
|
|
105
|
-
let
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
return
|
|
110
|
-
},
|
|
111
|
-
const
|
|
112
|
-
return n -
|
|
113
|
-
},
|
|
114
|
-
const
|
|
115
|
-
return
|
|
116
|
-
},
|
|
117
|
-
const
|
|
118
|
-
if (
|
|
119
|
-
|
|
125
|
+
function V(t, n, i = {}) {
|
|
126
|
+
const { leading: l = !1, trailing: o = !0, maxWait: u } = i;
|
|
127
|
+
let r = null, e = null, a = null, c = 0, f = null, p = null, m;
|
|
128
|
+
const d = u !== void 0, y = d ? Math.max(u, n) : 0, h = (s) => {
|
|
129
|
+
c = s;
|
|
130
|
+
const g = f, T = p;
|
|
131
|
+
return f = null, p = null, m = t.apply(T, g), m;
|
|
132
|
+
}, $ = (s) => {
|
|
133
|
+
const g = a ? s - a : 0;
|
|
134
|
+
return n - g;
|
|
135
|
+
}, D = (s) => {
|
|
136
|
+
const g = a ? s - a : n, T = s - c;
|
|
137
|
+
return a === null || g >= n || g < 0 || d && T >= y;
|
|
138
|
+
}, A = (s) => (r = null, o && f ? h(s) : (f = null, p = null, m)), x = () => {
|
|
139
|
+
const s = Date.now();
|
|
140
|
+
if (D(s)) {
|
|
141
|
+
A(s);
|
|
120
142
|
return;
|
|
121
143
|
}
|
|
122
|
-
|
|
123
|
-
},
|
|
124
|
-
const
|
|
125
|
-
e = null,
|
|
126
|
-
},
|
|
127
|
-
|
|
128
|
-
}, L = () =>
|
|
129
|
-
const
|
|
130
|
-
if (
|
|
131
|
-
if (
|
|
132
|
-
return
|
|
133
|
-
if (
|
|
134
|
-
return clearTimeout(
|
|
144
|
+
r = setTimeout(x, $(s));
|
|
145
|
+
}, E = () => {
|
|
146
|
+
const s = Date.now();
|
|
147
|
+
e = null, f && (h(s), r !== null && (clearTimeout(r), r = setTimeout(x, n)));
|
|
148
|
+
}, P = (s) => (c = s, r = setTimeout(x, n), d && (e = setTimeout(E, y)), l ? h(s) : m), b = () => {
|
|
149
|
+
r !== null && clearTimeout(r), e !== null && clearTimeout(e), c = 0, f = null, p = null, a = null, r = null, e = null;
|
|
150
|
+
}, L = () => r === null && e === null ? m : A(Date.now()), O = () => r !== null || e !== null, M = function(...s) {
|
|
151
|
+
const g = Date.now(), T = D(g);
|
|
152
|
+
if (f = s, p = this, a = g, T) {
|
|
153
|
+
if (r === null)
|
|
154
|
+
return P(g);
|
|
155
|
+
if (d)
|
|
156
|
+
return clearTimeout(r), r = setTimeout(x, n), h(g);
|
|
135
157
|
}
|
|
136
|
-
return
|
|
158
|
+
return r === null && (r = setTimeout(x, n)), m;
|
|
137
159
|
};
|
|
138
|
-
return
|
|
160
|
+
return M.cancel = b, M.flush = L, M.pending = O, M;
|
|
139
161
|
}
|
|
140
|
-
function et(t, n,
|
|
141
|
-
const { leading:
|
|
162
|
+
function et(t, n, i = {}) {
|
|
163
|
+
const { leading: l = !0, trailing: o = !0 } = i;
|
|
142
164
|
return V(t, n, {
|
|
143
|
-
leading:
|
|
144
|
-
trailing:
|
|
165
|
+
leading: l,
|
|
166
|
+
trailing: o,
|
|
145
167
|
maxWait: n
|
|
146
168
|
});
|
|
147
169
|
}
|
|
148
170
|
export {
|
|
149
|
-
|
|
171
|
+
S as assignRef,
|
|
150
172
|
K as cn,
|
|
151
173
|
J as createSafeContext,
|
|
152
174
|
V as debounce,
|
|
153
|
-
|
|
175
|
+
C as formatThousands,
|
|
154
176
|
G as getFontSize,
|
|
155
177
|
tt as getObjectPath,
|
|
178
|
+
nt as getStepperCalculations,
|
|
156
179
|
Q as isTextChildren,
|
|
157
|
-
|
|
180
|
+
z as mergeRefs,
|
|
158
181
|
H as textFormatter,
|
|
159
182
|
et as throttle,
|
|
160
183
|
q as toPascalCase,
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export type StepperCalculationsInput = {
|
|
2
|
+
/** Current step number (1-based). Use 0 or negative for minimal dot. */
|
|
3
|
+
currentStep: number;
|
|
4
|
+
/** Total number of steps. */
|
|
5
|
+
totalSteps: number;
|
|
6
|
+
/** Size of the circular stepper in pixels. */
|
|
7
|
+
size: number;
|
|
8
|
+
/** Optional custom label. Defaults to "{currentStep}/{totalSteps}". */
|
|
9
|
+
label?: string;
|
|
10
|
+
/** Stroke width in pixels. @default 4 */
|
|
11
|
+
strokeWidth?: number;
|
|
12
|
+
/** Percentage of full circle to show as arc (0–1). @default 0.75 (270°) */
|
|
13
|
+
arcPercentage?: number;
|
|
14
|
+
};
|
|
15
|
+
export type StepperCalculationsOutput = {
|
|
16
|
+
/** Label text displayed in the center. */
|
|
17
|
+
displayLabel: string;
|
|
18
|
+
/** Progress value between 0 and 1. */
|
|
19
|
+
progress: number;
|
|
20
|
+
/** Circle radius in pixels. */
|
|
21
|
+
r: number;
|
|
22
|
+
/** Center x coordinate. */
|
|
23
|
+
cx: number;
|
|
24
|
+
/** Center y coordinate. */
|
|
25
|
+
cy: number;
|
|
26
|
+
/** Full circle circumference. */
|
|
27
|
+
circumference: number;
|
|
28
|
+
/** Length of the visible arc (track). */
|
|
29
|
+
trackArcLength: number;
|
|
30
|
+
/** strokeDasharray for the gray track circle. */
|
|
31
|
+
trackDashArray: string;
|
|
32
|
+
/** strokeDasharray for the progress (purple) circle. */
|
|
33
|
+
progressDashArray: string;
|
|
34
|
+
/** strokeDashoffset for the progress (purple) circle. */
|
|
35
|
+
progressDashOffset: number;
|
|
36
|
+
/** Whether to show minimal dot instead of progress arc. */
|
|
37
|
+
showMinimalDot: boolean;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Computes all SVG-related values needed to render a circular stepper.
|
|
41
|
+
*
|
|
42
|
+
* This is a pure utility shared between React (web) and React Native
|
|
43
|
+
* implementations of the Stepper component.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const calcs = getStepperCalculations({
|
|
48
|
+
* currentStep: 2,
|
|
49
|
+
* totalSteps: 4,
|
|
50
|
+
* size: 48,
|
|
51
|
+
* });
|
|
52
|
+
*
|
|
53
|
+
* // calcs.displayLabel → '2/4'
|
|
54
|
+
* // calcs.progress → 0.5
|
|
55
|
+
* // calcs.trackDashArray → '<trackArcLength> <circumference>'
|
|
56
|
+
* // calcs.progressDashArray → '<trackArcLength> <circumference>'
|
|
57
|
+
* // calcs.progressDashOffset → offset for 50% fill
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export declare const getStepperCalculations: ({ currentStep, totalSteps, size, label, strokeWidth, arcPercentage, }: StepperCalculationsInput) => StepperCalculationsOutput;
|
|
61
|
+
//# sourceMappingURL=getStepperCalculations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getStepperCalculations.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/getStepperCalculations/getStepperCalculations.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,wBAAwB,GAAG;IACrC,wEAAwE;IACxE,WAAW,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,0CAA0C;IAC1C,YAAY,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,2BAA2B;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,2BAA2B;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,iCAAiC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,cAAc,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,2DAA2D;IAC3D,cAAc,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,sBAAsB,0EAOhC,wBAAwB,KAAG,yBAmC7B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/getStepperCalculations/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,GAC/B,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export type StepperCalculationsInput = {
|
|
2
|
+
/** Current step number (1-based). Use 0 or negative for minimal dot. */
|
|
3
|
+
currentStep: number;
|
|
4
|
+
/** Total number of steps. */
|
|
5
|
+
totalSteps: number;
|
|
6
|
+
/** Size of the circular stepper in pixels. */
|
|
7
|
+
size: number;
|
|
8
|
+
/** Optional custom label. Defaults to "{currentStep}/{totalSteps}". */
|
|
9
|
+
label?: string;
|
|
10
|
+
/** Stroke width in pixels. @default 4 */
|
|
11
|
+
strokeWidth?: number;
|
|
12
|
+
/** Percentage of full circle to show as arc (0–1). @default 0.75 (270°) */
|
|
13
|
+
arcPercentage?: number;
|
|
14
|
+
};
|
|
15
|
+
export type StepperCalculationsOutput = {
|
|
16
|
+
/** Label text displayed in the center. */
|
|
17
|
+
displayLabel: string;
|
|
18
|
+
/** Progress value between 0 and 1. */
|
|
19
|
+
progress: number;
|
|
20
|
+
/** Circle radius in pixels. */
|
|
21
|
+
r: number;
|
|
22
|
+
/** Center x coordinate. */
|
|
23
|
+
cx: number;
|
|
24
|
+
/** Center y coordinate. */
|
|
25
|
+
cy: number;
|
|
26
|
+
/** Full circle circumference. */
|
|
27
|
+
circumference: number;
|
|
28
|
+
/** Length of the visible arc (track). */
|
|
29
|
+
trackArcLength: number;
|
|
30
|
+
/** strokeDasharray for the gray track circle. */
|
|
31
|
+
trackDashArray: string;
|
|
32
|
+
/** strokeDasharray for the progress (purple) circle. */
|
|
33
|
+
progressDashArray: string;
|
|
34
|
+
/** strokeDashoffset for the progress (purple) circle. */
|
|
35
|
+
progressDashOffset: number;
|
|
36
|
+
/** Whether to show minimal dot instead of progress arc. */
|
|
37
|
+
showMinimalDot: boolean;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Computes all SVG-related values needed to render a circular stepper.
|
|
41
|
+
*
|
|
42
|
+
* This is a pure utility shared between React (web) and React Native
|
|
43
|
+
* implementations of the Stepper component.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const calcs = getStepperCalculations({
|
|
48
|
+
* currentStep: 2,
|
|
49
|
+
* totalSteps: 4,
|
|
50
|
+
* size: 48,
|
|
51
|
+
* });
|
|
52
|
+
*
|
|
53
|
+
* // calcs.displayLabel → '2/4'
|
|
54
|
+
* // calcs.progress → 0.5
|
|
55
|
+
* // calcs.trackDashArray → '<trackArcLength> <circumference>'
|
|
56
|
+
* // calcs.progressDashArray → '<trackArcLength> <circumference>'
|
|
57
|
+
* // calcs.progressDashOffset → offset for 50% fill
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export declare const getStepperCalculations: ({ currentStep, totalSteps, size, label, strokeWidth, arcPercentage, }: StepperCalculationsInput) => StepperCalculationsOutput;
|
|
61
|
+
//# sourceMappingURL=getStepperCalculations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getStepperCalculations.d.ts","sourceRoot":"","sources":["../../../src/lib/getStepperCalculations/getStepperCalculations.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,wBAAwB,GAAG;IACrC,wEAAwE;IACxE,WAAW,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,0CAA0C;IAC1C,YAAY,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,2BAA2B;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,2BAA2B;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,iCAAiC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,cAAc,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,2DAA2D;IAC3D,cAAc,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,sBAAsB,0EAOhC,wBAAwB,KAAG,yBAkC7B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/getStepperCalculations/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,GAC/B,MAAM,0BAA0B,CAAC"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { getStepperCalculations } from '../getStepperCalculations';
|
|
3
|
+
|
|
4
|
+
describe('getStepperCalculations', () => {
|
|
5
|
+
const defaultInput = {
|
|
6
|
+
currentStep: 2,
|
|
7
|
+
totalSteps: 4,
|
|
8
|
+
size: 48,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
it('should compute display label as "currentStep/totalSteps"', () => {
|
|
12
|
+
const result = getStepperCalculations(defaultInput);
|
|
13
|
+
expect(result.displayLabel).toBe('2/4');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should use custom label when provided', () => {
|
|
17
|
+
const result = getStepperCalculations({
|
|
18
|
+
...defaultInput,
|
|
19
|
+
label: 'Step 2 of 4',
|
|
20
|
+
});
|
|
21
|
+
expect(result.displayLabel).toBe('Step 2 of 4');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should compute progress as currentStep / totalSteps', () => {
|
|
25
|
+
const result = getStepperCalculations(defaultInput);
|
|
26
|
+
expect(result.progress).toBe(0.5);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should clamp progress and label when currentStep is out of bounds', () => {
|
|
30
|
+
const negative = getStepperCalculations({
|
|
31
|
+
...defaultInput,
|
|
32
|
+
currentStep: -3,
|
|
33
|
+
});
|
|
34
|
+
expect(negative.progress).toBe(0);
|
|
35
|
+
expect(negative.displayLabel).toBe('0/4');
|
|
36
|
+
|
|
37
|
+
const overflow = getStepperCalculations({
|
|
38
|
+
...defaultInput,
|
|
39
|
+
currentStep: 10,
|
|
40
|
+
});
|
|
41
|
+
expect(overflow.progress).toBe(1);
|
|
42
|
+
expect(overflow.displayLabel).toBe('4/4');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should handle zero totalSteps gracefully', () => {
|
|
46
|
+
const result = getStepperCalculations({
|
|
47
|
+
...defaultInput,
|
|
48
|
+
totalSteps: 0,
|
|
49
|
+
});
|
|
50
|
+
expect(result.progress).toBe(0);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should show minimal dot when currentStep <= 0', () => {
|
|
54
|
+
const zero = getStepperCalculations({
|
|
55
|
+
...defaultInput,
|
|
56
|
+
currentStep: 0,
|
|
57
|
+
});
|
|
58
|
+
expect(zero.showMinimalDot).toBe(true);
|
|
59
|
+
|
|
60
|
+
const started = getStepperCalculations(defaultInput);
|
|
61
|
+
expect(started.showMinimalDot).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export type StepperCalculationsInput = {
|
|
2
|
+
/** Current step number (1-based). Use 0 or negative for minimal dot. */
|
|
3
|
+
currentStep: number;
|
|
4
|
+
/** Total number of steps. */
|
|
5
|
+
totalSteps: number;
|
|
6
|
+
/** Size of the circular stepper in pixels. */
|
|
7
|
+
size: number;
|
|
8
|
+
/** Optional custom label. Defaults to "{currentStep}/{totalSteps}". */
|
|
9
|
+
label?: string;
|
|
10
|
+
/** Stroke width in pixels. @default 4 */
|
|
11
|
+
strokeWidth?: number;
|
|
12
|
+
/** Percentage of full circle to show as arc (0–1). @default 0.75 (270°) */
|
|
13
|
+
arcPercentage?: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type StepperCalculationsOutput = {
|
|
17
|
+
/** Label text displayed in the center. */
|
|
18
|
+
displayLabel: string;
|
|
19
|
+
/** Progress value between 0 and 1. */
|
|
20
|
+
progress: number;
|
|
21
|
+
/** Circle radius in pixels. */
|
|
22
|
+
r: number;
|
|
23
|
+
/** Center x coordinate. */
|
|
24
|
+
cx: number;
|
|
25
|
+
/** Center y coordinate. */
|
|
26
|
+
cy: number;
|
|
27
|
+
/** Full circle circumference. */
|
|
28
|
+
circumference: number;
|
|
29
|
+
/** Length of the visible arc (track). */
|
|
30
|
+
trackArcLength: number;
|
|
31
|
+
/** strokeDasharray for the gray track circle. */
|
|
32
|
+
trackDashArray: string;
|
|
33
|
+
/** strokeDasharray for the progress (purple) circle. */
|
|
34
|
+
progressDashArray: string;
|
|
35
|
+
/** strokeDashoffset for the progress (purple) circle. */
|
|
36
|
+
progressDashOffset: number;
|
|
37
|
+
/** Whether to show minimal dot instead of progress arc. */
|
|
38
|
+
showMinimalDot: boolean;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Computes all SVG-related values needed to render a circular stepper.
|
|
43
|
+
*
|
|
44
|
+
* This is a pure utility shared between React (web) and React Native
|
|
45
|
+
* implementations of the Stepper component.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* const calcs = getStepperCalculations({
|
|
50
|
+
* currentStep: 2,
|
|
51
|
+
* totalSteps: 4,
|
|
52
|
+
* size: 48,
|
|
53
|
+
* });
|
|
54
|
+
*
|
|
55
|
+
* // calcs.displayLabel → '2/4'
|
|
56
|
+
* // calcs.progress → 0.5
|
|
57
|
+
* // calcs.trackDashArray → '<trackArcLength> <circumference>'
|
|
58
|
+
* // calcs.progressDashArray → '<trackArcLength> <circumference>'
|
|
59
|
+
* // calcs.progressDashOffset → offset for 50% fill
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export const getStepperCalculations = ({
|
|
63
|
+
currentStep,
|
|
64
|
+
totalSteps,
|
|
65
|
+
size,
|
|
66
|
+
label,
|
|
67
|
+
strokeWidth = 4,
|
|
68
|
+
arcPercentage = 0.75,
|
|
69
|
+
}: StepperCalculationsInput): StepperCalculationsOutput => {
|
|
70
|
+
// Clamp currentStep: minimum 0, maximum totalSteps
|
|
71
|
+
const clampedCurrentStep = Math.min(Math.max(currentStep, 0), totalSteps);
|
|
72
|
+
|
|
73
|
+
const displayLabel = label ?? `${clampedCurrentStep}/${totalSteps}`;
|
|
74
|
+
const progress = totalSteps <= 0 ? 0 : clampedCurrentStep / totalSteps;
|
|
75
|
+
|
|
76
|
+
// SVG circle geometry
|
|
77
|
+
const r = (size - strokeWidth) / 2;
|
|
78
|
+
const cx = size / 2;
|
|
79
|
+
const cy = size / 2;
|
|
80
|
+
const circumference = 2 * Math.PI * r;
|
|
81
|
+
|
|
82
|
+
// Arc calculations
|
|
83
|
+
const trackArcLength = circumference * arcPercentage;
|
|
84
|
+
const dashOffset = trackArcLength * (1 - progress);
|
|
85
|
+
|
|
86
|
+
// Minimal dot handling (currentStep <= 0 means "not started")
|
|
87
|
+
const showMinimalDot = clampedCurrentStep <= 0;
|
|
88
|
+
const progressDashArray = `${trackArcLength} ${circumference}`;
|
|
89
|
+
const progressDashOffset = showMinimalDot ? trackArcLength - 2 : dashOffset;
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
displayLabel,
|
|
93
|
+
progress,
|
|
94
|
+
r,
|
|
95
|
+
cx,
|
|
96
|
+
cy,
|
|
97
|
+
circumference,
|
|
98
|
+
trackArcLength,
|
|
99
|
+
trackDashArray: `${trackArcLength} ${circumference}`,
|
|
100
|
+
progressDashArray,
|
|
101
|
+
progressDashOffset,
|
|
102
|
+
showMinimalDot,
|
|
103
|
+
};
|
|
104
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './getStepperCalculations';
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { getStepperCalculations } from './getStepperCalculations';
|
|
3
|
+
|
|
4
|
+
describe('getStepperCalculations', () => {
|
|
5
|
+
const defaultInput = {
|
|
6
|
+
currentStep: 2,
|
|
7
|
+
totalSteps: 4,
|
|
8
|
+
size: 48,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
it('should compute display label from currentStep and totalSteps', () => {
|
|
12
|
+
const result = getStepperCalculations(defaultInput);
|
|
13
|
+
expect(result.displayLabel).toBe('2/4');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should use custom label when provided', () => {
|
|
17
|
+
const result = getStepperCalculations({
|
|
18
|
+
...defaultInput,
|
|
19
|
+
label: 'Step 2 of 4',
|
|
20
|
+
});
|
|
21
|
+
expect(result.displayLabel).toBe('Step 2 of 4');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should clamp display label to totalSteps', () => {
|
|
25
|
+
const result = getStepperCalculations({
|
|
26
|
+
...defaultInput,
|
|
27
|
+
currentStep: 10,
|
|
28
|
+
});
|
|
29
|
+
expect(result.displayLabel).toBe('4/4');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should compute progress as currentStep / totalSteps', () => {
|
|
33
|
+
const result = getStepperCalculations(defaultInput);
|
|
34
|
+
expect(result.progress).toBe(0.5);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should clamp progress between 0 and 1', () => {
|
|
38
|
+
const negativeResult = getStepperCalculations({
|
|
39
|
+
...defaultInput,
|
|
40
|
+
currentStep: -1,
|
|
41
|
+
});
|
|
42
|
+
expect(negativeResult.progress).toBe(0);
|
|
43
|
+
|
|
44
|
+
const overflowResult = getStepperCalculations({
|
|
45
|
+
...defaultInput,
|
|
46
|
+
currentStep: 10,
|
|
47
|
+
});
|
|
48
|
+
expect(overflowResult.progress).toBe(1);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should handle zero totalSteps gracefully', () => {
|
|
52
|
+
const result = getStepperCalculations({
|
|
53
|
+
...defaultInput,
|
|
54
|
+
totalSteps: 0,
|
|
55
|
+
});
|
|
56
|
+
expect(result.progress).toBe(0);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should compute correct SVG geometry for default size', () => {
|
|
60
|
+
const result = getStepperCalculations(defaultInput);
|
|
61
|
+
expect(result.cx).toBe(24);
|
|
62
|
+
expect(result.cy).toBe(24);
|
|
63
|
+
expect(result.r).toBe(22); // (48 - 4) / 2
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should respect custom strokeWidth', () => {
|
|
67
|
+
const result = getStepperCalculations({
|
|
68
|
+
...defaultInput,
|
|
69
|
+
strokeWidth: 6,
|
|
70
|
+
});
|
|
71
|
+
expect(result.r).toBe(21); // (48 - 6) / 2
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should compute trackArcLength as 75% of circumference by default', () => {
|
|
75
|
+
const result = getStepperCalculations(defaultInput);
|
|
76
|
+
const expectedCircumference = 2 * Math.PI * 22;
|
|
77
|
+
expect(result.circumference).toBeCloseTo(expectedCircumference);
|
|
78
|
+
expect(result.trackArcLength).toBeCloseTo(expectedCircumference * 0.75);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should respect custom arcPercentage', () => {
|
|
82
|
+
const result = getStepperCalculations({
|
|
83
|
+
...defaultInput,
|
|
84
|
+
arcPercentage: 0.5,
|
|
85
|
+
});
|
|
86
|
+
const expectedCircumference = 2 * Math.PI * 22;
|
|
87
|
+
expect(result.trackArcLength).toBeCloseTo(expectedCircumference * 0.5);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should show minimal dot when currentStep is 0', () => {
|
|
91
|
+
const result = getStepperCalculations({
|
|
92
|
+
...defaultInput,
|
|
93
|
+
currentStep: 0,
|
|
94
|
+
});
|
|
95
|
+
expect(result.showMinimalDot).toBe(true);
|
|
96
|
+
expect(result.progressDashOffset).toBeCloseTo(result.trackArcLength - 2);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should show minimal dot when currentStep is negative', () => {
|
|
100
|
+
const result = getStepperCalculations({
|
|
101
|
+
...defaultInput,
|
|
102
|
+
currentStep: -1,
|
|
103
|
+
});
|
|
104
|
+
expect(result.showMinimalDot).toBe(true);
|
|
105
|
+
expect(result.progressDashOffset).toBeCloseTo(result.trackArcLength - 2);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should show normal progress arc when currentStep > 0', () => {
|
|
109
|
+
const result = getStepperCalculations(defaultInput);
|
|
110
|
+
expect(result.showMinimalDot).toBe(false);
|
|
111
|
+
expect(result.progressDashArray).toContain(String(result.trackArcLength));
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should compute progressDashOffset as 0 when progress is 100%', () => {
|
|
115
|
+
const result = getStepperCalculations({
|
|
116
|
+
...defaultInput,
|
|
117
|
+
currentStep: 4,
|
|
118
|
+
totalSteps: 4,
|
|
119
|
+
});
|
|
120
|
+
expect(result.progressDashOffset).toBeCloseTo(0);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should compute progressDashOffset equal to trackArcLength when progress is 0%', () => {
|
|
124
|
+
const result = getStepperCalculations({
|
|
125
|
+
...defaultInput,
|
|
126
|
+
currentStep: 0,
|
|
127
|
+
totalSteps: 4,
|
|
128
|
+
});
|
|
129
|
+
// When showMinimalDot, offset = trackArcLength - 2
|
|
130
|
+
expect(result.progressDashOffset).toBeCloseTo(result.trackArcLength - 2);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should compute trackDashArray consistently', () => {
|
|
134
|
+
const result = getStepperCalculations(defaultInput);
|
|
135
|
+
expect(result.trackDashArray).toBe(
|
|
136
|
+
`${result.trackArcLength} ${result.circumference}`,
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export type StepperCalculationsInput = {
|
|
2
|
+
/** Current step number (1-based). Use 0 or negative for minimal dot. */
|
|
3
|
+
currentStep: number;
|
|
4
|
+
/** Total number of steps. */
|
|
5
|
+
totalSteps: number;
|
|
6
|
+
/** Size of the circular stepper in pixels. */
|
|
7
|
+
size: number;
|
|
8
|
+
/** Optional custom label. Defaults to "{currentStep}/{totalSteps}". */
|
|
9
|
+
label?: string;
|
|
10
|
+
/** Stroke width in pixels. @default 4 */
|
|
11
|
+
strokeWidth?: number;
|
|
12
|
+
/** Percentage of full circle to show as arc (0–1). @default 0.75 (270°) */
|
|
13
|
+
arcPercentage?: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type StepperCalculationsOutput = {
|
|
17
|
+
/** Label text displayed in the center. */
|
|
18
|
+
displayLabel: string;
|
|
19
|
+
/** Progress value between 0 and 1. */
|
|
20
|
+
progress: number;
|
|
21
|
+
/** Circle radius in pixels. */
|
|
22
|
+
r: number;
|
|
23
|
+
/** Center x coordinate. */
|
|
24
|
+
cx: number;
|
|
25
|
+
/** Center y coordinate. */
|
|
26
|
+
cy: number;
|
|
27
|
+
/** Full circle circumference. */
|
|
28
|
+
circumference: number;
|
|
29
|
+
/** Length of the visible arc (track). */
|
|
30
|
+
trackArcLength: number;
|
|
31
|
+
/** strokeDasharray for the gray track circle. */
|
|
32
|
+
trackDashArray: string;
|
|
33
|
+
/** strokeDasharray for the progress (purple) circle. */
|
|
34
|
+
progressDashArray: string;
|
|
35
|
+
/** strokeDashoffset for the progress (purple) circle. */
|
|
36
|
+
progressDashOffset: number;
|
|
37
|
+
/** Whether to show minimal dot instead of progress arc. */
|
|
38
|
+
showMinimalDot: boolean;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Computes all SVG-related values needed to render a circular stepper.
|
|
43
|
+
*
|
|
44
|
+
* This is a pure utility shared between React (web) and React Native
|
|
45
|
+
* implementations of the Stepper component.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* const calcs = getStepperCalculations({
|
|
50
|
+
* currentStep: 2,
|
|
51
|
+
* totalSteps: 4,
|
|
52
|
+
* size: 48,
|
|
53
|
+
* });
|
|
54
|
+
*
|
|
55
|
+
* // calcs.displayLabel → '2/4'
|
|
56
|
+
* // calcs.progress → 0.5
|
|
57
|
+
* // calcs.trackDashArray → '<trackArcLength> <circumference>'
|
|
58
|
+
* // calcs.progressDashArray → '<trackArcLength> <circumference>'
|
|
59
|
+
* // calcs.progressDashOffset → offset for 50% fill
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export const getStepperCalculations = ({
|
|
63
|
+
currentStep,
|
|
64
|
+
totalSteps,
|
|
65
|
+
size,
|
|
66
|
+
label,
|
|
67
|
+
strokeWidth = 4,
|
|
68
|
+
arcPercentage = 0.75,
|
|
69
|
+
}: StepperCalculationsInput): StepperCalculationsOutput => {
|
|
70
|
+
const displayLabel =
|
|
71
|
+
label ?? `${Math.min(currentStep, totalSteps)}/${totalSteps}`;
|
|
72
|
+
const progress =
|
|
73
|
+
totalSteps <= 0 ? 0 : Math.min(1, Math.max(0, currentStep / totalSteps));
|
|
74
|
+
|
|
75
|
+
// SVG circle geometry
|
|
76
|
+
const r = (size - strokeWidth) / 2;
|
|
77
|
+
const cx = size / 2;
|
|
78
|
+
const cy = size / 2;
|
|
79
|
+
const circumference = 2 * Math.PI * r;
|
|
80
|
+
|
|
81
|
+
// Arc calculations
|
|
82
|
+
const trackArcLength = circumference * arcPercentage;
|
|
83
|
+
const dashOffset = trackArcLength * (1 - progress);
|
|
84
|
+
|
|
85
|
+
// Minimal dot handling (currentStep <= 0 means "not started")
|
|
86
|
+
const showMinimalDot = currentStep <= 0;
|
|
87
|
+
const progressDashArray = `${trackArcLength} ${circumference}`;
|
|
88
|
+
const progressDashOffset = showMinimalDot ? trackArcLength - 2 : dashOffset;
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
displayLabel,
|
|
92
|
+
progress,
|
|
93
|
+
r,
|
|
94
|
+
cx,
|
|
95
|
+
cy,
|
|
96
|
+
circumference,
|
|
97
|
+
trackArcLength,
|
|
98
|
+
trackDashArray: `${trackArcLength} ${circumference}`,
|
|
99
|
+
progressDashArray,
|
|
100
|
+
progressDashOffset,
|
|
101
|
+
showMinimalDot,
|
|
102
|
+
};
|
|
103
|
+
};
|