@ledgerhq/lumen-utils-shared 0.1.6 → 0.1.8
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 +149 -147
- package/dist/lib/inputFormatter/textFormatter.d.ts +13 -2
- package/dist/lib/inputFormatter/textFormatter.d.ts.map +1 -1
- package/dist/lib/resolveProps/index.d.ts +2 -0
- package/dist/lib/resolveProps/index.d.ts.map +1 -0
- package/dist/lib/resolveProps/resolveProps.d.ts +4 -0
- package/dist/lib/resolveProps/resolveProps.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/lib/inputFormatter/textFormatter.test.ts +59 -1
- package/src/lib/inputFormatter/textFormatter.ts +63 -51
- package/src/lib/resolveProps/index.ts +1 -0
- package/src/lib/resolveProps/resolveProps.test.ts +22 -0
- package/src/lib/resolveProps/resolveProps.ts +11 -0
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from './lib/cn';
|
|
|
6
6
|
export * from './lib/isTextChildren';
|
|
7
7
|
export * from './lib/useMergeRef';
|
|
8
8
|
export * from './lib/getObjectPath';
|
|
9
|
+
export * from './lib/resolveProps';
|
|
9
10
|
export * from './lib/components';
|
|
10
11
|
export * from './lib/throttle';
|
|
11
12
|
export * from './lib/debounce';
|
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,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,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;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,mCAAmC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,mCAAmC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,143 +1,144 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { createContext as
|
|
3
|
-
import { clsx as
|
|
4
|
-
import { twMerge as
|
|
5
|
-
function
|
|
1
|
+
import { jsx as v } from "react/jsx-runtime";
|
|
2
|
+
import { createContext as F, useRef as R, useContext as W, useMemo as C } from "react";
|
|
3
|
+
import { clsx as w } from "clsx";
|
|
4
|
+
import { twMerge as B } from "tailwind-merge";
|
|
5
|
+
function et(t) {
|
|
6
6
|
return t ? t.split(/[-_ ]+/).map((e) => e.charAt(0).toUpperCase() + e.slice(1).toLowerCase()).join("") : "";
|
|
7
7
|
}
|
|
8
8
|
const _ = {
|
|
9
9
|
md: { max: 48, min: 17, scale: 2, startAt: 0 },
|
|
10
10
|
sm: { max: 28, min: 12, scale: 2, startAt: 6 }
|
|
11
11
|
};
|
|
12
|
-
function
|
|
13
|
-
const { max: r, min:
|
|
14
|
-
return Math.max(
|
|
12
|
+
function nt(t, e = "md") {
|
|
13
|
+
const { max: r, min: n, scale: i, startAt: s } = _[e], o = t.replace(/\D/g, "").length;
|
|
14
|
+
return Math.max(n, r - Math.max(0, o - s) * i);
|
|
15
15
|
}
|
|
16
|
-
function
|
|
16
|
+
function z(t, e = !0) {
|
|
17
17
|
if (!t) return "";
|
|
18
|
-
const r = t.includes("."), [
|
|
19
|
-
return r && e ?
|
|
18
|
+
const r = t.includes("."), [n, i] = t.split("."), s = n.replace(/\B(?=(\d{3})+(?!\d))/g, " ");
|
|
19
|
+
return r && e ? i ? `${s}.${i}` : `${s}.` : s;
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
const Z = (t) => t.replaceAll(",", ".").replaceAll(/[^\d.]/g, ""), q = (t) => t.replace(/^0+(?=\d)/, ""), T = (t, e) => e > 0 ? t.slice(0, e) : t, K = (t, e) => T(t.replaceAll(/\D/g, ""), e), N = (t, e, r) => {
|
|
22
|
+
if (t === ".") return "0.";
|
|
23
|
+
const n = t.indexOf(".");
|
|
24
|
+
if (n === -1) return T(t, e);
|
|
25
|
+
const i = T(t.slice(0, n), e) || "0", s = t.slice(n + 1).replaceAll(".", "").slice(0, r);
|
|
26
|
+
return s.length > 0 ? `${i}.${s}` : t.endsWith(".") ? `${i}.` : i;
|
|
27
|
+
};
|
|
28
|
+
function rt(t, e = {}) {
|
|
22
29
|
const {
|
|
23
30
|
allowDecimals: r = !0,
|
|
24
|
-
thousandsSeparator:
|
|
25
|
-
maxIntegerLength:
|
|
26
|
-
maxDecimalLength:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return n = n.replace(/\D/g, ""), s > 0 && n.length > s && (n = n.slice(0, s)), o ? A(n) : n;
|
|
31
|
-
n === "." && (n = "0.");
|
|
32
|
-
const u = n.indexOf(".");
|
|
33
|
-
if (u === -1)
|
|
34
|
-
s > 0 && n.length > s && (n = n.slice(0, s));
|
|
35
|
-
else {
|
|
36
|
-
let a = n.slice(0, u), f = n.slice(u + 1).replace(/\./g, "");
|
|
37
|
-
s > 0 && a.length > s && (a = a.slice(0, s)), f = f.slice(0, l), a === "" && (a = "0");
|
|
38
|
-
const h = i.endsWith(".") || n.endsWith(".");
|
|
39
|
-
n = f.length > 0 ? `${a}.${f}` : h ? `${a}.` : a;
|
|
40
|
-
}
|
|
41
|
-
return o ? A(n) : n;
|
|
31
|
+
thousandsSeparator: n = !0,
|
|
32
|
+
maxIntegerLength: i = 9,
|
|
33
|
+
maxDecimalLength: s = 9,
|
|
34
|
+
decimalSeparator: o = "."
|
|
35
|
+
} = e, c = q(Z(t)), a = r ? N(c, i, s) : K(c, i), d = n ? z(a) : a;
|
|
36
|
+
return o === "." ? d : d.replaceAll(".", o);
|
|
42
37
|
}
|
|
43
|
-
function
|
|
38
|
+
function H(t, e) {
|
|
44
39
|
if (Object.is(t, e)) return !0;
|
|
45
|
-
const r = t,
|
|
46
|
-
return
|
|
47
|
-
(
|
|
40
|
+
const r = t, n = e, i = Object.keys(r), s = Object.keys(n);
|
|
41
|
+
return i.length !== s.length ? !1 : i.every(
|
|
42
|
+
(o) => Object.prototype.hasOwnProperty.call(n, o) && Object.is(r[o], n[o])
|
|
48
43
|
);
|
|
49
44
|
}
|
|
50
|
-
function
|
|
51
|
-
const r =
|
|
52
|
-
const
|
|
53
|
-
return
|
|
45
|
+
function U(t, e) {
|
|
46
|
+
const r = F(e), n = ({ children: s, value: o }) => {
|
|
47
|
+
const c = R(o);
|
|
48
|
+
return H(c.current, o) || (c.current = o), /* @__PURE__ */ v(r.Provider, { value: c.current, children: s });
|
|
54
49
|
};
|
|
55
|
-
|
|
56
|
-
function
|
|
57
|
-
consumerName:
|
|
58
|
-
contextRequired:
|
|
50
|
+
n.displayName = t + "Provider";
|
|
51
|
+
function i({
|
|
52
|
+
consumerName: s,
|
|
53
|
+
contextRequired: o
|
|
59
54
|
}) {
|
|
60
|
-
const
|
|
61
|
-
if (
|
|
62
|
-
return
|
|
63
|
-
if (
|
|
55
|
+
const c = W(r);
|
|
56
|
+
if (c)
|
|
57
|
+
return c;
|
|
58
|
+
if (o)
|
|
64
59
|
throw new Error(
|
|
65
|
-
`${
|
|
60
|
+
`${s} must be used within ${t}`
|
|
66
61
|
);
|
|
67
62
|
return e || {};
|
|
68
63
|
}
|
|
69
|
-
return [
|
|
64
|
+
return [n, i];
|
|
70
65
|
}
|
|
71
|
-
const [
|
|
66
|
+
const [ot, Y] = U("Disabled", { disabled: !1 }), it = ({
|
|
72
67
|
consumerName: t,
|
|
73
68
|
contextRequired: e,
|
|
74
69
|
mergeWith: r
|
|
75
70
|
}) => {
|
|
76
|
-
const
|
|
71
|
+
const n = Y({
|
|
77
72
|
consumerName: t,
|
|
78
73
|
contextRequired: e ?? !1
|
|
79
74
|
});
|
|
80
|
-
return !!(r?.disabled ||
|
|
75
|
+
return !!(r?.disabled || n.disabled);
|
|
81
76
|
};
|
|
82
|
-
function
|
|
83
|
-
return
|
|
77
|
+
function st(...t) {
|
|
78
|
+
return B(w(t));
|
|
84
79
|
}
|
|
85
|
-
const O = (t) => typeof t == "string" || typeof t == "number",
|
|
80
|
+
const O = (t) => typeof t == "string" || typeof t == "number", ct = (t) => Array.isArray(t) ? t.every(O) : O(t);
|
|
86
81
|
function S(t, e) {
|
|
87
82
|
if (typeof t == "function")
|
|
88
83
|
return t(e);
|
|
89
84
|
typeof t == "object" && t !== null && "current" in t && (t.current = e);
|
|
90
85
|
}
|
|
91
|
-
function
|
|
86
|
+
function G(...t) {
|
|
92
87
|
const e = /* @__PURE__ */ new Map();
|
|
93
88
|
return (r) => {
|
|
94
89
|
if (r === null) {
|
|
95
|
-
t.forEach((
|
|
96
|
-
const
|
|
97
|
-
typeof
|
|
90
|
+
t.forEach((n) => {
|
|
91
|
+
const i = e.get(n);
|
|
92
|
+
typeof i == "function" && i(), S(n, null);
|
|
98
93
|
}), e.clear();
|
|
99
94
|
return;
|
|
100
95
|
}
|
|
101
|
-
t.forEach((
|
|
102
|
-
const
|
|
103
|
-
typeof
|
|
96
|
+
t.forEach((n) => {
|
|
97
|
+
const i = S(n, r);
|
|
98
|
+
typeof i == "function" && e.set(n, i);
|
|
104
99
|
});
|
|
105
100
|
};
|
|
106
101
|
}
|
|
107
|
-
function
|
|
108
|
-
return
|
|
102
|
+
function lt(...t) {
|
|
103
|
+
return C(() => G(...t), t);
|
|
109
104
|
}
|
|
110
|
-
const
|
|
105
|
+
const at = (t, e) => {
|
|
111
106
|
let r = t;
|
|
112
|
-
for (const
|
|
107
|
+
for (const n of e) {
|
|
113
108
|
if (r == null)
|
|
114
109
|
return;
|
|
115
|
-
r = r[
|
|
110
|
+
r = r[n];
|
|
116
111
|
}
|
|
117
112
|
return r;
|
|
118
|
-
}
|
|
113
|
+
};
|
|
114
|
+
function ut(t) {
|
|
115
|
+
return function(r, n) {
|
|
116
|
+
return t[r][n];
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const ft = ({
|
|
119
120
|
currentStep: t,
|
|
120
121
|
totalSteps: e,
|
|
121
122
|
size: r,
|
|
122
|
-
label:
|
|
123
|
-
strokeWidth:
|
|
124
|
-
arcPercentage:
|
|
123
|
+
label: n,
|
|
124
|
+
strokeWidth: i = 4,
|
|
125
|
+
arcPercentage: s = 0.75
|
|
125
126
|
}) => {
|
|
126
|
-
const
|
|
127
|
+
const o = Math.min(Math.max(t, 0), e), c = n ?? `${o}/${e}`, a = e <= 0 ? 0 : o / e, d = (r - i) / 2, g = r / 2, p = r / 2, u = 2 * Math.PI * d, m = u * s, P = m * (1 - a), h = o <= 0, D = `${m} ${u}`, b = h ? m - 2 : P;
|
|
127
128
|
return {
|
|
128
|
-
displayLabel:
|
|
129
|
-
progress:
|
|
130
|
-
r:
|
|
131
|
-
cx:
|
|
132
|
-
cy:
|
|
133
|
-
circumference:
|
|
134
|
-
trackArcLength:
|
|
135
|
-
trackDashArray: `${
|
|
129
|
+
displayLabel: c,
|
|
130
|
+
progress: a,
|
|
131
|
+
r: d,
|
|
132
|
+
cx: g,
|
|
133
|
+
cy: p,
|
|
134
|
+
circumference: u,
|
|
135
|
+
trackArcLength: m,
|
|
136
|
+
trackDashArray: `${m} ${u}`,
|
|
136
137
|
progressDashArray: D,
|
|
137
|
-
progressDashOffset:
|
|
138
|
-
showMinimalDot:
|
|
138
|
+
progressDashOffset: b,
|
|
139
|
+
showMinimalDot: h
|
|
139
140
|
};
|
|
140
|
-
},
|
|
141
|
+
}, dt = (t) => C(
|
|
141
142
|
() => ({
|
|
142
143
|
integerPart: Array.from(t.integerPart, (e) => ({
|
|
143
144
|
value: e,
|
|
@@ -149,57 +150,57 @@ const ot = (t, e) => {
|
|
|
149
150
|
})) : []
|
|
150
151
|
}),
|
|
151
152
|
[t.integerPart, t.decimalPart]
|
|
152
|
-
),
|
|
153
|
+
), mt = (t, e, r) => {
|
|
153
154
|
if (e) return r;
|
|
154
|
-
const
|
|
155
|
-
return t.currencyPosition === "end" ? `${
|
|
155
|
+
const n = t.decimalPart ? `${t.decimalSeparator}${t.decimalPart}` : "", i = `${t.integerPart}${n}`;
|
|
156
|
+
return t.currencyPosition === "end" ? `${i} ${t.currencyText}` : `${t.currencyText} ${i}`;
|
|
156
157
|
};
|
|
157
|
-
function
|
|
158
|
-
const { leading:
|
|
159
|
-
let
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
return
|
|
164
|
-
}, D = (
|
|
165
|
-
const
|
|
166
|
-
return e -
|
|
167
|
-
},
|
|
168
|
-
const
|
|
169
|
-
return
|
|
170
|
-
}, $ = (
|
|
171
|
-
const
|
|
172
|
-
if (
|
|
173
|
-
$(
|
|
158
|
+
function J(t, e, r = {}) {
|
|
159
|
+
const { leading: n = !1, trailing: i = !0, maxWait: s } = r;
|
|
160
|
+
let o = null, c = null, a = null, d = 0, g = null, p = null, u;
|
|
161
|
+
const m = s !== void 0, P = m ? Math.max(s, e) : 0, h = (l) => {
|
|
162
|
+
d = l;
|
|
163
|
+
const f = g, x = p;
|
|
164
|
+
return g = null, p = null, u = t.apply(x, f), u;
|
|
165
|
+
}, D = (l) => {
|
|
166
|
+
const f = a ? l - a : 0;
|
|
167
|
+
return e - f;
|
|
168
|
+
}, b = (l) => {
|
|
169
|
+
const f = a ? l - a : e, x = l - d;
|
|
170
|
+
return a === null || f >= e || f < 0 || m && x >= P;
|
|
171
|
+
}, $ = (l) => (o = null, i && g ? h(l) : (g = null, p = null, u)), y = () => {
|
|
172
|
+
const l = Date.now();
|
|
173
|
+
if (b(l)) {
|
|
174
|
+
$(l);
|
|
174
175
|
return;
|
|
175
176
|
}
|
|
176
|
-
|
|
177
|
+
o = setTimeout(y, D(l));
|
|
177
178
|
}, M = () => {
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
},
|
|
181
|
-
|
|
182
|
-
},
|
|
183
|
-
const
|
|
184
|
-
if (
|
|
185
|
-
if (
|
|
186
|
-
return
|
|
187
|
-
if (
|
|
188
|
-
return clearTimeout(
|
|
179
|
+
const l = Date.now();
|
|
180
|
+
c = null, g && (h(l), o !== null && (clearTimeout(o), o = setTimeout(y, e)));
|
|
181
|
+
}, E = (l) => (d = l, o = setTimeout(y, e), m && (c = setTimeout(M, P)), n ? h(l) : u), L = () => {
|
|
182
|
+
o !== null && clearTimeout(o), c !== null && clearTimeout(c), d = 0, g = null, p = null, a = null, o = null, c = null;
|
|
183
|
+
}, j = () => o === null && c === null ? u : $(Date.now()), k = () => o !== null || c !== null, A = function(...l) {
|
|
184
|
+
const f = Date.now(), x = b(f);
|
|
185
|
+
if (g = l, p = this, a = f, x) {
|
|
186
|
+
if (o === null)
|
|
187
|
+
return E(f);
|
|
188
|
+
if (m)
|
|
189
|
+
return clearTimeout(o), o = setTimeout(y, e), h(f);
|
|
189
190
|
}
|
|
190
|
-
return
|
|
191
|
+
return o === null && (o = setTimeout(y, e)), u;
|
|
191
192
|
};
|
|
192
|
-
return
|
|
193
|
+
return A.cancel = L, A.flush = j, A.pending = k, A;
|
|
193
194
|
}
|
|
194
|
-
function
|
|
195
|
-
const { leading:
|
|
196
|
-
return
|
|
197
|
-
leading:
|
|
198
|
-
trailing:
|
|
195
|
+
function gt(t, e, r = {}) {
|
|
196
|
+
const { leading: n = !0, trailing: i = !0 } = r;
|
|
197
|
+
return J(t, e, {
|
|
198
|
+
leading: n,
|
|
199
|
+
trailing: i,
|
|
199
200
|
maxWait: e
|
|
200
201
|
});
|
|
201
202
|
}
|
|
202
|
-
const
|
|
203
|
+
const pt = ({
|
|
203
204
|
onClick: t,
|
|
204
205
|
disabled: e
|
|
205
206
|
}) => t ? e ? {
|
|
@@ -209,43 +210,44 @@ const at = ({
|
|
|
209
210
|
} : {
|
|
210
211
|
role: "button",
|
|
211
212
|
tabIndex: 0,
|
|
212
|
-
onKeyDown: (
|
|
213
|
-
(
|
|
213
|
+
onKeyDown: (n) => {
|
|
214
|
+
(n.key === "Enter" || n.key === " ") && (n.preventDefault(), t?.(n));
|
|
214
215
|
},
|
|
215
216
|
onClick: t
|
|
216
|
-
} : void 0,
|
|
217
|
+
} : void 0, I = " ", ht = ({
|
|
217
218
|
label: t,
|
|
218
219
|
placeholder: e
|
|
219
220
|
}) => {
|
|
220
221
|
const r = typeof e == "string" && e.trim().length > 0 ? e : void 0;
|
|
221
222
|
return t ? {
|
|
222
|
-
inputPlaceholder: r ??
|
|
223
|
+
inputPlaceholder: r ?? I,
|
|
223
224
|
labelStaysFloatedWithPlaceholder: r !== void 0
|
|
224
225
|
} : {
|
|
225
|
-
inputPlaceholder: e ??
|
|
226
|
+
inputPlaceholder: e ?? I,
|
|
226
227
|
labelStaysFloatedWithPlaceholder: !1
|
|
227
228
|
};
|
|
228
229
|
};
|
|
229
230
|
export {
|
|
230
|
-
|
|
231
|
+
ot as DisabledProvider,
|
|
231
232
|
S as assignRef,
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
233
|
+
mt as buildAriaLabel,
|
|
234
|
+
st as cn,
|
|
235
|
+
ut as createPropsResolver,
|
|
236
|
+
U as createSafeContext,
|
|
237
|
+
J as debounce,
|
|
238
|
+
z as formatThousands,
|
|
239
|
+
pt as getButtonA11yProps,
|
|
240
|
+
nt as getFontSize,
|
|
241
|
+
at as getObjectPath,
|
|
242
|
+
ft as getStepperCalculations,
|
|
243
|
+
ct as isTextChildren,
|
|
244
|
+
G as mergeRefs,
|
|
245
|
+
ht as resolveBaseInputPlaceholder,
|
|
246
|
+
H as shallowEqual,
|
|
247
|
+
rt as textFormatter,
|
|
248
|
+
gt as throttle,
|
|
249
|
+
et as toPascalCase,
|
|
250
|
+
it as useDisabledContext,
|
|
251
|
+
lt as useMergedRef,
|
|
252
|
+
dt as useSplitText
|
|
251
253
|
};
|
|
@@ -7,11 +7,20 @@ export type TextFormatterOptions = {
|
|
|
7
7
|
maxIntegerLength?: number;
|
|
8
8
|
/** Maximum length for decimal part (after decimal) */
|
|
9
9
|
maxDecimalLength?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Character used to display the decimal separator.
|
|
12
|
+
* Input parsing always accepts both `.` and `,` regardless of this value.
|
|
13
|
+
*/
|
|
14
|
+
decimalSeparator?: '.' | ',';
|
|
10
15
|
};
|
|
11
16
|
/**
|
|
12
17
|
* Formats and validates numeric input text for amount inputs.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
18
|
+
*
|
|
19
|
+
* Parsing is always lenient: both `.` and `,` are accepted as the decimal
|
|
20
|
+
* separator on input, so the result stays correct regardless of the locale
|
|
21
|
+
* keyboard. The `decimalSeparator` option only controls how the result is
|
|
22
|
+
* displayed.
|
|
23
|
+
*
|
|
15
24
|
* @returns Formatted and cleaned numeric string
|
|
16
25
|
*
|
|
17
26
|
* @example
|
|
@@ -19,9 +28,11 @@ export type TextFormatterOptions = {
|
|
|
19
28
|
* textFormatter('.5') // '0.5'
|
|
20
29
|
* textFormatter('1.2.3') // '1.23'
|
|
21
30
|
* textFormatter('1,5') // '1.5'
|
|
31
|
+
* textFormatter('1,5', { decimalSeparator: ',' }) // '1,5'
|
|
22
32
|
* textFormatter('abc123') // '123'
|
|
23
33
|
* textFormatter('1000000', { thousandsSeparator: true }) // '1 000 000'
|
|
24
34
|
* textFormatter('1234.5678', { thousandsSeparator: true }) // '1 234.5678'
|
|
35
|
+
* textFormatter('1234.5', { decimalSeparator: ',' }) // '1 234,5'
|
|
25
36
|
* textFormatter('123456789012', { maxIntegerLength: 9 }) // '123456789'
|
|
26
37
|
* textFormatter('123.123456', { maxDecimalLength: 2 }) // '123.12'
|
|
27
38
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textFormatter.d.ts","sourceRoot":"","sources":["../../../src/lib/inputFormatter/textFormatter.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,oBAAoB,GAAG;IACjC,sCAAsC;IACtC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,uDAAuD;IACvD,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,uDAAuD;IACvD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"textFormatter.d.ts","sourceRoot":"","sources":["../../../src/lib/inputFormatter/textFormatter.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,oBAAoB,GAAG;IACjC,sCAAsC;IACtC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,uDAAuD;IACvD,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,uDAAuD;IACvD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;CAC9B,CAAC;AAyCF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,aAAa;AAC3B,gCAAgC;AAChC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,oBAAyB,GACjC,MAAM,CAsBR"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/resolveProps/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type PropsMap<Variant extends string, Props> = Record<Variant, Props>;
|
|
2
|
+
export type ContextPropsMap = Record<string, PropsMap<string, unknown>>;
|
|
3
|
+
export declare function createPropsResolver<T extends ContextPropsMap>(config: T): <Context extends keyof T, Variant extends keyof T[Context]>(context: Context, variant: Variant) => T[Context][Variant];
|
|
4
|
+
//# sourceMappingURL=resolveProps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveProps.d.ts","sourceRoot":"","sources":["../../../src/lib/resolveProps/resolveProps.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,CAAC,OAAO,SAAS,MAAM,EAAE,KAAK,IAAI,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AAC7E,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAExE,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,eAAe,EAAE,MAAM,EAAE,CAAC,IAEpE,OAAO,SAAS,MAAM,CAAC,EACvB,OAAO,SAAS,MAAM,CAAC,CAAC,OAAO,CAAC,EAChC,SAAS,OAAO,EAAE,SAAS,OAAO,yBAGrC"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from './lib/cn';
|
|
|
6
6
|
export * from './lib/isTextChildren';
|
|
7
7
|
export * from './lib/useMergeRef';
|
|
8
8
|
export * from './lib/getObjectPath';
|
|
9
|
+
export * from './lib/resolveProps';
|
|
9
10
|
export * from './lib/components';
|
|
10
11
|
export * from './lib/throttle';
|
|
11
12
|
export * from './lib/debounce';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { textFormatter } from './textFormatter.js';
|
|
2
|
+
import { textFormatter, type TextFormatterOptions } from './textFormatter.js';
|
|
3
3
|
|
|
4
4
|
describe('textFormatter', () => {
|
|
5
5
|
describe('decimal handling with allowDecimals=true (default)', () => {
|
|
@@ -93,6 +93,64 @@ describe('textFormatter', () => {
|
|
|
93
93
|
});
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
+
describe('decimalSeparator display', () => {
|
|
97
|
+
type Case = {
|
|
98
|
+
name: string;
|
|
99
|
+
input: string;
|
|
100
|
+
expected: string;
|
|
101
|
+
options?: TextFormatterOptions;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const commaSeparatorCases: Case[] = [
|
|
105
|
+
{
|
|
106
|
+
name: 'display dot decimal as comma',
|
|
107
|
+
input: '1.5',
|
|
108
|
+
expected: '1,5',
|
|
109
|
+
options: { decimalSeparator: ',' },
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'display comma input as comma',
|
|
113
|
+
input: '1,5',
|
|
114
|
+
expected: '1,5',
|
|
115
|
+
options: { decimalSeparator: ',' },
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: 'comma decimal with space thousands',
|
|
119
|
+
input: '1234.5',
|
|
120
|
+
expected: '1 234,5',
|
|
121
|
+
options: { decimalSeparator: ',' },
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: 'preserve trailing separator while typing',
|
|
125
|
+
input: '12.',
|
|
126
|
+
expected: '12,',
|
|
127
|
+
options: { decimalSeparator: ',' },
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'no decimal part is unaffected',
|
|
131
|
+
input: '1234',
|
|
132
|
+
expected: '1 234',
|
|
133
|
+
options: { decimalSeparator: ',' },
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: 'integer-only mode is unaffected by comma separator',
|
|
137
|
+
input: '1.5',
|
|
138
|
+
expected: '15',
|
|
139
|
+
options: { decimalSeparator: ',', allowDecimals: false },
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'explicit dot separator matches default',
|
|
143
|
+
input: '1234.5',
|
|
144
|
+
expected: '1 234.5',
|
|
145
|
+
options: { decimalSeparator: '.' },
|
|
146
|
+
},
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
it.each(commaSeparatorCases)('$name', ({ input, expected, options }) => {
|
|
150
|
+
expect(textFormatter(input, options)).toBe(expected);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
96
154
|
describe('non-numeric character removal', () => {
|
|
97
155
|
type Case = { name: string; input: string; expected: string };
|
|
98
156
|
|
|
@@ -9,12 +9,60 @@ export type TextFormatterOptions = {
|
|
|
9
9
|
maxIntegerLength?: number;
|
|
10
10
|
/** Maximum length for decimal part (after decimal) */
|
|
11
11
|
maxDecimalLength?: number;
|
|
12
|
+
/**
|
|
13
|
+
* Character used to display the decimal separator.
|
|
14
|
+
* Input parsing always accepts both `.` and `,` regardless of this value.
|
|
15
|
+
*/
|
|
16
|
+
decimalSeparator?: '.' | ',';
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/** Keep only digits and dots; commas are treated as decimal separators. */
|
|
20
|
+
const sanitize = (value: string): string =>
|
|
21
|
+
value.replaceAll(',', '.').replaceAll(/[^\d.]/g, '');
|
|
22
|
+
|
|
23
|
+
/** Drop leading zeros, keeping one when it precedes a dot or stands alone (e.g. "0", "0.5"). */
|
|
24
|
+
const stripLeadingZeros = (value: string): string =>
|
|
25
|
+
value.replace(/^0+(?=\d)/, '');
|
|
26
|
+
|
|
27
|
+
/** Truncate to `max` characters. A `max` of 0 disables the limit. */
|
|
28
|
+
const limitIntegerLength = (value: string, max: number): string =>
|
|
29
|
+
max > 0 ? value.slice(0, max) : value;
|
|
30
|
+
|
|
31
|
+
const formatIntegerOnly = (value: string, maxIntegerLength: number): string =>
|
|
32
|
+
limitIntegerLength(value.replaceAll(/\D/g, ''), maxIntegerLength);
|
|
33
|
+
|
|
34
|
+
const formatDecimal = (
|
|
35
|
+
value: string,
|
|
36
|
+
maxIntegerLength: number,
|
|
37
|
+
maxDecimalLength: number,
|
|
38
|
+
): string => {
|
|
39
|
+
// A lone separator becomes "0." so the user can keep typing decimals.
|
|
40
|
+
if (value === '.') return '0.';
|
|
41
|
+
|
|
42
|
+
const firstDot = value.indexOf('.');
|
|
43
|
+
if (firstDot === -1) return limitIntegerLength(value, maxIntegerLength);
|
|
44
|
+
|
|
45
|
+
const integerPart =
|
|
46
|
+
limitIntegerLength(value.slice(0, firstDot), maxIntegerLength) || '0';
|
|
47
|
+
// Extra dots are ignored, e.g. the decimal part of "1.2.3" is "23".
|
|
48
|
+
const decimalPart = value
|
|
49
|
+
.slice(firstDot + 1)
|
|
50
|
+
.replaceAll('.', '')
|
|
51
|
+
.slice(0, maxDecimalLength);
|
|
52
|
+
|
|
53
|
+
if (decimalPart.length > 0) return `${integerPart}.${decimalPart}`;
|
|
54
|
+
// Preserve a trailing dot mid-typing (e.g. "12.").
|
|
55
|
+
return value.endsWith('.') ? `${integerPart}.` : integerPart;
|
|
12
56
|
};
|
|
13
57
|
|
|
14
58
|
/**
|
|
15
59
|
* Formats and validates numeric input text for amount inputs.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
60
|
+
*
|
|
61
|
+
* Parsing is always lenient: both `.` and `,` are accepted as the decimal
|
|
62
|
+
* separator on input, so the result stays correct regardless of the locale
|
|
63
|
+
* keyboard. The `decimalSeparator` option only controls how the result is
|
|
64
|
+
* displayed.
|
|
65
|
+
*
|
|
18
66
|
* @returns Formatted and cleaned numeric string
|
|
19
67
|
*
|
|
20
68
|
* @example
|
|
@@ -22,9 +70,11 @@ export type TextFormatterOptions = {
|
|
|
22
70
|
* textFormatter('.5') // '0.5'
|
|
23
71
|
* textFormatter('1.2.3') // '1.23'
|
|
24
72
|
* textFormatter('1,5') // '1.5'
|
|
73
|
+
* textFormatter('1,5', { decimalSeparator: ',' }) // '1,5'
|
|
25
74
|
* textFormatter('abc123') // '123'
|
|
26
75
|
* textFormatter('1000000', { thousandsSeparator: true }) // '1 000 000'
|
|
27
76
|
* textFormatter('1234.5678', { thousandsSeparator: true }) // '1 234.5678'
|
|
77
|
+
* textFormatter('1234.5', { decimalSeparator: ',' }) // '1 234,5'
|
|
28
78
|
* textFormatter('123456789012', { maxIntegerLength: 9 }) // '123456789'
|
|
29
79
|
* textFormatter('123.123456', { maxDecimalLength: 2 }) // '123.12'
|
|
30
80
|
*/
|
|
@@ -38,58 +88,20 @@ export function textFormatter(
|
|
|
38
88
|
thousandsSeparator = true,
|
|
39
89
|
maxIntegerLength = 9,
|
|
40
90
|
maxDecimalLength = 9,
|
|
91
|
+
decimalSeparator = '.',
|
|
41
92
|
} = options;
|
|
42
|
-
// Normalize input: convert comma to dot, remove non-digit/non-dot characters
|
|
43
|
-
const normalizedValue = value.replace(',', '.').replace(/[^\d.]/g, '');
|
|
44
|
-
let cleaned = normalizedValue;
|
|
45
|
-
|
|
46
|
-
// Remove leading zeros (except when followed by dot)
|
|
47
|
-
cleaned = cleaned.replace(/^0+(?=\d)/, '');
|
|
48
|
-
|
|
49
|
-
if (!allowDecimals) {
|
|
50
|
-
// Integer-only: remove any non-digit and apply max length
|
|
51
|
-
cleaned = cleaned.replace(/\D/g, '');
|
|
52
|
-
if (maxIntegerLength > 0 && cleaned.length > maxIntegerLength) {
|
|
53
|
-
cleaned = cleaned.slice(0, maxIntegerLength);
|
|
54
|
-
}
|
|
55
|
-
return thousandsSeparator ? formatThousands(cleaned) : cleaned;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Handle single dot input as "0."
|
|
59
|
-
if (cleaned === '.') {
|
|
60
|
-
cleaned = '0.';
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const firstDot = cleaned.indexOf('.');
|
|
64
|
-
if (firstDot === -1) {
|
|
65
|
-
// No decimal point, just apply integer length limit
|
|
66
|
-
if (maxIntegerLength > 0 && cleaned.length > maxIntegerLength) {
|
|
67
|
-
cleaned = cleaned.slice(0, maxIntegerLength);
|
|
68
|
-
}
|
|
69
|
-
} else {
|
|
70
|
-
// Split integer and decimal parts
|
|
71
|
-
let integerPart = cleaned.slice(0, firstDot);
|
|
72
|
-
let decimalPart = cleaned.slice(firstDot + 1).replace(/\./g, '');
|
|
73
93
|
|
|
74
|
-
|
|
75
|
-
if (maxIntegerLength > 0 && integerPart.length > maxIntegerLength) {
|
|
76
|
-
integerPart = integerPart.slice(0, maxIntegerLength);
|
|
77
|
-
}
|
|
78
|
-
decimalPart = decimalPart.slice(0, maxDecimalLength);
|
|
94
|
+
const sanitized = stripLeadingZeros(sanitize(value));
|
|
79
95
|
|
|
80
|
-
|
|
81
|
-
|
|
96
|
+
const cleaned = allowDecimals
|
|
97
|
+
? formatDecimal(sanitized, maxIntegerLength, maxDecimalLength)
|
|
98
|
+
: formatIntegerOnly(sanitized, maxIntegerLength);
|
|
82
99
|
|
|
83
|
-
|
|
84
|
-
const hasTrailingDot =
|
|
85
|
-
normalizedValue.endsWith('.') || cleaned.endsWith('.');
|
|
86
|
-
cleaned =
|
|
87
|
-
decimalPart.length > 0
|
|
88
|
-
? `${integerPart}.${decimalPart}`
|
|
89
|
-
: hasTrailingDot
|
|
90
|
-
? `${integerPart}.`
|
|
91
|
-
: integerPart;
|
|
92
|
-
}
|
|
100
|
+
const grouped = thousandsSeparator ? formatThousands(cleaned) : cleaned;
|
|
93
101
|
|
|
94
|
-
|
|
102
|
+
// formatThousands groups with spaces and keeps a single dot for the decimal,
|
|
103
|
+
// so localizing the separator only ever touches that one dot.
|
|
104
|
+
return decimalSeparator === '.'
|
|
105
|
+
? grouped
|
|
106
|
+
: grouped.replaceAll('.', decimalSeparator);
|
|
95
107
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './resolveProps';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { createPropsResolver } from './resolveProps';
|
|
3
|
+
|
|
4
|
+
describe('createPropsResolver', () => {
|
|
5
|
+
const resolveProps = createPropsResolver({
|
|
6
|
+
avatar: {
|
|
7
|
+
sm: { size: 'lg', appearance: 'red' },
|
|
8
|
+
md: { size: 'xl', appearance: 'red' },
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('resolves the config for a given context and size', () => {
|
|
13
|
+
expect(resolveProps('avatar', 'sm')).toEqual({
|
|
14
|
+
size: 'lg',
|
|
15
|
+
appearance: 'red',
|
|
16
|
+
});
|
|
17
|
+
expect(resolveProps('avatar', 'md')).toEqual({
|
|
18
|
+
size: 'xl',
|
|
19
|
+
appearance: 'red',
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type PropsMap<Variant extends string, Props> = Record<Variant, Props>;
|
|
2
|
+
export type ContextPropsMap = Record<string, PropsMap<string, unknown>>;
|
|
3
|
+
|
|
4
|
+
export function createPropsResolver<T extends ContextPropsMap>(config: T) {
|
|
5
|
+
return function resolveProps<
|
|
6
|
+
Context extends keyof T,
|
|
7
|
+
Variant extends keyof T[Context],
|
|
8
|
+
>(context: Context, variant: Variant) {
|
|
9
|
+
return config[context][variant];
|
|
10
|
+
};
|
|
11
|
+
}
|