@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 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
@@ -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 W } from "react/jsx-runtime";
2
- import { createContext as k, useMemo as A, use as F } from "react";
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 z = 48, N = 17, R = 2;
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
- N,
13
- z - n * R
12
+ R,
13
+ N - n * w
14
14
  );
15
15
  }
16
- function D(t, n = !0) {
16
+ function C(t, n = !0) {
17
17
  if (!t) return "";
18
- const l = t.includes("."), [o, r] = t.split("."), c = o.replace(/\B(?=(\d{3})+(?!\d))/g, " ");
19
- return l && n ? r ? `${c}.${r}` : `${c}.` : c;
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: l = !0,
24
- thousandsSeparator: o = !0,
25
- maxIntegerLength: r = 9,
26
- maxDecimalLength: c = 9
27
- } = n, i = t.replace(",", ".").replace(/[^\d.]/g, "");
28
- let e = i;
29
- if (e = e.replace(/^0+(?=\d)/, ""), !l)
30
- return e = e.replace(/\D/g, ""), r > 0 && e.length > r && (e = e.slice(0, r)), o ? D(e) : e;
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 f = e.indexOf(".");
33
- if (f !== -1) {
34
- let s = e.slice(0, f), a = e.slice(f + 1).replace(/\./g, "");
35
- r > 0 && s.length > r && (s = s.slice(0, r)), a = a.slice(0, c), s === "" && (s = "0");
36
- const g = i.endsWith(".") || e.endsWith(".");
37
- e = a.length > 0 ? `${s}.${a}` : g ? `${s}.` : s;
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
- r > 0 && e.length > r && (e = e.slice(0, r));
40
- return o ? D(e) : e;
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 l = k(n), o = ({ children: c, value: i }) => {
44
- const e = A(
45
- () => i,
46
- Object.values(i ?? {})
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__ */ W(l, { value: e, children: c });
48
+ return /* @__PURE__ */ k(i, { value: e, children: u });
49
49
  };
50
- o.displayName = t + "Provider";
51
- function r({
52
- consumerName: c,
53
- contextRequired: i
50
+ l.displayName = t + "Provider";
51
+ function o({
52
+ consumerName: u,
53
+ contextRequired: r
54
54
  }) {
55
- const e = F(l);
55
+ const e = F(i);
56
56
  if (e)
57
57
  return e;
58
- if (i)
58
+ if (r)
59
59
  throw new Error(
60
- `${c} must be used within ${t}`
60
+ `${u} must be used within ${t}`
61
61
  );
62
62
  return n || {};
63
63
  }
64
- return [o, r];
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 M(t, n) {
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 w(...t) {
75
+ function z(...t) {
76
76
  const n = /* @__PURE__ */ new Map();
77
- return (l) => {
78
- if (l === null) {
79
- t.forEach((o) => {
80
- const r = n.get(o);
81
- typeof r == "function" && r(), M(o, null);
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((o) => {
86
- const r = M(o, l);
87
- typeof r == "function" && n.set(o, r);
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 A(() => w(...t), t);
92
+ return v(() => z(...t), t);
93
93
  }
94
94
  const tt = (t, n) => {
95
- let l = t;
96
- for (const o of n) {
97
- if (l == null)
95
+ let i = t;
96
+ for (const l of n) {
97
+ if (i == null)
98
98
  return;
99
- l = l[o];
99
+ i = i[l];
100
100
  }
101
- return l;
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, l = {}) {
104
- const { leading: o = !1, trailing: r = !0, maxWait: c } = l;
105
- let i = null, e = null, f = null, s = 0, a = null, g = null, d;
106
- const x = c !== void 0, S = x ? Math.max(c, n) : 0, T = (u) => {
107
- s = u;
108
- const m = a, h = g;
109
- return a = null, g = null, d = t.apply(h, m), d;
110
- }, E = (u) => {
111
- const m = f ? u - f : 0;
112
- return n - m;
113
- }, v = (u) => {
114
- const m = f ? u - f : n, h = u - s;
115
- return f === null || m >= n || m < 0 || x && h >= S;
116
- }, C = (u) => (i = null, r && a ? T(u) : (a = null, g = null, d)), p = () => {
117
- const u = Date.now();
118
- if (v(u)) {
119
- C(u);
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
- i = setTimeout(p, E(u));
123
- }, P = () => {
124
- const u = Date.now();
125
- e = null, a && (T(u), i !== null && (clearTimeout(i), i = setTimeout(p, n)));
126
- }, b = (u) => (s = u, i = setTimeout(p, n), x && (e = setTimeout(P, S)), o ? T(u) : d), $ = () => {
127
- i !== null && clearTimeout(i), e !== null && clearTimeout(e), s = 0, a = null, g = null, f = null, i = null, e = null;
128
- }, L = () => i === null && e === null ? d : C(Date.now()), O = () => i !== null || e !== null, y = function(...u) {
129
- const m = Date.now(), h = v(m);
130
- if (a = u, g = this, f = m, h) {
131
- if (i === null)
132
- return b(m);
133
- if (x)
134
- return clearTimeout(i), i = setTimeout(p, n), T(m);
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 i === null && (i = setTimeout(p, n)), d;
158
+ return r === null && (r = setTimeout(x, n)), m;
137
159
  };
138
- return y.cancel = $, y.flush = L, y.pending = O, y;
160
+ return M.cancel = b, M.flush = L, M.pending = O, M;
139
161
  }
140
- function et(t, n, l = {}) {
141
- const { leading: o = !0, trailing: r = !0 } = l;
162
+ function et(t, n, i = {}) {
163
+ const { leading: l = !0, trailing: o = !0 } = i;
142
164
  return V(t, n, {
143
- leading: o,
144
- trailing: r,
165
+ leading: l,
166
+ trailing: o,
145
167
  maxWait: n
146
168
  });
147
169
  }
148
170
  export {
149
- M as assignRef,
171
+ S as assignRef,
150
172
  K as cn,
151
173
  J as createSafeContext,
152
174
  V as debounce,
153
- D as formatThousands,
175
+ C as formatThousands,
154
176
  G as getFontSize,
155
177
  tt as getObjectPath,
178
+ nt as getStepperCalculations,
156
179
  Q as isTextChildren,
157
- w as mergeRefs,
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,2 @@
1
+ export { getStepperCalculations, type StepperCalculationsInput, type StepperCalculationsOutput, } from './getStepperCalculations';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -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,2 @@
1
+ export * from './getStepperCalculations';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -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,2 @@
1
+ export { getStepperCalculations, type StepperCalculationsInput, type StepperCalculationsOutput, } from './getStepperCalculations';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ledgerhq/lumen-utils-shared",
3
- "version": "0.0.19",
3
+ "version": "0.0.20",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
package/src/index.ts CHANGED
@@ -5,5 +5,6 @@ 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';
@@ -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,5 @@
1
+ export {
2
+ getStepperCalculations,
3
+ type StepperCalculationsInput,
4
+ type StepperCalculationsOutput,
5
+ } from './getStepperCalculations';
@@ -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
+ };
@@ -0,0 +1,5 @@
1
+ export {
2
+ getStepperCalculations,
3
+ type StepperCalculationsInput,
4
+ type StepperCalculationsOutput,
5
+ } from './getStepperCalculations';