@milaboratories/uikit 2.10.45 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +19 -19
- package/.turbo/turbo-formatter$colon$check.log +2 -2
- package/.turbo/turbo-linter$colon$check.log +2 -2
- package/.turbo/turbo-types$colon$check.log +1 -1
- package/CHANGELOG.md +18 -0
- package/dist/components/PlNumberField/PlNumberField.js.map +1 -1
- package/dist/components/PlNumberField/PlNumberField.vue.d.ts +45 -75
- package/dist/components/PlNumberField/PlNumberField.vue.d.ts.map +1 -1
- package/dist/components/PlNumberField/PlNumberField.vue2.js +129 -121
- package/dist/components/PlNumberField/PlNumberField.vue2.js.map +1 -1
- package/dist/components/PlNumberField/__test__/PlNumberField.spec.d.ts.map +1 -0
- package/dist/components/PlNumberField/__test__/parseNumber.spec.d.ts +2 -0
- package/dist/components/PlNumberField/__test__/parseNumber.spec.d.ts.map +1 -0
- package/dist/components/PlNumberField/parseNumber.d.ts +56 -7
- package/dist/components/PlNumberField/parseNumber.d.ts.map +1 -1
- package/dist/components/PlNumberField/parseNumber.js +40 -56
- package/dist/components/PlNumberField/parseNumber.js.map +1 -1
- package/dist/components/PlNumberField/pl-number-field.css +1 -1
- package/dist/components/PlSearchField/PlSearchField.js.map +1 -1
- package/dist/components/PlSearchField/PlSearchField.style.js.map +1 -1
- package/dist/components/PlSearchField/PlSearchField.vue.d.ts +20 -32
- package/dist/components/PlSearchField/PlSearchField.vue.d.ts.map +1 -1
- package/dist/components/PlSearchField/PlSearchField.vue2.js +4 -2
- package/dist/components/PlSearchField/PlSearchField.vue2.js.map +1 -1
- package/dist/components/PlTextField/PlTextField.js.map +1 -1
- package/dist/components/PlTextField/PlTextField.vue.d.ts +46 -118
- package/dist/components/PlTextField/PlTextField.vue.d.ts.map +1 -1
- package/dist/components/PlTextField/PlTextField.vue2.js +61 -58
- package/dist/components/PlTextField/PlTextField.vue2.js.map +1 -1
- package/package.json +5 -5
- package/src/components/PlNumberField/PlNumberField.vue +151 -143
- package/src/components/PlNumberField/__test__/PlNumberField.spec.ts +296 -0
- package/src/components/PlNumberField/__test__/parseNumber.spec.ts +204 -0
- package/src/components/PlNumberField/parseNumber.ts +125 -98
- package/src/components/PlNumberField/pl-number-field.scss +17 -4
- package/src/components/PlSearchField/PlSearchField.vue +8 -4
- package/src/components/PlTextField/PlTextField.vue +37 -49
- package/src/components/PlTextField/__tests__/TextField.spec.ts +2 -2
- package/dist/components/PlNumberField/__tests__/PlNumberField.spec.d.ts.map +0 -1
- package/src/components/PlNumberField/__tests__/PlNumberField.spec.ts +0 -182
- /package/dist/components/PlNumberField/{__tests__ → __test__}/PlNumberField.spec.d.ts +0 -0
|
@@ -1,169 +1,177 @@
|
|
|
1
1
|
import e from "../PlTooltip/PlTooltip.js";
|
|
2
2
|
import "../PlTooltip/index.js";
|
|
3
|
-
import t from "
|
|
4
|
-
import
|
|
3
|
+
import t from "../PlIcon16/PlIcon16.js";
|
|
4
|
+
import n from "../../utils/DoubleContour.js";
|
|
5
|
+
import { useLabelNotch as r } from "../../utils/useLabelNotch.js";
|
|
6
|
+
import "../PlIcon16/index.js";
|
|
5
7
|
import './pl-number-field.css';/* empty css */
|
|
6
|
-
import {
|
|
7
|
-
import { computed as
|
|
8
|
-
var
|
|
8
|
+
import { numberToDecimalString as i, tryParseNumber as a, validateNumber as o } from "./parseNumber.js";
|
|
9
|
+
import { computed as s, createBlock as c, createCommentVNode as l, createElementBlock as u, createElementVNode as d, createTextVNode as f, createVNode as p, defineComponent as m, mergeModels as h, normalizeClass as g, openBlock as _, ref as v, renderSlot as y, toDisplayString as b, unref as x, useModel as S, useSlots as C, watch as w, withCtx as T, withModifiers as E } from "vue";
|
|
10
|
+
var D = { class: "pl-number-field__main-wrapper d-flex" }, O = { class: "pl-number-field__wrapper flex-grow d-flex flex-align-center" }, k = {
|
|
9
11
|
key: 0,
|
|
10
12
|
class: "text-description"
|
|
11
|
-
},
|
|
13
|
+
}, A = [
|
|
14
|
+
"value",
|
|
15
|
+
"disabled",
|
|
16
|
+
"placeholder"
|
|
17
|
+
], j = {
|
|
12
18
|
key: 0,
|
|
13
19
|
class: "pl-number-field__error"
|
|
14
|
-
},
|
|
20
|
+
}, M = /* @__PURE__ */ m({
|
|
15
21
|
name: "PlNumberField",
|
|
16
|
-
props: /* @__PURE__ */
|
|
17
|
-
|
|
18
|
-
label: {
|
|
19
|
-
placeholder: {
|
|
22
|
+
props: /* @__PURE__ */ h({
|
|
23
|
+
required: {},
|
|
24
|
+
label: {},
|
|
25
|
+
placeholder: {},
|
|
20
26
|
step: { default: 1 },
|
|
21
|
-
minValue: {
|
|
22
|
-
maxValue: {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
validate: {
|
|
30
|
-
type: Function,
|
|
31
|
-
default: void 0
|
|
32
|
-
},
|
|
33
|
-
groupPosition: { default: void 0 }
|
|
27
|
+
minValue: {},
|
|
28
|
+
maxValue: {},
|
|
29
|
+
disabled: { type: Boolean },
|
|
30
|
+
disableSteps: { type: Boolean },
|
|
31
|
+
errorMessage: {},
|
|
32
|
+
validate: {},
|
|
33
|
+
clearable: {},
|
|
34
|
+
groupPosition: {}
|
|
34
35
|
}, {
|
|
35
36
|
modelValue: { required: !0 },
|
|
36
37
|
modelModifiers: {}
|
|
37
38
|
}),
|
|
38
|
-
emits: [
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
(
|
|
39
|
+
emits: /* @__PURE__ */ h([
|
|
40
|
+
"blur",
|
|
41
|
+
"focus",
|
|
42
|
+
"enter"
|
|
43
|
+
], ["update:modelValue"]),
|
|
44
|
+
setup(m, { emit: h }) {
|
|
45
|
+
let M = S(m, "modelValue"), N = h, P = m, F = C(), I = v();
|
|
46
|
+
r(I);
|
|
47
|
+
let L = v(i(M.value));
|
|
48
|
+
w(M, (e) => {
|
|
49
|
+
a(L.value).value !== e && (L.value = i(e));
|
|
49
50
|
});
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
let t = r(f, e);
|
|
56
|
-
F.value = t.cleanInput, t.error || f.updateOnEnterOrClickOutside ? M.value.value = t.cleanInput : k.value = t.value;
|
|
57
|
-
}
|
|
58
|
-
}), R = h(!1);
|
|
59
|
-
function z() {
|
|
60
|
-
P.value.error === void 0 && (k.value = P.value.value);
|
|
51
|
+
function R(e) {
|
|
52
|
+
let t = e.target;
|
|
53
|
+
L.value = t.value;
|
|
54
|
+
let n = a(t.value);
|
|
55
|
+
n.value !== void 0 && (M.value = n.value);
|
|
61
56
|
}
|
|
62
|
-
|
|
63
|
-
let e =
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
else if (f.validate && t.value !== void 0) {
|
|
68
|
-
let n = f.validate(t.value);
|
|
69
|
-
n && e.push(n);
|
|
70
|
-
}
|
|
71
|
-
return e = [...e], e.join(" ");
|
|
72
|
-
}), V = i(() => {
|
|
73
|
-
let e = P.value;
|
|
74
|
-
return f.maxValue !== void 0 && e.value !== void 0 ? e.value >= f.maxValue : !1;
|
|
75
|
-
}), H = i(() => {
|
|
76
|
-
let e = P.value;
|
|
77
|
-
return f.minValue !== void 0 && e.value !== void 0 ? e.value <= f.minValue : !1;
|
|
78
|
-
}), U = i(() => 10 ** (f.step.toString().split(".").at(1)?.length ?? 0));
|
|
79
|
-
function W() {
|
|
80
|
-
let e = P.value.value;
|
|
81
|
-
if (!V.value) {
|
|
82
|
-
let t;
|
|
83
|
-
t = e === void 0 ? f.minValue ? f.minValue : 0 : ((e || 0) * U.value + f.step * U.value) / U.value, k.value = f.maxValue === void 0 ? t : Math.min(f.maxValue, t);
|
|
57
|
+
function z() {
|
|
58
|
+
let e = L.value.trim(), t = a(e);
|
|
59
|
+
if (e === "" || t.value === void 0 && t.error === void 0) {
|
|
60
|
+
L.value = "", P.required || (M.value = void 0);
|
|
61
|
+
return;
|
|
84
62
|
}
|
|
63
|
+
t.value !== void 0 && (M.value = t.value, L.value = i(t.value));
|
|
85
64
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
65
|
+
let B = s(() => {
|
|
66
|
+
if (P.errorMessage) return P.errorMessage;
|
|
67
|
+
let e = a(L.value);
|
|
68
|
+
if (e.error) return e.error;
|
|
69
|
+
if (e.value !== void 0) return o(e.value, P);
|
|
70
|
+
if (P.required && L.value.trim() === "") return "Value is required";
|
|
71
|
+
}), V = s(() => P.clearable && (M.value !== void 0 || L.value.trim() !== "") && !P.disabled);
|
|
72
|
+
function H() {
|
|
73
|
+
typeof P.clearable == "function" ? (M.value = P.clearable(), L.value = i(M.value)) : (M.value = void 0, L.value = "");
|
|
74
|
+
}
|
|
75
|
+
let U = s(() => B.value ? !0 : P.maxValue !== void 0 && M.value !== void 0 && M.value >= P.maxValue), W = s(() => B.value ? !0 : P.minValue !== void 0 && M.value !== void 0 && M.value <= P.minValue), G = s(() => 10 ** (P.step.toString().split(".").at(1)?.length ?? 0));
|
|
76
|
+
function K() {
|
|
77
|
+
if (U.value) return;
|
|
78
|
+
let e;
|
|
79
|
+
e = M.value === void 0 ? P.minValue ?? 0 : ((M.value || 0) * G.value + P.step * G.value) / G.value, M.value = P.maxValue === void 0 ? e : Math.min(P.maxValue, e);
|
|
80
|
+
}
|
|
81
|
+
function q() {
|
|
82
|
+
if (W.value) return;
|
|
83
|
+
let e;
|
|
84
|
+
e = M.value === void 0 ? 0 : ((M.value || 0) * G.value - P.step * G.value) / G.value, M.value = P.minValue === void 0 ? e : Math.max(P.minValue, e);
|
|
85
|
+
}
|
|
86
|
+
function J() {
|
|
87
|
+
z(), N("blur", M.value);
|
|
92
88
|
}
|
|
93
|
-
function
|
|
94
|
-
|
|
89
|
+
function Y() {
|
|
90
|
+
N("focus", M.value);
|
|
95
91
|
}
|
|
96
|
-
|
|
92
|
+
function X(e) {
|
|
93
|
+
e.code === "Enter" && (z(), N("enter", M.value)), ["ArrowDown", "ArrowUp"].includes(e.code) && e.preventDefault(), !P.disableSteps && e.code === "ArrowUp" && K(), !P.disableSteps && e.code === "ArrowDown" && q();
|
|
94
|
+
}
|
|
95
|
+
function Z(e) {
|
|
97
96
|
e.detail > 1 && e.preventDefault();
|
|
98
|
-
}
|
|
99
|
-
return (
|
|
97
|
+
}
|
|
98
|
+
return (r, i) => (_(), u("div", {
|
|
100
99
|
ref_key: "rootRef",
|
|
101
|
-
ref:
|
|
102
|
-
class:
|
|
103
|
-
error: !!B.value
|
|
104
|
-
disabled:
|
|
100
|
+
ref: I,
|
|
101
|
+
class: g([{
|
|
102
|
+
error: !!B.value,
|
|
103
|
+
disabled: m.disabled
|
|
105
104
|
}, "pl-number-field d-flex-column"]),
|
|
106
|
-
onKeydown:
|
|
107
|
-
}, [
|
|
108
|
-
|
|
105
|
+
onKeydown: X
|
|
106
|
+
}, [d("div", D, [
|
|
107
|
+
p(n, {
|
|
109
108
|
class: "pl-number-field__contour",
|
|
110
|
-
"group-position":
|
|
109
|
+
"group-position": m.groupPosition
|
|
111
110
|
}, null, 8, ["group-position"]),
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
111
|
+
d("div", O, [
|
|
112
|
+
m.label ? (_(), u("label", k, [f(b(m.label) + " ", 1), x(F).tooltip ? (_(), c(x(e), {
|
|
113
|
+
key: 0,
|
|
114
|
+
class: "info",
|
|
115
|
+
position: "top"
|
|
116
|
+
}, {
|
|
117
|
+
tooltip: T(() => [y(r.$slots, "tooltip")]),
|
|
118
|
+
_: 3
|
|
119
|
+
})) : l("", !0)])) : l("", !0),
|
|
120
|
+
d("input", {
|
|
121
|
+
ref: "inputRef",
|
|
122
|
+
type: "text",
|
|
123
|
+
inputmode: "numeric",
|
|
124
|
+
value: L.value,
|
|
125
|
+
disabled: m.disabled,
|
|
126
|
+
placeholder: m.placeholder,
|
|
127
|
+
class: "text-s flex-grow",
|
|
128
|
+
onInput: R,
|
|
129
|
+
onFocusout: J,
|
|
130
|
+
onFocusin: Y
|
|
131
|
+
}, null, 40, A),
|
|
132
|
+
V.value ? (_(), c(x(t), {
|
|
133
|
+
key: 1,
|
|
134
|
+
class: "pl-number-field__clearable",
|
|
135
|
+
name: "delete-clear",
|
|
136
|
+
onClick: E(H, ["stop"])
|
|
137
|
+
})) : l("", !0)
|
|
138
|
+
]),
|
|
139
|
+
P.disableSteps ? l("", !0) : (_(), u("div", {
|
|
132
140
|
key: 0,
|
|
133
141
|
class: "pl-number-field__icons d-flex-column",
|
|
134
|
-
onMousedown:
|
|
135
|
-
}, [
|
|
136
|
-
class:
|
|
137
|
-
onClick:
|
|
138
|
-
}, [...
|
|
142
|
+
onMousedown: Z
|
|
143
|
+
}, [d("div", {
|
|
144
|
+
class: g([{ disabled: U.value }, "pl-number-field__icon d-flex flex-justify-center uc-pointer flex-grow flex-align-center"]),
|
|
145
|
+
onClick: K
|
|
146
|
+
}, [...i[0] ||= [d("svg", {
|
|
139
147
|
xmlns: "http://www.w3.org/2000/svg",
|
|
140
148
|
width: "16",
|
|
141
149
|
height: "16",
|
|
142
150
|
viewBox: "0 0 16 16",
|
|
143
151
|
fill: "none"
|
|
144
|
-
}, [
|
|
152
|
+
}, [d("path", {
|
|
145
153
|
"fill-rule": "evenodd",
|
|
146
154
|
"clip-rule": "evenodd",
|
|
147
155
|
d: "M8 4.93933L13.5303 10.4697L12.4697 11.5303L8 7.06065L3.53033 11.5303L2.46967 10.4697L8 4.93933Z",
|
|
148
156
|
fill: "#110529"
|
|
149
|
-
})], -1)]], 2),
|
|
150
|
-
class:
|
|
151
|
-
onClick:
|
|
152
|
-
}, [...
|
|
157
|
+
})], -1)]], 2), d("div", {
|
|
158
|
+
class: g([{ disabled: W.value }, "pl-number-field__icon d-flex flex-justify-center uc-pointer flex-grow flex-align-center"]),
|
|
159
|
+
onClick: q
|
|
160
|
+
}, [...i[1] ||= [d("svg", {
|
|
153
161
|
xmlns: "http://www.w3.org/2000/svg",
|
|
154
162
|
width: "16",
|
|
155
163
|
height: "16",
|
|
156
164
|
viewBox: "0 0 16 16",
|
|
157
165
|
fill: "none"
|
|
158
|
-
}, [
|
|
166
|
+
}, [d("path", {
|
|
159
167
|
"fill-rule": "evenodd",
|
|
160
168
|
"clip-rule": "evenodd",
|
|
161
169
|
d: "M2.46967 6.53033L3.53033 5.46967L8 9.93934L12.4697 5.46967L13.5303 6.53033L8 12.0607L2.46967 6.53033Z",
|
|
162
170
|
fill: "#110529"
|
|
163
|
-
})], -1)]], 2)], 32))
|
|
164
|
-
]), B.value
|
|
171
|
+
})], -1)]], 2)], 32))
|
|
172
|
+
]), B.value ? (_(), u("div", j, b(B.value), 1)) : l("", !0)], 34));
|
|
165
173
|
}
|
|
166
174
|
});
|
|
167
|
-
export {
|
|
175
|
+
export { M as default };
|
|
168
176
|
|
|
169
177
|
//# sourceMappingURL=PlNumberField.vue2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlNumberField.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/PlNumberField/PlNumberField.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Number input field with increment/decrement buttons, validation, and min/max constraints.\n *\n * @example\n * <PlNumberField v-model=\"price\" :step=\"0.01\" :min-value=\"0\" label=\"Price\" />\n *\n * @example\n * <PlNumberField\n * v-model=\"evenNumber\"\n * :validate=\"(v) => v % 2 !== 0 ? 'Number must be even' : undefined\"\n * :update-on-enter-or-click-outside=\"true\"\n * label=\"Even Number\"\n * />\n */\nexport default {\n name: \"PlNumberField\",\n};\n</script>\n\n<script setup lang=\"ts\">\nimport \"./pl-number-field.scss\";\nimport DoubleContour from \"../../utils/DoubleContour.vue\";\nimport { useLabelNotch } from \"../../utils/useLabelNotch\";\nimport { computed, ref, useSlots, watch } from \"vue\";\nimport { PlTooltip } from \"../PlTooltip\";\nimport { parseNumber } from \"./parseNumber\";\n\nconst props = withDefaults(\n defineProps<{\n /** Input is disabled if true */\n disabled?: boolean;\n /** Label on the top border of the field, empty by default */\n label?: string;\n /** Input placeholder, empty by default */\n placeholder?: string;\n /** Step for increment/decrement buttons, 1 by default */\n step?: number;\n /** If defined - show an error if value is lower */\n minValue?: number;\n /** If defined - show an error if value is higher */\n maxValue?: number;\n /** If false - remove buttons on the right */\n useIncrementButtons?: boolean;\n /** If true - changes do not apply immediately, they apply only by removing focus from the input (by click enter or by click outside) */\n updateOnEnterOrClickOutside?: boolean;\n /** Error message that shows always when it's provided, without other checks */\n errorMessage?: string;\n /** Additional validity check for input value that must return an error text if failed */\n validate?: (v: number) => string | undefined;\n /** Makes some of corners not rounded */\n groupPosition?:\n | \"top\"\n | \"bottom\"\n | \"left\"\n | \"right\"\n | \"top-left\"\n | \"top-right\"\n | \"bottom-left\"\n | \"bottom-right\"\n | \"middle\";\n }>(),\n {\n step: 1,\n label: undefined,\n placeholder: undefined,\n minValue: undefined,\n maxValue: undefined,\n useIncrementButtons: true,\n updateOnEnter: false,\n errorMessage: undefined,\n validate: undefined,\n groupPosition: undefined,\n },\n);\n\nconst modelValue = defineModel<number | undefined>({ required: true });\n\nconst slots = useSlots();\n\nconst rootRef = ref<HTMLElement>();\nconst inputRef = ref<HTMLInputElement>();\n\nuseLabelNotch(rootRef);\n\nfunction modelToString(v: number | undefined) {\n return v === undefined ? \"\" : String(+v); // (+v) to avoid staying in input non-number values if they are provided in model\n}\n\nconst parsedResult = computed(() => parseNumber(props, inputValue.value));\n\nconst cachedValue = ref<string | undefined>(undefined);\n\nconst resetCachedValue = () => (cachedValue.value = undefined);\n\nwatch(modelValue, (n) => {\n const r = parsedResult.value;\n if (r.error || n !== r.value) {\n resetCachedValue();\n }\n});\n\nconst inputValue = computed({\n get() {\n return cachedValue.value ?? modelToString(modelValue.value);\n },\n set(nextValue: string) {\n const r = parseNumber(props, nextValue);\n\n cachedValue.value = r.cleanInput;\n\n if (r.error || props.updateOnEnterOrClickOutside) {\n inputRef.value!.value = r.cleanInput;\n } else {\n modelValue.value = r.value;\n }\n },\n});\n\nconst focused = ref(false);\n\nfunction applyChanges() {\n if (parsedResult.value.error === undefined) {\n modelValue.value = parsedResult.value.value;\n }\n}\n\nconst errors = computed(() => {\n let ers: string[] = [];\n\n if (props.errorMessage) {\n ers.push(props.errorMessage);\n }\n\n const r = parsedResult.value;\n\n if (r.error) {\n ers.push(r.error.message);\n } else if (props.validate && r.value !== undefined) {\n const error = props.validate(r.value);\n if (error) {\n ers.push(error);\n }\n }\n\n ers = [...ers];\n\n return ers.join(\" \");\n});\n\nconst isIncrementDisabled = computed(() => {\n const r = parsedResult.value;\n\n if (props.maxValue !== undefined && r.value !== undefined) {\n return r.value >= props.maxValue;\n }\n\n return false;\n});\n\nconst isDecrementDisabled = computed(() => {\n const r = parsedResult.value;\n\n if (props.minValue !== undefined && r.value !== undefined) {\n return r.value <= props.minValue;\n }\n\n return false;\n});\n\nconst multiplier = computed(() => 10 ** (props.step.toString().split(\".\").at(1)?.length ?? 0));\n\nfunction increment() {\n const r = parsedResult.value;\n\n const parsedValue = r.value;\n\n if (!isIncrementDisabled.value) {\n let nV;\n if (parsedValue === undefined) {\n nV = props.minValue ? props.minValue : 0;\n } else {\n nV =\n ((parsedValue || 0) * multiplier.value + props.step * multiplier.value) / multiplier.value;\n }\n modelValue.value = props.maxValue !== undefined ? Math.min(props.maxValue, nV) : nV;\n }\n}\n\nfunction decrement() {\n const r = parsedResult.value;\n\n const parsedValue = r.value;\n\n if (!isDecrementDisabled.value) {\n let nV;\n if (parsedValue === undefined) {\n nV = 0;\n } else {\n nV =\n ((parsedValue || 0) * multiplier.value - props.step * multiplier.value) / multiplier.value;\n }\n modelValue.value = props.minValue !== undefined ? Math.max(props.minValue, nV) : nV;\n }\n}\n\nfunction handleKeyPress(e: { code: string; preventDefault(): void }) {\n if (props.updateOnEnterOrClickOutside) {\n if (e.code === \"Escape\") {\n inputValue.value = modelToString(modelValue.value);\n inputRef.value?.blur();\n }\n if (e.code === \"Enter\") {\n inputRef.value?.blur();\n }\n }\n\n if (e.code === \"Enter\") {\n inputValue.value = String(modelValue.value); // to make .1 => 0.1, 10.00 => 10, remove leading zeros etc\n }\n\n if ([\"ArrowDown\", \"ArrowUp\"].includes(e.code)) {\n e.preventDefault();\n }\n\n if (props.useIncrementButtons && e.code === \"ArrowUp\") {\n increment();\n }\n\n if (props.useIncrementButtons && e.code === \"ArrowDown\") {\n decrement();\n }\n}\n\n// https://stackoverflow.com/questions/880512/prevent-text-selection-after-double-click#:~:text=If%20you%20encounter%20a%20situation,none%3B%20to%20the%20summary%20element.\n// this prevents selecting of more than input content in some cases,\n// but also disable selecting input content by double-click (useful feature)\nconst onMousedown = (ev: MouseEvent) => {\n if (ev.detail > 1) {\n ev.preventDefault();\n }\n};\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"{ error: !!errors.trim(), disabled: disabled }\"\n class=\"pl-number-field d-flex-column\"\n @keydown=\"handleKeyPress($event)\"\n >\n <div class=\"pl-number-field__main-wrapper d-flex\">\n <DoubleContour class=\"pl-number-field__contour\" :group-position=\"groupPosition\" />\n <div\n class=\"pl-number-field__wrapper flex-grow d-flex flex-align-center\"\n :class=\"{ withoutArrows: !useIncrementButtons }\"\n >\n <label v-if=\"label\" class=\"text-description\">\n {{ label }}\n <PlTooltip v-if=\"slots.tooltip\" class=\"info\" position=\"top\">\n <template #tooltip>\n <slot name=\"tooltip\" />\n </template>\n </PlTooltip>\n </label>\n <input\n ref=\"inputRef\"\n v-model=\"inputValue\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n class=\"text-s flex-grow\"\n @focusin=\"focused = true\"\n @focusout=\"\n focused = false;\n applyChanges();\n \"\n />\n </div>\n <div\n v-if=\"useIncrementButtons\"\n class=\"pl-number-field__icons d-flex-column\"\n @mousedown=\"onMousedown\"\n >\n <div\n :class=\"{ disabled: isIncrementDisabled }\"\n class=\"pl-number-field__icon d-flex flex-justify-center uc-pointer flex-grow flex-align-center\"\n @click=\"increment\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n fill-rule=\"evenodd\"\n clip-rule=\"evenodd\"\n d=\"M8 4.93933L13.5303 10.4697L12.4697 11.5303L8 7.06065L3.53033 11.5303L2.46967 10.4697L8 4.93933Z\"\n fill=\"#110529\"\n />\n </svg>\n </div>\n <div\n :class=\"{ disabled: isDecrementDisabled }\"\n class=\"pl-number-field__icon d-flex flex-justify-center uc-pointer flex-grow flex-align-center\"\n @click=\"decrement\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n fill-rule=\"evenodd\"\n clip-rule=\"evenodd\"\n d=\"M2.46967 6.53033L3.53033 5.46967L8 9.93934L12.4697 5.46967L13.5303 6.53033L8 12.0607L2.46967 6.53033Z\"\n fill=\"#110529\"\n />\n </svg>\n </div>\n </div>\n </div>\n <div v-if=\"errors.trim()\" class=\"pl-number-field__error\">\n {{ errors }}\n </div>\n </div>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PlNumberField.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/PlNumberField/PlNumberField.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Number input field with increment/decrement buttons, validation, and min/max constraints.\n *\n * @example\n * <PlNumberField v-model=\"price\" :step=\"0.01\" :min-value=\"0\" label=\"Price\" />\n *\n * @example\n * <PlNumberField\n * v-model=\"evenNumber\"\n * :validate=\"(v) => v % 2 !== 0 ? 'Number must be even' : undefined\"\n * label=\"Even Number\"\n * />\n */\nexport default {\n name: \"PlNumberField\",\n};\n</script>\n\n<script\n setup\n lang=\"ts\"\n generic=\"\n R extends true | false,\n V extends undefined | number,\n C extends Exclude<V, R extends true ? undefined : never>\n \"\n>\nimport \"./pl-number-field.scss\";\nimport DoubleContour from \"../../utils/DoubleContour.vue\";\nimport { useLabelNotch } from \"../../utils/useLabelNotch\";\nimport { computed, ref, useSlots, watch } from \"vue\";\nimport { PlTooltip } from \"../PlTooltip\";\nimport { PlIcon16 } from \"../PlIcon16\";\nimport { tryParseNumber, numberToDecimalString, validateNumber } from \"./parseNumber\";\n\nconst modelValue = defineModel<V>({ required: true });\n\nconst emit = defineEmits<{\n blur: [value: V];\n focus: [value: V];\n enter: [value: V];\n}>();\n\nconst props = withDefaults(\n defineProps<{\n /** If `true`, the field is required and will show an error if left empty. */\n required?: R;\n /** Label on the top border of the field, empty by default */\n label?: string;\n /** Input placeholder, empty by default */\n placeholder?: string;\n /** Step for increment/decrement buttons, 1 by default */\n step?: number;\n /** If defined - show an error if value is lower */\n minValue?: number;\n /** If defined - show an error if value is higher */\n maxValue?: number;\n /** Input is disabled if true */\n disabled?: boolean;\n /** If true - remove buttons on the right */\n disableSteps?: boolean;\n /** Error message that shows always when it's provided, without other checks */\n errorMessage?: string;\n /** Additional validity check for input value that must return an error text if failed */\n validate?: (v: number) => string | undefined;\n /** If `true`, shows a clear button that resets value to `undefined`. If a function, calls it to get the reset value. */\n clearable?: (R extends true ? never : boolean) | (() => C);\n /** Makes some of corners not rounded */\n groupPosition?:\n | \"top\"\n | \"bottom\"\n | \"left\"\n | \"right\"\n | \"top-left\"\n | \"top-right\"\n | \"bottom-left\"\n | \"bottom-right\"\n | \"middle\";\n }>(),\n { step: 1 },\n);\n\nconst slots = useSlots();\n\nconst rootRef = ref<HTMLElement>();\n\nuseLabelNotch(rootRef);\n\nconst displayText = ref(numberToDecimalString(modelValue.value));\n\n// Sync display when model changes externally (parent, increment/decrement).\n// Skip if the current input already represents the same value.\nwatch(modelValue, (newVal) => {\n const parsed = tryParseNumber(displayText.value);\n if (parsed.value === newVal) return;\n displayText.value = numberToDecimalString(newVal);\n});\n\nfunction handleInput(event: Event) {\n const input = event.target as HTMLInputElement;\n displayText.value = input.value;\n\n const result = tryParseNumber(input.value);\n if (result.value !== undefined) {\n modelValue.value = result.value as V;\n }\n}\n\n// On Enter or blur: if parseable, replace display with canonical decimal string.\n// Converts exponential (1e-5) to plain form (0.00001).\nfunction commitValue() {\n const text = displayText.value.trim();\n const result = tryParseNumber(text);\n\n // Empty or partial (-, ., -.) → clear display and reset model for non-required fields\n if (text === \"\" || (result.value === undefined && result.error === undefined)) {\n displayText.value = \"\";\n if (!props.required) {\n modelValue.value = undefined as V;\n }\n return;\n }\n\n if (result.value !== undefined) {\n modelValue.value = result.value as V;\n displayText.value = numberToDecimalString(result.value);\n }\n}\n\nconst error = computed(() => {\n if (props.errorMessage) return props.errorMessage;\n\n const result = tryParseNumber(displayText.value);\n if (result.error) return result.error;\n if (result.value !== undefined) {\n return validateNumber(result.value, props);\n }\n\n if (props.required && displayText.value.trim() === \"\") {\n return \"Value is required\";\n }\n\n return undefined;\n});\n\nconst canShowClearable = computed(\n () =>\n props.clearable &&\n (modelValue.value !== undefined || displayText.value.trim() !== \"\") &&\n !props.disabled,\n);\n\nfunction clear() {\n if (typeof props.clearable === \"function\") {\n modelValue.value = props.clearable();\n displayText.value = numberToDecimalString(modelValue.value);\n } else {\n modelValue.value = undefined as V;\n displayText.value = \"\";\n }\n}\n\nconst isIncrementDisabled = computed(() => {\n if (error.value) return true;\n return (\n props.maxValue !== undefined &&\n modelValue.value !== undefined &&\n modelValue.value >= props.maxValue\n );\n});\n\nconst isDecrementDisabled = computed(() => {\n if (error.value) return true;\n return (\n props.minValue !== undefined &&\n modelValue.value !== undefined &&\n modelValue.value <= props.minValue\n );\n});\n\nconst multiplier = computed(() => 10 ** (props.step.toString().split(\".\").at(1)?.length ?? 0));\n\nfunction increment() {\n if (isIncrementDisabled.value) return;\n\n let nV: number;\n if (modelValue.value === undefined) {\n nV = props.minValue ?? 0;\n } else {\n nV =\n ((modelValue.value || 0) * multiplier.value + props.step * multiplier.value) /\n multiplier.value;\n }\n\n modelValue.value = (props.maxValue !== undefined ? Math.min(props.maxValue, nV) : nV) as V;\n}\n\nfunction decrement() {\n if (isDecrementDisabled.value) return;\n\n let nV: number;\n if (modelValue.value === undefined) {\n nV = 0;\n } else {\n nV =\n ((modelValue.value || 0) * multiplier.value - props.step * multiplier.value) /\n multiplier.value;\n }\n\n modelValue.value = (props.minValue !== undefined ? Math.max(props.minValue, nV) : nV) as V;\n}\n\nfunction handleBlur() {\n commitValue();\n emit(\"blur\", modelValue.value);\n}\n\nfunction handleFocus() {\n emit(\"focus\", modelValue.value);\n}\n\nfunction handleKeyDown(e: KeyboardEvent) {\n if (e.code === \"Enter\") {\n commitValue();\n emit(\"enter\", modelValue.value);\n }\n\n if ([\"ArrowDown\", \"ArrowUp\"].includes(e.code)) {\n e.preventDefault();\n }\n\n if (!props.disableSteps && e.code === \"ArrowUp\") {\n increment();\n }\n\n if (!props.disableSteps && e.code === \"ArrowDown\") {\n decrement();\n }\n}\n\n// Prevent selecting beyond input content on triple-click etc.\nfunction handleMousedown(ev: MouseEvent) {\n if (ev.detail > 1) {\n ev.preventDefault();\n }\n}\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"{ error: !!error, disabled: disabled }\"\n class=\"pl-number-field d-flex-column\"\n @keydown=\"handleKeyDown\"\n >\n <div class=\"pl-number-field__main-wrapper d-flex\">\n <DoubleContour class=\"pl-number-field__contour\" :group-position=\"groupPosition\" />\n <div class=\"pl-number-field__wrapper flex-grow d-flex flex-align-center\">\n <label v-if=\"label\" class=\"text-description\">\n {{ label }}\n <PlTooltip v-if=\"slots.tooltip\" class=\"info\" position=\"top\">\n <template #tooltip>\n <slot name=\"tooltip\" />\n </template>\n </PlTooltip>\n </label>\n <input\n ref=\"inputRef\"\n type=\"text\"\n inputmode=\"numeric\"\n :value=\"displayText\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n class=\"text-s flex-grow\"\n @input=\"handleInput\"\n @focusout=\"handleBlur\"\n @focusin=\"handleFocus\"\n />\n <PlIcon16\n v-if=\"canShowClearable\"\n class=\"pl-number-field__clearable\"\n name=\"delete-clear\"\n @click.stop=\"clear\"\n />\n </div>\n <div\n v-if=\"!props.disableSteps\"\n class=\"pl-number-field__icons d-flex-column\"\n @mousedown=\"handleMousedown\"\n >\n <div\n :class=\"{ disabled: isIncrementDisabled }\"\n class=\"pl-number-field__icon d-flex flex-justify-center uc-pointer flex-grow flex-align-center\"\n @click=\"increment\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n fill-rule=\"evenodd\"\n clip-rule=\"evenodd\"\n d=\"M8 4.93933L13.5303 10.4697L12.4697 11.5303L8 7.06065L3.53033 11.5303L2.46967 10.4697L8 4.93933Z\"\n fill=\"#110529\"\n />\n </svg>\n </div>\n <div\n :class=\"{ disabled: isDecrementDisabled }\"\n class=\"pl-number-field__icon d-flex flex-justify-center uc-pointer flex-grow flex-align-center\"\n @click=\"decrement\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n fill-rule=\"evenodd\"\n clip-rule=\"evenodd\"\n d=\"M2.46967 6.53033L3.53033 5.46967L8 9.93934L12.4697 5.46967L13.5303 6.53033L8 12.0607L2.46967 6.53033Z\"\n fill=\"#110529\"\n />\n </svg>\n </div>\n </div>\n </div>\n <div v-if=\"error\" class=\"pl-number-field__error\">\n {{ error }}\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;CAeE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;EAqBR,IAAM,IAAa,EAAc,GAAA,aAAoB,EAE/C,IAAO,GAMP,IAAQ,GAuCR,IAAQ,GAAU,EAElB,IAAU,GAAkB;AAElC,IAAc,EAAQ;EAEtB,IAAM,IAAc,EAAI,EAAsB,EAAW,MAAM,CAAC;AAIhE,IAAM,IAAa,MAAW;AACb,KAAe,EAAY,MAAM,CACrC,UAAU,MACrB,EAAY,QAAQ,EAAsB,EAAO;IACjD;EAEF,SAAS,EAAY,GAAc;GACjC,IAAM,IAAQ,EAAM;AACpB,KAAY,QAAQ,EAAM;GAE1B,IAAM,IAAS,EAAe,EAAM,MAAM;AAC1C,GAAI,EAAO,UAAU,KAAA,MACnB,EAAW,QAAQ,EAAO;;EAM9B,SAAS,IAAc;GACrB,IAAM,IAAO,EAAY,MAAM,MAAM,EAC/B,IAAS,EAAe,EAAK;AAGnC,OAAI,MAAS,MAAO,EAAO,UAAU,KAAA,KAAa,EAAO,UAAU,KAAA,GAAY;AAE7E,IADA,EAAY,QAAQ,IACf,EAAM,aACT,EAAW,QAAQ,KAAA;AAErB;;AAGF,GAAI,EAAO,UAAU,KAAA,MACnB,EAAW,QAAQ,EAAO,OAC1B,EAAY,QAAQ,EAAsB,EAAO,MAAM;;EAI3D,IAAM,IAAQ,QAAe;AAC3B,OAAI,EAAM,aAAc,QAAO,EAAM;GAErC,IAAM,IAAS,EAAe,EAAY,MAAM;AAChD,OAAI,EAAO,MAAO,QAAO,EAAO;AAChC,OAAI,EAAO,UAAU,KAAA,EACnB,QAAO,EAAe,EAAO,OAAO,EAAM;AAG5C,OAAI,EAAM,YAAY,EAAY,MAAM,MAAM,KAAK,GACjD,QAAO;IAIT,EAEI,IAAmB,QAErB,EAAM,cACL,EAAW,UAAU,KAAA,KAAa,EAAY,MAAM,MAAM,KAAK,OAChE,CAAC,EAAM,SACV;EAED,SAAS,IAAQ;AACf,GAAI,OAAO,EAAM,aAAc,cAC7B,EAAW,QAAQ,EAAM,WAAW,EACpC,EAAY,QAAQ,EAAsB,EAAW,MAAM,KAE3D,EAAW,QAAQ,KAAA,GACnB,EAAY,QAAQ;;EAIxB,IAAM,IAAsB,QACtB,EAAM,QAAc,KAEtB,EAAM,aAAa,KAAA,KACnB,EAAW,UAAU,KAAA,KACrB,EAAW,SAAS,EAAM,SAE5B,EAEI,IAAsB,QACtB,EAAM,QAAc,KAEtB,EAAM,aAAa,KAAA,KACnB,EAAW,UAAU,KAAA,KACrB,EAAW,SAAS,EAAM,SAE5B,EAEI,IAAa,QAAe,OAAO,EAAM,KAAK,UAAU,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,EAAE,UAAU,GAAG;EAE9F,SAAS,IAAY;AACnB,OAAI,EAAoB,MAAO;GAE/B,IAAI;AASJ,GARA,AAGE,IAHE,EAAW,UAAU,KAAA,IAClB,EAAM,YAAY,MAGnB,EAAW,SAAS,KAAK,EAAW,QAAQ,EAAM,OAAO,EAAW,SACtE,EAAW,OAGf,EAAW,QAAS,EAAM,aAAa,KAAA,IAA2C,IAA/B,KAAK,IAAI,EAAM,UAAU,EAAG;;EAGjF,SAAS,IAAY;AACnB,OAAI,EAAoB,MAAO;GAE/B,IAAI;AASJ,GARA,AAGE,IAHE,EAAW,UAAU,KAAA,IAClB,MAGD,EAAW,SAAS,KAAK,EAAW,QAAQ,EAAM,OAAO,EAAW,SACtE,EAAW,OAGf,EAAW,QAAS,EAAM,aAAa,KAAA,IAA2C,IAA/B,KAAK,IAAI,EAAM,UAAU,EAAG;;EAGjF,SAAS,IAAa;AAEpB,GADA,GAAa,EACb,EAAK,QAAQ,EAAW,MAAM;;EAGhC,SAAS,IAAc;AACrB,KAAK,SAAS,EAAW,MAAM;;EAGjC,SAAS,EAAc,GAAkB;AAcvC,GAbI,EAAE,SAAS,YACb,GAAa,EACb,EAAK,SAAS,EAAW,MAAM,GAG7B,CAAC,aAAa,UAAU,CAAC,SAAS,EAAE,KAAK,IAC3C,EAAE,gBAAgB,EAGhB,CAAC,EAAM,gBAAgB,EAAE,SAAS,aACpC,GAAW,EAGT,CAAC,EAAM,gBAAgB,EAAE,SAAS,eACpC,GAAW;;EAKf,SAAS,EAAgB,GAAgB;AACvC,GAAI,EAAG,SAAS,KACd,EAAG,gBAAgB;;yBAMrB,EAsFM,OAAA;YArFA;GAAJ,KAAI;GACH,OAAK,EAAA,CAAA;IAAA,OAAA,CAAA,CAAa,EAAA;IAAK,UAAY,EAAA;IAAQ,EACtC,gCAA+B,CAAA;GACpC,WAAS;MAEV,EA4EM,OA5EN,GA4EM;GA3EJ,EAAkF,GAAA;IAAnE,OAAM;IAA4B,kBAAgB,EAAA;;GACjE,EA2BM,OA3BN,GA2BM;IA1BS,EAAA,SAAA,GAAA,EAAb,EAOQ,SAPR,GAOQ,CAAA,EAAA,EANH,EAAA,MAAK,GAAG,KACX,EAAA,EAAiB,EAAA,EAAK,CAAC,WAAA,GAAA,EAAvB,EAIY,EAAA,EAAA,EAAA;;KAJoB,OAAM;KAAO,UAAS;;KACzC,SAAO,QACO,CAAvB,EAAuB,EAAA,QAAA,UAAA,CAAA,CAAA;;;IAI7B,EAWE,SAAA;KAVA,KAAI;KACJ,MAAK;KACL,WAAU;KACT,OAAO,EAAA;KACP,UAAU,EAAA;KACV,aAAa,EAAA;KACd,OAAM;KACL,SAAO;KACP,YAAU;KACV,WAAS;;IAGJ,EAAA,SAAA,GAAA,EADR,EAKE,EAAA,EAAA,EAAA;;KAHA,OAAM;KACN,MAAK;KACJ,SAAK,EAAO,GAAK,CAAA,OAAA,CAAA;;;GAIb,EAAM,4BAAA,GAAA,EADf,EA6CM,OAAA;;IA3CJ,OAAM;IACL,aAAW;OAEZ,EAmBM,OAAA;IAlBH,OAAK,EAAA,CAAA,EAAA,UAAc,EAAA,OAAmB,EACjC,0FAAyF,CAAA;IAC9F,SAAO;oBAER,EAaM,OAAA;IAZJ,OAAM;IACN,OAAM;IACN,QAAO;IACP,SAAQ;IACR,MAAK;OAEL,EAKE,QAAA;IAJA,aAAU;IACV,aAAU;IACV,GAAE;IACF,MAAK;mBAIX,EAmBM,OAAA;IAlBH,OAAK,EAAA,CAAA,EAAA,UAAc,EAAA,OAAmB,EACjC,0FAAyF,CAAA;IAC9F,SAAO;oBAER,EAaM,OAAA;IAZJ,OAAM;IACN,OAAM;IACN,QAAO;IACP,SAAQ;IACR,MAAK;OAEL,EAKE,QAAA;IAJA,aAAU;IACV,aAAU;IACV,GAAE;IACF,MAAK;;MAMJ,EAAA,SAAA,GAAA,EAAX,EAEM,OAFN,GAEM,EADD,EAAA,MAAK,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA,CAAA,EAAA,GAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlNumberField.spec.d.ts","sourceRoot":"","sources":["../../../../src/components/PlNumberField/__test__/PlNumberField.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseNumber.spec.d.ts","sourceRoot":"","sources":["../../../../src/components/PlNumberField/__test__/parseNumber.spec.ts"],"names":[],"mappings":""}
|
|
@@ -1,12 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Strict number parser. No locale guessing, no normalization.
|
|
3
|
+
* Non-canonical forms (leading zeros, trailing dots, etc.) are accepted —
|
|
4
|
+
* formatting to canonical form happens on blur/enter in the component.
|
|
5
|
+
*
|
|
6
|
+
* Input/output table (covers ~90% of real cases):
|
|
7
|
+
*
|
|
8
|
+
* | Input | Result | Reason |
|
|
9
|
+
* |---------------------|---------------------------------|---------------------------------|
|
|
10
|
+
* | "" | {} | empty |
|
|
11
|
+
* | "-" | {} | partial | apply blur/enter format
|
|
12
|
+
* | "." | {} | partial | apply blur/enter format
|
|
13
|
+
* | "-." | {} | partial | apply blur/enter format
|
|
14
|
+
* | "123." | { value: 123 } | trailing dot | apply blur/enter format
|
|
15
|
+
* | "1e" | { value: 1 } | partial exp → 1e+0 | apply blur/enter format
|
|
16
|
+
* | "1e-" | { value: 1 } | partial exp → 1e-0 | apply blur/enter format
|
|
17
|
+
* | "1e+" | { value: 1 } | partial exp → 1e+0 | apply blur/enter format
|
|
18
|
+
* | "123" | { value: 123 } | exact match |
|
|
19
|
+
* | "-5" | { value: -5 } | exact match |
|
|
20
|
+
* | "0.5" | { value: 0.5 } | exact match |
|
|
21
|
+
* | "0.0000000001" | { value: 1e-10 } | decimal form matches |
|
|
22
|
+
* | "1e-5" | { value: 0.00001 } | exponential notation | apply blur/enter format
|
|
23
|
+
* | "2e+10" | { value: 2e10 } | exponential notation | apply blur/enter format
|
|
24
|
+
* | ".5" | { value: 0.5 } | not canonical | apply blur/enter format
|
|
25
|
+
* | "01" | { value: 1 } | leading zero | apply blur/enter format
|
|
26
|
+
* | "1.0" | { value: 1 } | trailing zero | apply blur/enter format
|
|
27
|
+
* | "1.10" | { value: 1.1 } | trailing zero | apply blur/enter format
|
|
28
|
+
* | "+5" | { value: 5 } | unnecessary plus | apply blur/enter format
|
|
29
|
+
* | "1,5" | { error: "...separator..." } | comma instead of dot |
|
|
30
|
+
* | "1.232,111" | { error: "...separator..." } | EU locale format |
|
|
31
|
+
* | "1.237.62" | { error: "...separator..." } | multiple dots (EU thousands) |
|
|
32
|
+
* | "555.555.555,100" | { error: "...separator..." } | EU locale format |
|
|
33
|
+
* | "1,222,333.05" | { error: "...separator..." } | US locale format |
|
|
34
|
+
* | "abc" | { error: "not a number" } | letters |
|
|
35
|
+
* | "12abc" | { error: "not a number" } | letters mixed in |
|
|
36
|
+
* | "1.237.asdf62" | { error: "not a number" } | letters mixed in |
|
|
37
|
+
* | "9007199254740993" | { error: "precision..." } | integer exceeds safe range |
|
|
38
|
+
* | "0.1234567890123456789" | { error: "precision..." } | too many digits for float64 |
|
|
39
|
+
*/
|
|
40
|
+
export type ParseResult = {
|
|
41
|
+
value: number;
|
|
42
|
+
error?: undefined;
|
|
43
|
+
} | {
|
|
44
|
+
value?: undefined;
|
|
45
|
+
error: string;
|
|
46
|
+
} | {
|
|
47
|
+
value?: undefined;
|
|
48
|
+
error?: undefined;
|
|
5
49
|
};
|
|
6
|
-
export declare function
|
|
50
|
+
export declare function tryParseNumber(str: string): ParseResult;
|
|
51
|
+
/**
|
|
52
|
+
* Converts a number to a plain decimal string (no exponential notation).
|
|
53
|
+
* E.g. 1e-7 → "0.0000001", 2e+21 → "2000000000000000000000"
|
|
54
|
+
*/
|
|
55
|
+
export declare function numberToDecimalString(n: number | undefined): string;
|
|
56
|
+
export declare function validateNumber(value: number, props: {
|
|
7
57
|
minValue?: number;
|
|
8
58
|
maxValue?: number;
|
|
9
59
|
validate?: (v: number) => string | undefined;
|
|
10
|
-
}
|
|
11
|
-
export {};
|
|
60
|
+
}): string | undefined;
|
|
12
61
|
//# sourceMappingURL=parseNumber.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseNumber.d.ts","sourceRoot":"","sources":["../../../src/components/PlNumberField/parseNumber.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"parseNumber.d.ts","sourceRoot":"","sources":["../../../src/components/PlNumberField/parseNumber.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,MAAM,MAAM,WAAW,GACnB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CAAE,GACpC;IAAE,KAAK,CAAC,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,KAAK,CAAC,EAAE,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC;AA6C7C,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CA8BvD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CASnE;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;IACL,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CAC9C,GACA,MAAM,GAAG,SAAS,CAQpB"}
|
|
@@ -1,65 +1,49 @@
|
|
|
1
|
-
var e =
|
|
2
|
-
|
|
3
|
-
return e === "." || e === "," || e === "-";
|
|
4
|
-
}
|
|
1
|
+
var e = /^-?\d+(\.\d+)?e[+-]?\d+$/i, t = /^-?\d+(\.\d+)?e[+-]?$/i;
|
|
2
|
+
/** "-", ".", "-." — NaN for Number() but clearly in-progress typing */
|
|
5
3
|
function n(e) {
|
|
6
|
-
return e
|
|
4
|
+
return e === "-" || e === "." || e === "-.";
|
|
7
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* Normalize a decimal string by removing cosmetic differences:
|
|
8
|
+
* leading +, leading zeros, trailing zeros after decimal, trailing dot.
|
|
9
|
+
* Used to compare user input with canonical float representation.
|
|
10
|
+
*/
|
|
8
11
|
function r(e) {
|
|
9
|
-
|
|
12
|
+
let t = "";
|
|
13
|
+
return e.startsWith("-") ? (t = "-", e = e.slice(1)) : e.startsWith("+") && (e = e.slice(1)), e = e.replace(/^0+(?=\d)/, ""), e.startsWith(".") && (e = "0" + e), e.includes(".") && (e = e.replace(/0+$/, "").replace(/\.$/, "")), e === "" || e === "0" ? "0" : t + e;
|
|
14
|
+
}
|
|
15
|
+
/** Complete partial exponential: "1e" → "1e+0", "1e-" → "1e-0", "1e+" → "1e+0" */
|
|
16
|
+
function i(e) {
|
|
17
|
+
return /e$/i.test(e) ? e + "+0" : /e[+-]$/i.test(e) ? e + "0" : e;
|
|
10
18
|
}
|
|
11
|
-
function
|
|
12
|
-
if (
|
|
13
|
-
if (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
let
|
|
18
|
-
return
|
|
19
|
+
function a(a) {
|
|
20
|
+
if (a = a.trim(), a === "" || n(a)) return {};
|
|
21
|
+
if (e.test(a) || t.test(a)) {
|
|
22
|
+
let e = i(a), t = Number(e);
|
|
23
|
+
return Number.isFinite(t) ? { value: t } : { error: "Value is not a number" };
|
|
24
|
+
}
|
|
25
|
+
let s = Number(a);
|
|
26
|
+
if (!Number.isFinite(s)) return /^[-+]?[\d.,\s]+$/.test(a) ? { error: "Use dot as decimal separator, e.g. 3.14" } : { error: "Value is not a number" };
|
|
27
|
+
let c = o(s);
|
|
28
|
+
return r(a) === c ? { value: s } : { error: `Precision exceeded, actual value: ${c}` };
|
|
19
29
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (!
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
error: Error("Enter a number"),
|
|
33
|
-
cleanInput: o
|
|
34
|
-
};
|
|
35
|
-
let s = r(a);
|
|
36
|
-
if (isNaN(s)) return {
|
|
37
|
-
error: Error("Value is not a number"),
|
|
38
|
-
cleanInput: o
|
|
39
|
-
};
|
|
40
|
-
if (n.minValue !== void 0 && s < n.minValue) return {
|
|
41
|
-
error: Error(`Value must be higher than ${n.minValue}`),
|
|
42
|
-
value: s,
|
|
43
|
-
cleanInput: o
|
|
44
|
-
};
|
|
45
|
-
if (n.maxValue !== void 0 && s > n.maxValue) return {
|
|
46
|
-
error: Error(`Value must be less than ${n.maxValue}`),
|
|
47
|
-
value: s,
|
|
48
|
-
cleanInput: o
|
|
49
|
-
};
|
|
50
|
-
if (n.validate) {
|
|
51
|
-
let e = n.validate(s);
|
|
52
|
-
if (e) return {
|
|
53
|
-
error: Error(e),
|
|
54
|
-
value: s,
|
|
55
|
-
cleanInput: o
|
|
56
|
-
};
|
|
30
|
+
/**
|
|
31
|
+
* Converts a number to a plain decimal string (no exponential notation).
|
|
32
|
+
* E.g. 1e-7 → "0.0000001", 2e+21 → "2000000000000000000000"
|
|
33
|
+
*/
|
|
34
|
+
function o(e) {
|
|
35
|
+
if (e === void 0) return "";
|
|
36
|
+
let t = String(e);
|
|
37
|
+
if (!t.includes("e") && !t.includes("E")) return t;
|
|
38
|
+
try {
|
|
39
|
+
return e.toFixed(20).replace(/\.?0+$/, "");
|
|
40
|
+
} catch {
|
|
41
|
+
return t;
|
|
57
42
|
}
|
|
58
|
-
return {
|
|
59
|
-
value: s,
|
|
60
|
-
cleanInput: o
|
|
61
|
-
};
|
|
62
43
|
}
|
|
63
|
-
|
|
44
|
+
function s(e, t) {
|
|
45
|
+
return t.minValue !== void 0 && e < t.minValue ? `Value must be higher than ${t.minValue}` : t.maxValue !== void 0 && e > t.maxValue ? `Value must be less than ${t.maxValue}` : t.validate?.(e);
|
|
46
|
+
}
|
|
47
|
+
export { o as numberToDecimalString, a as tryParseNumber, s as validateNumber };
|
|
64
48
|
|
|
65
49
|
//# sourceMappingURL=parseNumber.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseNumber.js","names":[],"sources":["../../../src/components/PlNumberField/parseNumber.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"parseNumber.js","names":[],"sources":["../../../src/components/PlNumberField/parseNumber.ts"],"sourcesContent":["/**\n * Strict number parser. No locale guessing, no normalization.\n * Non-canonical forms (leading zeros, trailing dots, etc.) are accepted —\n * formatting to canonical form happens on blur/enter in the component.\n *\n * Input/output table (covers ~90% of real cases):\n *\n * | Input | Result | Reason |\n * |---------------------|---------------------------------|---------------------------------|\n * | \"\" | {} | empty |\n * | \"-\" | {} | partial | apply blur/enter format\n * | \".\" | {} | partial | apply blur/enter format\n * | \"-.\" | {} | partial | apply blur/enter format\n * | \"123.\" | { value: 123 } | trailing dot | apply blur/enter format\n * | \"1e\" | { value: 1 } | partial exp → 1e+0 | apply blur/enter format\n * | \"1e-\" | { value: 1 } | partial exp → 1e-0 | apply blur/enter format\n * | \"1e+\" | { value: 1 } | partial exp → 1e+0 | apply blur/enter format\n * | \"123\" | { value: 123 } | exact match |\n * | \"-5\" | { value: -5 } | exact match |\n * | \"0.5\" | { value: 0.5 } | exact match |\n * | \"0.0000000001\" | { value: 1e-10 } | decimal form matches |\n * | \"1e-5\" | { value: 0.00001 } | exponential notation | apply blur/enter format\n * | \"2e+10\" | { value: 2e10 } | exponential notation | apply blur/enter format\n * | \".5\" | { value: 0.5 } | not canonical | apply blur/enter format\n * | \"01\" | { value: 1 } | leading zero | apply blur/enter format\n * | \"1.0\" | { value: 1 } | trailing zero | apply blur/enter format\n * | \"1.10\" | { value: 1.1 } | trailing zero | apply blur/enter format\n * | \"+5\" | { value: 5 } | unnecessary plus | apply blur/enter format\n * | \"1,5\" | { error: \"...separator...\" } | comma instead of dot |\n * | \"1.232,111\" | { error: \"...separator...\" } | EU locale format |\n * | \"1.237.62\" | { error: \"...separator...\" } | multiple dots (EU thousands) |\n * | \"555.555.555,100\" | { error: \"...separator...\" } | EU locale format |\n * | \"1,222,333.05\" | { error: \"...separator...\" } | US locale format |\n * | \"abc\" | { error: \"not a number\" } | letters |\n * | \"12abc\" | { error: \"not a number\" } | letters mixed in |\n * | \"1.237.asdf62\" | { error: \"not a number\" } | letters mixed in |\n * | \"9007199254740993\" | { error: \"precision...\" } | integer exceeds safe range |\n * | \"0.1234567890123456789\" | { error: \"precision...\" } | too many digits for float64 |\n */\n\nexport type ParseResult =\n | { value: number; error?: undefined }\n | { value?: undefined; error: string }\n | { value?: undefined; error?: undefined };\n\nconst EXP_RE = /^-?\\d+(\\.\\d+)?e[+-]?\\d+$/i;\nconst EXP_PARTIAL_RE = /^-?\\d+(\\.\\d+)?e[+-]?$/i;\n\n/** \"-\", \".\", \"-.\" — NaN for Number() but clearly in-progress typing */\nfunction isPartialInput(str: string): boolean {\n return str === \"-\" || str === \".\" || str === \"-.\";\n}\n\n/**\n * Normalize a decimal string by removing cosmetic differences:\n * leading +, leading zeros, trailing zeros after decimal, trailing dot.\n * Used to compare user input with canonical float representation.\n */\nfunction normalizeDecimalString(s: string): string {\n let sign = \"\";\n if (s.startsWith(\"-\")) {\n sign = \"-\";\n s = s.slice(1);\n } else if (s.startsWith(\"+\")) {\n s = s.slice(1);\n }\n\n // Remove leading zeros (keep one before decimal point)\n s = s.replace(/^0+(?=\\d)/, \"\");\n if (s.startsWith(\".\")) s = \"0\" + s;\n\n // Remove trailing zeros after decimal point, then trailing dot\n if (s.includes(\".\")) {\n s = s.replace(/0+$/, \"\").replace(/\\.$/, \"\");\n }\n\n if (s === \"\" || s === \"0\") return \"0\";\n\n return sign + s;\n}\n\n/** Complete partial exponential: \"1e\" → \"1e+0\", \"1e-\" → \"1e-0\", \"1e+\" → \"1e+0\" */\nfunction completeExponential(str: string): string {\n if (/e$/i.test(str)) return str + \"+0\";\n if (/e[+-]$/i.test(str)) return str + \"0\";\n return str;\n}\n\nexport function tryParseNumber(str: string): ParseResult {\n str = str.trim();\n if (str === \"\") return {};\n if (isPartialInput(str)) return {};\n\n // Exponential notation (full or partial)\n if (EXP_RE.test(str) || EXP_PARTIAL_RE.test(str)) {\n const completed = completeExponential(str);\n const n = Number(completed);\n if (!Number.isFinite(n)) return { error: \"Value is not a number\" };\n return { value: n };\n }\n\n const n = Number(str);\n if (!Number.isFinite(n)) {\n // Only digits, dots, commas, sign, spaces → likely a locale/format issue\n if (/^[-+]?[\\d.,\\s]+$/.test(str)) {\n return { error: \"Use dot as decimal separator, e.g. 3.14\" };\n }\n return { error: \"Value is not a number\" };\n }\n\n // Precision loss: input has more precision than float64 can represent\n const canonical = numberToDecimalString(n);\n const normalized = normalizeDecimalString(str);\n if (normalized !== canonical) {\n return { error: `Precision exceeded, actual value: ${canonical}` };\n }\n\n return { value: n };\n}\n\n/**\n * Converts a number to a plain decimal string (no exponential notation).\n * E.g. 1e-7 → \"0.0000001\", 2e+21 → \"2000000000000000000000\"\n */\nexport function numberToDecimalString(n: number | undefined): string {\n if (n === undefined) return \"\";\n const s = String(n);\n if (!s.includes(\"e\") && !s.includes(\"E\")) return s;\n try {\n return n.toFixed(20).replace(/\\.?0+$/, \"\");\n } catch {\n return s;\n }\n}\n\nexport function validateNumber(\n value: number,\n props: {\n minValue?: number;\n maxValue?: number;\n validate?: (v: number) => string | undefined;\n },\n): string | undefined {\n if (props.minValue !== undefined && value < props.minValue) {\n return `Value must be higher than ${props.minValue}`;\n }\n if (props.maxValue !== undefined && value > props.maxValue) {\n return `Value must be less than ${props.maxValue}`;\n }\n return props.validate?.(value);\n}\n"],"mappings":"AA6CA,IAAM,IAAS,6BACT,IAAiB;;AAGvB,SAAS,EAAe,GAAsB;AAC5C,QAAO,MAAQ,OAAO,MAAQ,OAAO,MAAQ;;;;;;;AAQ/C,SAAS,EAAuB,GAAmB;CACjD,IAAI,IAAO;AAmBX,QAlBI,EAAE,WAAW,IAAI,IACnB,IAAO,KACP,IAAI,EAAE,MAAM,EAAE,IACL,EAAE,WAAW,IAAI,KAC1B,IAAI,EAAE,MAAM,EAAE,GAIhB,IAAI,EAAE,QAAQ,aAAa,GAAG,EAC1B,EAAE,WAAW,IAAI,KAAE,IAAI,MAAM,IAG7B,EAAE,SAAS,IAAI,KACjB,IAAI,EAAE,QAAQ,OAAO,GAAG,CAAC,QAAQ,OAAO,GAAG,GAGzC,MAAM,MAAM,MAAM,MAAY,MAE3B,IAAO;;;AAIhB,SAAS,EAAoB,GAAqB;AAGhD,QAFI,MAAM,KAAK,EAAI,GAAS,IAAM,OAC9B,UAAU,KAAK,EAAI,GAAS,IAAM,MAC/B;;AAGT,SAAgB,EAAe,GAA0B;AAGvD,KAFA,IAAM,EAAI,MAAM,EACZ,MAAQ,MACR,EAAe,EAAI,CAAE,QAAO,EAAE;AAGlC,KAAI,EAAO,KAAK,EAAI,IAAI,EAAe,KAAK,EAAI,EAAE;EAChD,IAAM,IAAY,EAAoB,EAAI,EACpC,IAAI,OAAO,EAAU;AAE3B,SADK,OAAO,SAAS,EAAE,GAChB,EAAE,OAAO,GAAG,GADa,EAAE,OAAO,yBAAyB;;CAIpE,IAAM,IAAI,OAAO,EAAI;AACrB,KAAI,CAAC,OAAO,SAAS,EAAE,CAKrB,QAHI,mBAAmB,KAAK,EAAI,GACvB,EAAE,OAAO,2CAA2C,GAEtD,EAAE,OAAO,yBAAyB;CAI3C,IAAM,IAAY,EAAsB,EAAE;AAM1C,QALmB,EAAuB,EAAI,KAC3B,IAIZ,EAAE,OAAO,GAAG,GAHV,EAAE,OAAO,qCAAqC,KAAa;;;;;;AAUtE,SAAgB,EAAsB,GAA+B;AACnE,KAAI,MAAM,KAAA,EAAW,QAAO;CAC5B,IAAM,IAAI,OAAO,EAAE;AACnB,KAAI,CAAC,EAAE,SAAS,IAAI,IAAI,CAAC,EAAE,SAAS,IAAI,CAAE,QAAO;AACjD,KAAI;AACF,SAAO,EAAE,QAAQ,GAAG,CAAC,QAAQ,UAAU,GAAG;SACpC;AACN,SAAO;;;AAIX,SAAgB,EACd,GACA,GAKoB;AAOpB,QANI,EAAM,aAAa,KAAA,KAAa,IAAQ,EAAM,WACzC,6BAA6B,EAAM,aAExC,EAAM,aAAa,KAAA,KAAa,IAAQ,EAAM,WACzC,2BAA2B,EAAM,aAEnC,EAAM,WAAW,EAAM"}
|