@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,124 +1,151 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
+
|
|
41
|
+
export type ParseResult =
|
|
42
|
+
| { value: number; error?: undefined }
|
|
43
|
+
| { value?: undefined; error: string }
|
|
44
|
+
| { value?: undefined; error?: undefined };
|
|
45
|
+
|
|
46
|
+
const EXP_RE = /^-?\d+(\.\d+)?e[+-]?\d+$/i;
|
|
47
|
+
const EXP_PARTIAL_RE = /^-?\d+(\.\d+)?e[+-]?$/i;
|
|
48
|
+
|
|
49
|
+
/** "-", ".", "-." — NaN for Number() but clearly in-progress typing */
|
|
50
|
+
function isPartialInput(str: string): boolean {
|
|
51
|
+
return str === "-" || str === "." || str === "-.";
|
|
20
52
|
}
|
|
21
53
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (
|
|
30
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Normalize a decimal string by removing cosmetic differences:
|
|
56
|
+
* leading +, leading zeros, trailing zeros after decimal, trailing dot.
|
|
57
|
+
* Used to compare user input with canonical float representation.
|
|
58
|
+
*/
|
|
59
|
+
function normalizeDecimalString(s: string): string {
|
|
60
|
+
let sign = "";
|
|
61
|
+
if (s.startsWith("-")) {
|
|
62
|
+
sign = "-";
|
|
63
|
+
s = s.slice(1);
|
|
64
|
+
} else if (s.startsWith("+")) {
|
|
65
|
+
s = s.slice(1);
|
|
31
66
|
}
|
|
32
67
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
68
|
+
// Remove leading zeros (keep one before decimal point)
|
|
69
|
+
s = s.replace(/^0+(?=\d)/, "");
|
|
70
|
+
if (s.startsWith(".")) s = "0" + s;
|
|
36
71
|
|
|
37
|
-
|
|
38
|
-
if (
|
|
39
|
-
|
|
72
|
+
// Remove trailing zeros after decimal point, then trailing dot
|
|
73
|
+
if (s.includes(".")) {
|
|
74
|
+
s = s.replace(/0+$/, "").replace(/\.$/, "");
|
|
40
75
|
}
|
|
41
76
|
|
|
42
|
-
if (
|
|
43
|
-
return clearNumericValue(v);
|
|
44
|
-
}
|
|
77
|
+
if (s === "" || s === "0") return "0";
|
|
45
78
|
|
|
46
|
-
|
|
79
|
+
return sign + s;
|
|
80
|
+
}
|
|
47
81
|
|
|
48
|
-
|
|
82
|
+
/** Complete partial exponential: "1e" → "1e+0", "1e-" → "1e-0", "1e+" → "1e+0" */
|
|
83
|
+
function completeExponential(str: string): string {
|
|
84
|
+
if (/e$/i.test(str)) return str + "+0";
|
|
85
|
+
if (/e[+-]$/i.test(str)) return str + "0";
|
|
86
|
+
return str;
|
|
49
87
|
}
|
|
50
88
|
|
|
51
|
-
export function
|
|
52
|
-
props: {
|
|
53
|
-
minValue?: number;
|
|
54
|
-
maxValue?: number;
|
|
55
|
-
validate?: (v: number) => string | undefined;
|
|
56
|
-
},
|
|
57
|
-
str: string,
|
|
58
|
-
): ParseResult {
|
|
89
|
+
export function tryParseNumber(str: string): ParseResult {
|
|
59
90
|
str = str.trim();
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
};
|
|
91
|
+
if (str === "") return {};
|
|
92
|
+
if (isPartialInput(str)) return {};
|
|
93
|
+
|
|
94
|
+
// Exponential notation (full or partial)
|
|
95
|
+
if (EXP_RE.test(str) || EXP_PARTIAL_RE.test(str)) {
|
|
96
|
+
const completed = completeExponential(str);
|
|
97
|
+
const n = Number(completed);
|
|
98
|
+
if (!Number.isFinite(n)) return { error: "Value is not a number" };
|
|
99
|
+
return { value: n };
|
|
68
100
|
}
|
|
69
101
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
102
|
+
const n = Number(str);
|
|
103
|
+
if (!Number.isFinite(n)) {
|
|
104
|
+
// Only digits, dots, commas, sign, spaces → likely a locale/format issue
|
|
105
|
+
if (/^[-+]?[\d.,\s]+$/.test(str)) {
|
|
106
|
+
return { error: "Use dot as decimal separator, e.g. 3.14" };
|
|
107
|
+
}
|
|
108
|
+
return { error: "Value is not a number" };
|
|
75
109
|
}
|
|
76
110
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
};
|
|
111
|
+
// Precision loss: input has more precision than float64 can represent
|
|
112
|
+
const canonical = numberToDecimalString(n);
|
|
113
|
+
const normalized = normalizeDecimalString(str);
|
|
114
|
+
if (normalized !== canonical) {
|
|
115
|
+
return { error: `Precision exceeded, actual value: ${canonical}` };
|
|
82
116
|
}
|
|
83
117
|
|
|
84
|
-
|
|
118
|
+
return { value: n };
|
|
119
|
+
}
|
|
85
120
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Converts a number to a plain decimal string (no exponential notation).
|
|
123
|
+
* E.g. 1e-7 → "0.0000001", 2e+21 → "2000000000000000000000"
|
|
124
|
+
*/
|
|
125
|
+
export function numberToDecimalString(n: number | undefined): string {
|
|
126
|
+
if (n === undefined) return "";
|
|
127
|
+
const s = String(n);
|
|
128
|
+
if (!s.includes("e") && !s.includes("E")) return s;
|
|
129
|
+
try {
|
|
130
|
+
return n.toFixed(20).replace(/\.?0+$/, "");
|
|
131
|
+
} catch {
|
|
132
|
+
return s;
|
|
91
133
|
}
|
|
134
|
+
}
|
|
92
135
|
|
|
136
|
+
export function validateNumber(
|
|
137
|
+
value: number,
|
|
138
|
+
props: {
|
|
139
|
+
minValue?: number;
|
|
140
|
+
maxValue?: number;
|
|
141
|
+
validate?: (v: number) => string | undefined;
|
|
142
|
+
},
|
|
143
|
+
): string | undefined {
|
|
93
144
|
if (props.minValue !== undefined && value < props.minValue) {
|
|
94
|
-
return {
|
|
95
|
-
error: Error(`Value must be higher than ${props.minValue}`),
|
|
96
|
-
value,
|
|
97
|
-
cleanInput,
|
|
98
|
-
};
|
|
145
|
+
return `Value must be higher than ${props.minValue}`;
|
|
99
146
|
}
|
|
100
|
-
|
|
101
147
|
if (props.maxValue !== undefined && value > props.maxValue) {
|
|
102
|
-
return {
|
|
103
|
-
error: Error(`Value must be less than ${props.maxValue}`),
|
|
104
|
-
value,
|
|
105
|
-
cleanInput,
|
|
106
|
-
};
|
|
148
|
+
return `Value must be less than ${props.maxValue}`;
|
|
107
149
|
}
|
|
108
|
-
|
|
109
|
-
if (props.validate) {
|
|
110
|
-
const error = props.validate(value);
|
|
111
|
-
if (error) {
|
|
112
|
-
return {
|
|
113
|
-
error: Error(error),
|
|
114
|
-
value,
|
|
115
|
-
cleanInput,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
value,
|
|
122
|
-
cleanInput,
|
|
123
|
-
};
|
|
150
|
+
return props.validate?.(value);
|
|
124
151
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
--label-offset-right-x: 8px;
|
|
8
8
|
--label-color: var(--txt-01);
|
|
9
9
|
--color-hint: #9d9eae;
|
|
10
|
+
--show-clearable: none;
|
|
10
11
|
|
|
11
12
|
// overflow: hidden;
|
|
12
13
|
|
|
@@ -22,12 +23,10 @@
|
|
|
22
23
|
|
|
23
24
|
&__wrapper {
|
|
24
25
|
padding-left: 12px;
|
|
25
|
-
|
|
26
|
+
padding-right: 8px;
|
|
27
|
+
gap: 8px;
|
|
26
28
|
border-radius: 6px;
|
|
27
29
|
}
|
|
28
|
-
&__wrapper.withoutArrows {
|
|
29
|
-
padding-right: 12px;
|
|
30
|
-
}
|
|
31
30
|
|
|
32
31
|
&__icons {
|
|
33
32
|
// background-color: green;
|
|
@@ -60,6 +59,12 @@
|
|
|
60
59
|
border-bottom: 1px solid var(--contour-color);
|
|
61
60
|
}
|
|
62
61
|
|
|
62
|
+
&__clearable {
|
|
63
|
+
display: var(--show-clearable) !important;
|
|
64
|
+
--icon-color: var(--ic-02) !important;
|
|
65
|
+
cursor: pointer;
|
|
66
|
+
}
|
|
67
|
+
|
|
63
68
|
&__hint {
|
|
64
69
|
margin-top: 3px;
|
|
65
70
|
color: var(--color-hint);
|
|
@@ -90,6 +95,14 @@
|
|
|
90
95
|
transition: all 0.3s;
|
|
91
96
|
}
|
|
92
97
|
|
|
98
|
+
&:hover {
|
|
99
|
+
--show-clearable: inline-block;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
&:focus-within {
|
|
103
|
+
--show-clearable: inline-block;
|
|
104
|
+
}
|
|
105
|
+
|
|
93
106
|
&:focus-within:not(.error) {
|
|
94
107
|
--label-color: var(--txt-focus);
|
|
95
108
|
--contour-color: var(--border-color-focus);
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
1
|
+
<script lang="ts" setup generic="V extends undefined | string, C extends V">
|
|
2
2
|
import { PlIcon16 } from "../PlIcon16";
|
|
3
3
|
import { PlIcon24 } from "../PlIcon24";
|
|
4
4
|
import { computed } from "vue";
|
|
5
5
|
import PlTooltip from "../PlTooltip/PlTooltip.vue";
|
|
6
6
|
|
|
7
|
-
const model = defineModel<
|
|
7
|
+
const model = defineModel<V>({ required: true });
|
|
8
8
|
|
|
9
9
|
const props = defineProps<{
|
|
10
10
|
modelValue?: string;
|
|
11
|
-
clearable?: boolean;
|
|
11
|
+
clearable?: boolean | (() => C);
|
|
12
12
|
placeholder?: string;
|
|
13
13
|
disabled?: boolean;
|
|
14
14
|
helper?: string;
|
|
@@ -20,7 +20,11 @@ const slots = defineSlots<{
|
|
|
20
20
|
const nonEmpty = computed(() => model.value != null && model.value.length > 0);
|
|
21
21
|
const hasHelper = computed(() => props.helper != null || slots.helper != null);
|
|
22
22
|
|
|
23
|
-
const clear = () =>
|
|
23
|
+
const clear = () => {
|
|
24
|
+
if (props.clearable) {
|
|
25
|
+
model.value = (typeof props.clearable === "function" ? props.clearable() : "") as V;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
24
28
|
</script>
|
|
25
29
|
|
|
26
30
|
<template>
|
|
@@ -7,13 +7,20 @@ export default {
|
|
|
7
7
|
};
|
|
8
8
|
</script>
|
|
9
9
|
|
|
10
|
-
<script
|
|
10
|
+
<script
|
|
11
|
+
lang="ts"
|
|
12
|
+
setup
|
|
13
|
+
generic="
|
|
14
|
+
R extends true | false,
|
|
15
|
+
V extends undefined | string,
|
|
16
|
+
C extends Exclude<V, R extends true ? undefined : never>
|
|
17
|
+
"
|
|
18
|
+
>
|
|
11
19
|
import { computed, ref, useSlots } from "vue";
|
|
12
20
|
import SvgRequired from "../../assets/images/required.svg?raw";
|
|
13
21
|
import { getErrorMessage } from "../../helpers/error.ts";
|
|
14
22
|
import DoubleContour from "../../utils/DoubleContour.vue";
|
|
15
23
|
import { useLabelNotch } from "../../utils/useLabelNotch";
|
|
16
|
-
import { useValidation } from "../../utils/useValidation";
|
|
17
24
|
import { PlIcon16 } from "../PlIcon16";
|
|
18
25
|
import { PlIcon24 } from "../PlIcon24";
|
|
19
26
|
import { PlSvg } from "../PlSvg";
|
|
@@ -25,58 +32,37 @@ const slots = useSlots();
|
|
|
25
32
|
/**
|
|
26
33
|
* The current value of the input field.
|
|
27
34
|
*/
|
|
28
|
-
const model = defineModel<
|
|
29
|
-
|
|
35
|
+
const model = defineModel<V>({
|
|
36
|
+
required: true,
|
|
30
37
|
});
|
|
31
38
|
|
|
32
39
|
const props = defineProps<{
|
|
33
|
-
/**
|
|
34
|
-
* The label to display above the input field.
|
|
35
|
-
*/
|
|
40
|
+
/** The label to display above the input field. */
|
|
36
41
|
label?: string;
|
|
37
42
|
/**
|
|
38
43
|
* If `true`, a clear icon will appear in the input field to clear the value (set it to empty string).
|
|
44
|
+
* If a function, calls it to get the reset value.
|
|
39
45
|
*/
|
|
40
|
-
clearable?: boolean;
|
|
41
|
-
/**
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
required?: boolean;
|
|
45
|
-
/**
|
|
46
|
-
* An error message to display below the input field.
|
|
47
|
-
*/
|
|
46
|
+
clearable?: (R extends true ? never : boolean) | (() => C);
|
|
47
|
+
/** If `true`, the input field is marked as required and will show an error if left empty. */
|
|
48
|
+
required?: R;
|
|
49
|
+
/** An error message to display below the input field. */
|
|
48
50
|
error?: unknown;
|
|
49
|
-
/**
|
|
50
|
-
* A helper text to display below the input field when there are no errors.
|
|
51
|
-
*/
|
|
51
|
+
/** A helper text to display below the input field when there are no errors. */
|
|
52
52
|
helper?: string;
|
|
53
|
-
/**
|
|
54
|
-
* A placeholder text to display inside the input field when it is empty.
|
|
55
|
-
*/
|
|
53
|
+
/** A placeholder text to display inside the input field when it is empty. */
|
|
56
54
|
placeholder?: string;
|
|
57
|
-
/**
|
|
58
|
-
* If `true`, the input field is disabled and cannot be interacted with.
|
|
59
|
-
*/
|
|
55
|
+
/** If `true`, the input field is disabled and cannot be interacted with. */
|
|
60
56
|
disabled?: boolean;
|
|
61
|
-
/**
|
|
62
|
-
* If `true`, the input field has a dashed border.
|
|
63
|
-
*/
|
|
57
|
+
/** If `true`, the input field has a dashed border. */
|
|
64
58
|
dashed?: boolean;
|
|
65
|
-
/**
|
|
66
|
-
* A prefix text to display inside the input field before the value.
|
|
67
|
-
*/
|
|
59
|
+
/** A prefix text to display inside the input field before the value. */
|
|
68
60
|
prefix?: string;
|
|
69
|
-
/**
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
rules?: ((v: string) => boolean | string)[];
|
|
73
|
-
/**
|
|
74
|
-
* The string specifies whether the field should be a password or not, value could be "password" or undefined.
|
|
75
|
-
*/
|
|
61
|
+
/** Additional validity check for input value that must return an error text if failed */
|
|
62
|
+
validate?: (v: V) => string | undefined;
|
|
63
|
+
/** The string specifies whether the field should be a password or not, value could be "password" or undefined. */
|
|
76
64
|
type?: "password";
|
|
77
|
-
/**
|
|
78
|
-
* Makes some of corners not rounded
|
|
79
|
-
* */
|
|
65
|
+
/** Makes some of corners not rounded */
|
|
80
66
|
groupPosition?:
|
|
81
67
|
| "top"
|
|
82
68
|
| "bottom"
|
|
@@ -107,24 +93,26 @@ const passwordIcon = computed(() => (showPassword.value ? "view-show" : "view-hi
|
|
|
107
93
|
|
|
108
94
|
const clear = () => {
|
|
109
95
|
if (props.clearable) {
|
|
110
|
-
model.value = "";
|
|
96
|
+
model.value = (typeof props.clearable === "function" ? props.clearable() : "") as V;
|
|
111
97
|
}
|
|
112
98
|
};
|
|
113
99
|
|
|
114
|
-
const validationData = useValidation(model, props.rules || []);
|
|
115
|
-
|
|
116
100
|
const isEmpty = computed(() => model.value === "");
|
|
117
101
|
|
|
118
|
-
const nonEmpty = computed(() => !isEmpty.value);
|
|
119
|
-
|
|
120
102
|
const displayErrors = computed(() => {
|
|
121
103
|
const errors: string[] = [];
|
|
122
104
|
const propsError = getErrorMessage(props.error);
|
|
123
105
|
if (propsError) {
|
|
124
106
|
errors.push(propsError);
|
|
125
107
|
}
|
|
126
|
-
if (
|
|
127
|
-
|
|
108
|
+
if (props.validate) {
|
|
109
|
+
const error = props.validate(model.value as V);
|
|
110
|
+
if (error) {
|
|
111
|
+
errors.push(error);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (props.required && isEmpty.value) {
|
|
115
|
+
errors.push("Value is required");
|
|
128
116
|
}
|
|
129
117
|
return errors;
|
|
130
118
|
});
|
|
@@ -132,7 +120,7 @@ const displayErrors = computed(() => {
|
|
|
132
120
|
const hasErrors = computed(() => displayErrors.value.length > 0);
|
|
133
121
|
|
|
134
122
|
const canShowClearable = computed(
|
|
135
|
-
() => props.clearable &&
|
|
123
|
+
() => props.clearable && !isEmpty.value && props.type !== "password" && !props.disabled,
|
|
136
124
|
);
|
|
137
125
|
|
|
138
126
|
const togglePasswordVisibility = () => (showPassword.value = !showPassword.value);
|
|
@@ -151,7 +139,7 @@ useLabelNotch(rootRef);
|
|
|
151
139
|
error: hasErrors,
|
|
152
140
|
disabled,
|
|
153
141
|
dashed,
|
|
154
|
-
nonEmpty,
|
|
142
|
+
nonEmpty: !isEmpty,
|
|
155
143
|
}"
|
|
156
144
|
>
|
|
157
145
|
<label v-if="label" ref="label">
|
|
@@ -18,7 +18,7 @@ describe("TextField", () => {
|
|
|
18
18
|
const wrapper = mount(PlTextField, {
|
|
19
19
|
props: {
|
|
20
20
|
modelValue: "initialText",
|
|
21
|
-
"onUpdate:modelValue": (e
|
|
21
|
+
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
22
22
|
},
|
|
23
23
|
});
|
|
24
24
|
|
|
@@ -31,7 +31,7 @@ describe("TextField", () => {
|
|
|
31
31
|
props: {
|
|
32
32
|
modelValue: "initialText" as string | undefined,
|
|
33
33
|
clearable: true,
|
|
34
|
-
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
34
|
+
"onUpdate:modelValue": (e: string | undefined) => wrapper.setProps({ modelValue: e }),
|
|
35
35
|
},
|
|
36
36
|
});
|
|
37
37
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PlNumberField.spec.d.ts","sourceRoot":"","sources":["../../../../src/components/PlNumberField/__tests__/PlNumberField.spec.ts"],"names":[],"mappings":""}
|