@milaboratories/uikit 2.2.53 → 2.2.55
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/CHANGELOG.md +12 -0
- package/dist/pl-uikit.js +3045 -2991
- package/dist/pl-uikit.js.map +1 -1
- package/dist/pl-uikit.umd.cjs +8 -8
- package/dist/pl-uikit.umd.cjs.map +1 -1
- package/dist/src/components/PlNumberField/PlNumberField.vue.d.ts +21 -5
- package/dist/src/generated/icons-16.d.ts +1 -1
- package/dist/src/generated/icons-24.d.ts +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/style.css +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/assets/icons/icon-assets/16_cell-type-subset.svg +3 -0
- package/src/assets/icons/icon-assets/24_cell-type-subset.svg +3 -0
- package/src/assets/icons/icon-assets-min/16_cell-type-subset.svg +1 -0
- package/src/assets/icons/icon-assets-min/24_cell-type-subset.svg +1 -0
- package/src/assets/icons/icons-16-generated.json +1 -0
- package/src/assets/icons/icons-16-generated.scss +1 -0
- package/src/assets/icons/icons-24-generated.json +1 -0
- package/src/assets/icons/icons-24-generated.scss +1 -0
- package/src/components/PlNumberField/PlNumberField.vue +135 -75
- package/src/components/PlNumberField/__tests__/PlNumberField.spec.ts +25 -6
- package/src/components/PlNumberField/pl-number-field.scss +4 -0
- package/src/generated/icons-16.ts +1 -0
- package/src/generated/icons-24.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milaboratories/uikit",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.55",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/pl-uikit.umd.js",
|
|
6
6
|
"module": "dist/pl-uikit.js",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"svgo": "^3.3.2",
|
|
36
36
|
"@types/d3": "^7.4.3",
|
|
37
37
|
"@milaboratories/eslint-config": "^1.0.1",
|
|
38
|
-
"@
|
|
39
|
-
"@
|
|
38
|
+
"@platforma-sdk/model": "^1.22.18",
|
|
39
|
+
"@milaboratories/helpers": "^1.6.11"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"dev": "vite",
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.40057 4.30094C7.08923 3.33207 8.22079 2.7 9.49999 2.7C11.5987 2.7 13.3 4.40132 13.3 6.5C13.3 7.7792 12.6679 8.91076 11.6991 9.59942C11.6997 9.56636 11.7 9.53322 11.7 9.5C11.7 6.62812 9.37187 4.3 6.49999 4.3C6.46677 4.3 6.43363 4.30032 6.40057 4.30094ZM4.64182 4.64183C5.38972 2.68764 7.2828 1.3 9.49999 1.3C12.3719 1.3 14.7 3.62812 14.7 6.5C14.7 8.71719 13.3123 10.6103 11.3582 11.3582C10.6103 13.3124 8.71717 14.7 6.49999 14.7C3.62811 14.7 1.29999 12.3719 1.29999 9.5C1.29999 7.28282 2.68763 5.38973 4.64182 4.64183ZM9.59941 11.6991C8.91075 12.6679 7.77918 13.3 6.49999 13.3C4.40131 13.3 2.69999 11.5987 2.69999 9.5C2.69999 8.22081 3.33206 7.08924 4.30092 6.40058C4.3003 6.43365 4.29999 6.46679 4.29999 6.5C4.29999 9.37188 6.62811 11.7 9.49999 11.7C9.5332 11.7 9.56634 11.6997 9.59941 11.6991ZM10.2299 10.23C9.99371 10.2759 9.74966 10.3 9.49999 10.3C7.40131 10.3 5.69999 8.59869 5.69999 6.5C5.69999 6.25033 5.72407 6.00628 5.77003 5.77005C6.00626 5.72408 6.25031 5.7 6.49999 5.7C8.59867 5.7 10.3 7.40132 10.3 9.5C10.3 9.74968 10.2759 9.99373 10.2299 10.23Z" fill="#07AD3E"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.2552 7.75512C11.0348 6.83446 12.1991 6.25 13.5 6.25C15.8472 6.25 17.75 8.15279 17.75 10.5C17.75 11.8009 17.1655 12.9652 16.2449 13.7448C16.2483 13.6636 16.25 13.582 16.25 13.5C16.25 10.3244 13.6756 7.75 10.5 7.75C10.418 7.75 10.3364 7.75172 10.2552 7.75512ZM8.21983 8.21983C9.10256 6.17845 11.1345 4.75 13.5 4.75C16.6756 4.75 19.25 7.32436 19.25 10.5C19.25 12.8655 17.8215 14.8974 15.7802 15.7802C14.8974 17.8215 12.8655 19.25 10.5 19.25C7.32436 19.25 4.75 16.6756 4.75 13.5C4.75 11.1345 6.17845 9.10256 8.21983 8.21983ZM13.7448 16.2449C12.9652 17.1655 11.8009 17.75 10.5 17.75C8.15279 17.75 6.25 15.8472 6.25 13.5C6.25 12.1991 6.83446 11.0348 7.75512 10.2552C7.75172 10.3364 7.75 10.418 7.75 10.5C7.75 13.6756 10.3244 16.25 13.5 16.25C13.582 16.25 13.6636 16.2483 13.7448 16.2449ZM14.6049 14.6049C14.2526 14.6996 13.8822 14.75 13.5 14.75C11.1528 14.75 9.25 12.8472 9.25 10.5C9.25 10.1178 9.30045 9.74739 9.39505 9.39505C9.74739 9.30045 10.1178 9.25 10.5 9.25C12.8472 9.25 14.75 11.1528 14.75 13.5C14.75 13.8822 14.6996 14.2526 14.6049 14.6049Z" fill="#07AD3E"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none"><path fill="#07AD3E" fill-rule="evenodd" d="M6.4 4.3A3.8 3.8 0 1 1 11.7 9.599l.001-.098a5.2 5.2 0 0 0-5.3-5.2m-1.758.342A5.202 5.202 0 0 1 14.7 6.5a5.2 5.2 0 0 1-3.342 4.858A5.202 5.202 0 0 1 1.3 9.5a5.2 5.2 0 0 1 3.342-4.858m4.957 7.057A3.8 3.8 0 1 1 4.301 6.4l-.001.1a5.2 5.2 0 0 0 5.3 5.2m.63-1.469q-.354.07-.729.07a3.8 3.8 0 0 1-3.73-4.53q.354-.07.73-.07a3.8 3.8 0 0 1 3.73 4.53" clip-rule="evenodd"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="#07AD3E" fill-rule="evenodd" d="M10.255 7.755a4.25 4.25 0 1 1 5.99 5.99q.005-.122.005-.245a5.75 5.75 0 0 0-5.995-5.745M8.22 8.22a5.751 5.751 0 1 1 7.56 7.56A5.752 5.752 0 0 1 4.75 13.5a5.75 5.75 0 0 1 3.47-5.28m5.525 8.025a4.25 4.25 0 1 1-5.99-5.99 5.75 5.75 0 0 0 5.99 5.99m.86-1.64a4.25 4.25 0 0 1-5.21-5.21 4.25 4.25 0 0 1 5.21 5.21" clip-rule="evenodd"/></svg>
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"caret-right": "caret-right.svg",
|
|
13
13
|
"caret-up": "caret-up.svg",
|
|
14
14
|
"cell-type-num": "cell-type-num.svg",
|
|
15
|
+
"cell-type-subset": "cell-type-subset.svg",
|
|
15
16
|
"cell-type-txt": "cell-type-txt.svg",
|
|
16
17
|
"checkmark": "checkmark.svg",
|
|
17
18
|
"chevron-down": "chevron-down.svg",
|
|
@@ -12,6 +12,7 @@ $icons16: (
|
|
|
12
12
|
caret-right: 'caret-right.svg',
|
|
13
13
|
caret-up: 'caret-up.svg',
|
|
14
14
|
cell-type-num: 'cell-type-num.svg',
|
|
15
|
+
cell-type-subset: 'cell-type-subset.svg',
|
|
15
16
|
cell-type-txt: 'cell-type-txt.svg',
|
|
16
17
|
checkmark: 'checkmark.svg',
|
|
17
18
|
chevron-down: 'chevron-down.svg',
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"calendar": "calendar.svg",
|
|
29
29
|
"canvas": "canvas.svg",
|
|
30
30
|
"cell-type-num": "cell-type-num.svg",
|
|
31
|
+
"cell-type-subset": "cell-type-subset.svg",
|
|
31
32
|
"cell-type-txt": "cell-type-txt.svg",
|
|
32
33
|
"checkbox-checked": "checkbox-checked.svg",
|
|
33
34
|
"checkbox-intermediate": "checkbox-intermediate.svg",
|
|
@@ -28,6 +28,7 @@ $icons24: (
|
|
|
28
28
|
calendar: 'calendar.svg',
|
|
29
29
|
canvas: 'canvas.svg',
|
|
30
30
|
cell-type-num: 'cell-type-num.svg',
|
|
31
|
+
cell-type-subset: 'cell-type-subset.svg',
|
|
31
32
|
cell-type-txt: 'cell-type-txt.svg',
|
|
32
33
|
checkbox-checked: 'checkbox-checked.svg',
|
|
33
34
|
checkbox-intermediate: 'checkbox-intermediate.svg',
|
|
@@ -2,18 +2,29 @@
|
|
|
2
2
|
import './pl-number-field.scss';
|
|
3
3
|
import DoubleContour from '@/utils/DoubleContour.vue';
|
|
4
4
|
import { useLabelNotch } from '@/utils/useLabelNotch';
|
|
5
|
-
import { computed,
|
|
5
|
+
import { computed, ref, useSlots, watch } from 'vue';
|
|
6
6
|
import { PlTooltip } from '@/components/PlTooltip';
|
|
7
7
|
|
|
8
8
|
type NumberInputProps = {
|
|
9
|
-
|
|
9
|
+
/** Input is disabled if true */
|
|
10
10
|
disabled?: boolean;
|
|
11
|
+
/** Label on the top border of the field, empty by default */
|
|
11
12
|
label?: string;
|
|
13
|
+
/** Input placeholder, empty by default */
|
|
12
14
|
placeholder?: string;
|
|
15
|
+
/** Step for increment/decrement buttons, 1 by default */
|
|
13
16
|
step?: number;
|
|
17
|
+
/** If defined - show an error if value is lower */
|
|
14
18
|
minValue?: number;
|
|
19
|
+
/** If defined - show an error if value is higher */
|
|
15
20
|
maxValue?: number;
|
|
21
|
+
/** If false - remove buttons on the right */
|
|
22
|
+
useIncrementButtons?: boolean;
|
|
23
|
+
/** If true - changes do not apply immediately, they apply only by removing focus from the input (by click enter or by click outside) */
|
|
24
|
+
updateOnEnterOrClickOutside?: boolean;
|
|
25
|
+
/** Error message that shows always when it's provided, without other checks */
|
|
16
26
|
errorMessage?: string;
|
|
27
|
+
/** Additional validity check for input value that must return an error text if failed */
|
|
17
28
|
validate?: (v: number) => string | undefined;
|
|
18
29
|
};
|
|
19
30
|
|
|
@@ -23,68 +34,100 @@ const props = withDefaults(defineProps<NumberInputProps>(), {
|
|
|
23
34
|
placeholder: undefined,
|
|
24
35
|
minValue: undefined,
|
|
25
36
|
maxValue: undefined,
|
|
37
|
+
useIncrementButtons: true,
|
|
38
|
+
updateOnEnter: false,
|
|
26
39
|
errorMessage: undefined,
|
|
27
40
|
validate: undefined,
|
|
28
41
|
});
|
|
29
42
|
|
|
30
|
-
const
|
|
43
|
+
const modelValue = defineModel<number | undefined>({ required: true });
|
|
31
44
|
|
|
32
45
|
const root = ref<HTMLElement>();
|
|
33
46
|
const slots = useSlots();
|
|
34
47
|
const input = ref<HTMLInputElement>();
|
|
35
|
-
// const localErrors = ref<string[]>([]);
|
|
36
48
|
|
|
37
49
|
useLabelNotch(root);
|
|
38
50
|
|
|
39
|
-
|
|
51
|
+
function modelToString(v: number | undefined) {
|
|
52
|
+
return v === undefined ? '' : String(+v); // (+v) to avoid staying in input non-number values if they are provided in model
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isPartial(v: string) {
|
|
56
|
+
return v === '.' || v === ',' || v === '-';
|
|
57
|
+
}
|
|
58
|
+
function stringToModel(v: string) {
|
|
59
|
+
if (v === '') {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
if (isPartial(v)) {
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
let forParsing = v;
|
|
66
|
+
forParsing = forParsing.replace(',', '.');
|
|
67
|
+
forParsing = forParsing.replace('−', '-'); // minus, replacing for the case of input the whole copied value
|
|
68
|
+
forParsing = forParsing.replace('–', '-'); // dash, replacing for the case of input the whole copied value
|
|
69
|
+
forParsing = forParsing.replace('+', '');
|
|
70
|
+
return parseFloat(forParsing);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const innerTextValue = ref(modelToString(modelValue.value));
|
|
74
|
+
const innerNumberValue = computed(() => stringToModel(innerTextValue.value));
|
|
75
|
+
|
|
76
|
+
watch(() => modelValue.value, (outerValue) => { // update inner value if outer value is changed
|
|
77
|
+
if (parseFloat(innerTextValue.value) !== outerValue) {
|
|
78
|
+
innerTextValue.value = modelToString(outerValue);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
40
81
|
|
|
41
|
-
const
|
|
82
|
+
const NUMBER_REGEX = /^[-−–+]?(\d+)?[\\.,]?(\d+)?$/; // parseFloat works without errors on strings with multiple dots, or letters in value
|
|
83
|
+
const inputValue = computed({
|
|
42
84
|
get() {
|
|
43
|
-
|
|
44
|
-
if (props.modelValue !== undefined) {
|
|
45
|
-
const num = new Number(props.modelValue);
|
|
46
|
-
return num.toString();
|
|
47
|
-
}
|
|
48
|
-
return '';
|
|
49
|
-
}
|
|
50
|
-
return '';
|
|
85
|
+
return innerTextValue.value;
|
|
51
86
|
},
|
|
52
|
-
set(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
});
|
|
87
|
+
set(nextValue: string) {
|
|
88
|
+
const parsedValue = stringToModel(nextValue);
|
|
89
|
+
// we allow to set empty value or valid numeric value, otherwise reset input value to previous valid
|
|
90
|
+
if (parsedValue === undefined
|
|
91
|
+
|| (nextValue.match(NUMBER_REGEX) && !isNaN(parsedValue))
|
|
92
|
+
) {
|
|
93
|
+
innerTextValue.value = nextValue;
|
|
94
|
+
if (!props.updateOnEnterOrClickOutside && !isPartial(nextValue)) { // to avoid applying '-' or '.'
|
|
95
|
+
applyChanges();
|
|
62
96
|
}
|
|
63
|
-
} else {
|
|
64
|
-
|
|
65
|
-
emit('update:modelValue', undefined);
|
|
66
|
-
}
|
|
67
|
-
canRenderValue.value = false;
|
|
68
|
-
nextTick(() => {
|
|
69
|
-
canRenderValue.value = true;
|
|
70
|
-
});
|
|
97
|
+
} else if (input.value) {
|
|
98
|
+
input.value.value = innerTextValue.value;
|
|
71
99
|
}
|
|
72
100
|
},
|
|
73
101
|
});
|
|
102
|
+
const focused = ref(false);
|
|
103
|
+
|
|
104
|
+
function applyChanges() {
|
|
105
|
+
if (innerTextValue.value === '') {
|
|
106
|
+
modelValue.value = undefined;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
modelValue.value = innerNumberValue.value;
|
|
110
|
+
}
|
|
74
111
|
|
|
75
112
|
const errors = computed(() => {
|
|
76
113
|
let ers: string[] = [];
|
|
77
114
|
if (props.errorMessage) {
|
|
78
115
|
ers.push(props.errorMessage);
|
|
79
116
|
}
|
|
80
|
-
|
|
81
|
-
|
|
117
|
+
const parsedValue = innerNumberValue.value;
|
|
118
|
+
if (parsedValue !== undefined && isNaN(parsedValue)) {
|
|
119
|
+
ers.push('Value is not a number');
|
|
120
|
+
} else if (props.validate && parsedValue !== undefined) {
|
|
121
|
+
const error = props.validate(parsedValue);
|
|
122
|
+
if (error) {
|
|
123
|
+
ers.push(error);
|
|
124
|
+
}
|
|
82
125
|
} else {
|
|
83
|
-
if (props.minValue !== undefined &&
|
|
84
|
-
ers.push(`
|
|
126
|
+
if (props.minValue !== undefined && parsedValue !== undefined && parsedValue < props.minValue) {
|
|
127
|
+
ers.push(`Value must be higher than ${props.minValue}`);
|
|
85
128
|
}
|
|
86
|
-
if (props.maxValue !== undefined &&
|
|
87
|
-
ers.push(`
|
|
129
|
+
if (props.maxValue !== undefined && parsedValue !== undefined && parsedValue > props.maxValue) {
|
|
130
|
+
ers.push(`Value must be less than ${props.maxValue}`);
|
|
88
131
|
}
|
|
89
132
|
}
|
|
90
133
|
|
|
@@ -94,69 +137,76 @@ const errors = computed(() => {
|
|
|
94
137
|
});
|
|
95
138
|
|
|
96
139
|
const isIncrementDisabled = computed(() => {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
140
|
+
const parsedValue = innerNumberValue.value;
|
|
141
|
+
if (props.maxValue !== undefined && parsedValue !== undefined) {
|
|
142
|
+
return parsedValue >= props.maxValue;
|
|
101
143
|
}
|
|
102
|
-
|
|
103
144
|
return false;
|
|
104
145
|
});
|
|
105
146
|
|
|
106
147
|
const isDecrementDisabled = computed(() => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
148
|
+
const parsedValue = innerNumberValue.value;
|
|
149
|
+
if (props.minValue !== undefined && parsedValue !== undefined) {
|
|
150
|
+
return parsedValue <= props.minValue;
|
|
111
151
|
}
|
|
112
|
-
|
|
113
152
|
return false;
|
|
114
153
|
});
|
|
115
154
|
|
|
116
|
-
function isNumeric(str: string | number | undefined) {
|
|
117
|
-
if (str !== undefined) {
|
|
118
|
-
str = str?.toString();
|
|
119
|
-
return !isNaN(+str) && !isNaN(parseFloat(str));
|
|
120
|
-
}
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
155
|
function increment() {
|
|
156
|
+
const parsedValue = innerNumberValue.value;
|
|
125
157
|
if (!isIncrementDisabled.value) {
|
|
126
|
-
let nV
|
|
127
|
-
if (
|
|
158
|
+
let nV;
|
|
159
|
+
if (parsedValue === undefined) {
|
|
128
160
|
nV = props.minValue ? props.minValue : 0;
|
|
129
161
|
} else {
|
|
130
|
-
nV =
|
|
162
|
+
nV = (parsedValue || 0) + props.step;
|
|
131
163
|
}
|
|
132
|
-
|
|
133
|
-
computedValue.value = nV.toString();
|
|
164
|
+
modelValue.value = props.maxValue !== undefined ? Math.min(props.maxValue, nV) : nV;
|
|
134
165
|
}
|
|
135
166
|
}
|
|
136
167
|
|
|
137
168
|
function decrement() {
|
|
169
|
+
const parsedValue = innerNumberValue.value;
|
|
138
170
|
if (!isDecrementDisabled.value) {
|
|
139
|
-
let nV
|
|
140
|
-
if (
|
|
171
|
+
let nV;
|
|
172
|
+
if (parsedValue === undefined) {
|
|
141
173
|
nV = 0;
|
|
142
174
|
} else {
|
|
143
|
-
nV = +(
|
|
175
|
+
nV = +(parsedValue || 0) - props.step;
|
|
144
176
|
}
|
|
145
|
-
|
|
146
|
-
computedValue.value = props.minValue ? Math.max(props.minValue, nV).toString() : nV.toString();
|
|
177
|
+
modelValue.value = props.minValue !== undefined ? Math.max(props.minValue, nV) : nV;
|
|
147
178
|
}
|
|
148
179
|
}
|
|
149
180
|
|
|
150
181
|
function handleKeyPress(e: { code: string; preventDefault(): void }) {
|
|
182
|
+
if (props.updateOnEnterOrClickOutside) {
|
|
183
|
+
if (e.code === 'Escape') {
|
|
184
|
+
innerTextValue.value = modelToString(modelValue.value);
|
|
185
|
+
input.value?.blur();
|
|
186
|
+
}
|
|
187
|
+
if (e.code === 'Enter') {
|
|
188
|
+
input.value?.blur();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (e.code === 'Enter') {
|
|
193
|
+
innerTextValue.value = String(modelValue.value); // to make .1 => 0.1, 10.00 => 10, remove leading zeros etc
|
|
194
|
+
}
|
|
195
|
+
|
|
151
196
|
if (['ArrowDown', 'ArrowUp'].includes(e.code)) {
|
|
152
197
|
e.preventDefault();
|
|
153
198
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
199
|
+
if (props.useIncrementButtons && e.code === 'ArrowUp') {
|
|
200
|
+
increment();
|
|
201
|
+
}
|
|
202
|
+
if (props.useIncrementButtons && e.code === 'ArrowDown') {
|
|
203
|
+
decrement();
|
|
204
|
+
}
|
|
157
205
|
}
|
|
158
206
|
|
|
159
207
|
// https://stackoverflow.com/questions/880512/prevent-text-selection-after-double-click#:~:text=If%20you%20encounter%20a%20situation,none%3B%20to%20the%20summary%20element.
|
|
208
|
+
// this prevents selecting of more than input content in some cases,
|
|
209
|
+
// but also disable selecting input content by double-click (useful feature)
|
|
160
210
|
const onMousedown = (ev: MouseEvent) => {
|
|
161
211
|
if (ev.detail > 1) {
|
|
162
212
|
ev.preventDefault();
|
|
@@ -169,23 +219,33 @@ const onMousedown = (ev: MouseEvent) => {
|
|
|
169
219
|
ref="root"
|
|
170
220
|
:class="{ error: !!errors.trim(), disabled: disabled }"
|
|
171
221
|
class="mi-number-field d-flex-column"
|
|
172
|
-
@mousedown="onMousedown"
|
|
173
222
|
@keydown="handleKeyPress($event)"
|
|
174
223
|
>
|
|
175
224
|
<div class="mi-number-field__main-wrapper d-flex">
|
|
176
|
-
<DoubleContour class="mi-number-field__contour"
|
|
177
|
-
<div
|
|
225
|
+
<DoubleContour class="mi-number-field__contour"/>
|
|
226
|
+
<div
|
|
227
|
+
class="mi-number-field__wrapper flex-grow d-flex flex-align-center"
|
|
228
|
+
:class="{withoutArrows: !useIncrementButtons}"
|
|
229
|
+
>
|
|
178
230
|
<label v-if="label" class="text-description">
|
|
179
231
|
{{ label }}
|
|
180
232
|
<PlTooltip v-if="slots.tooltip" class="info" position="top">
|
|
181
233
|
<template #tooltip>
|
|
182
|
-
<slot name="tooltip"
|
|
234
|
+
<slot name="tooltip"/>
|
|
183
235
|
</template>
|
|
184
236
|
</PlTooltip>
|
|
185
237
|
</label>
|
|
186
|
-
<input
|
|
238
|
+
<input
|
|
239
|
+
ref="input"
|
|
240
|
+
v-model="inputValue"
|
|
241
|
+
:disabled="disabled"
|
|
242
|
+
:placeholder="placeholder"
|
|
243
|
+
class="text-s flex-grow"
|
|
244
|
+
@focusin="focused = true"
|
|
245
|
+
@focusout="focused = false; applyChanges()"
|
|
246
|
+
/>
|
|
187
247
|
</div>
|
|
188
|
-
<div class="mi-number-field__icons d-flex-column">
|
|
248
|
+
<div v-if="useIncrementButtons" class="mi-number-field__icons d-flex-column" @mousedown="onMousedown">
|
|
189
249
|
<div
|
|
190
250
|
:class="{ disabled: isIncrementDisabled }"
|
|
191
251
|
class="mi-number-field__icon d-flex flex-justify-center uc-pointer flex-grow flex-align-center"
|
|
@@ -31,7 +31,7 @@ describe('NumberInput.vue', () => {
|
|
|
31
31
|
});
|
|
32
32
|
const incrementButton = wrapper.find('.mi-number-field__icons div:first-child');
|
|
33
33
|
await incrementButton.trigger('click');
|
|
34
|
-
expect(wrapper.
|
|
34
|
+
expect(wrapper.vm.modelValue).toEqual(12);
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
it('decrements the value when decrement button is clicked', async () => {
|
|
@@ -43,7 +43,7 @@ describe('NumberInput.vue', () => {
|
|
|
43
43
|
});
|
|
44
44
|
const decrementButton = wrapper.find('.mi-number-field__icons div:last-child');
|
|
45
45
|
await decrementButton.trigger('click');
|
|
46
|
-
expect(wrapper.
|
|
46
|
+
expect(wrapper.vm.modelValue).toEqual(9);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
it('disables increment button when value exceeds maxValue', () => {
|
|
@@ -77,7 +77,7 @@ describe('NumberInput.vue', () => {
|
|
|
77
77
|
},
|
|
78
78
|
});
|
|
79
79
|
expect(wrapper.find('.mi-number-field__hint').text()).toContain('Custom error message');
|
|
80
|
-
expect(wrapper.find('.mi-number-field__hint').text()).toContain('
|
|
80
|
+
expect(wrapper.find('.mi-number-field__hint').text()).toContain('Value must be higher than 10');
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
it('validates and updates the computedValue when the user types in the input field', async () => {
|
|
@@ -87,11 +87,29 @@ describe('NumberInput.vue', () => {
|
|
|
87
87
|
},
|
|
88
88
|
});
|
|
89
89
|
const input = wrapper.find('input');
|
|
90
|
+
await input.setValue('1.1.1');
|
|
91
|
+
await input.trigger('focusout');
|
|
92
|
+
expect(wrapper.vm.modelValue).toEqual(5);
|
|
93
|
+
|
|
90
94
|
await input.setValue('15');
|
|
91
|
-
|
|
95
|
+
await input.trigger('focusout');
|
|
96
|
+
expect(wrapper.vm.modelValue).toEqual(15);
|
|
97
|
+
|
|
98
|
+
await input.setValue('.');
|
|
99
|
+
await input.trigger('focusout');
|
|
100
|
+
expect(wrapper.vm.modelValue).toEqual(0);
|
|
101
|
+
|
|
102
|
+
await input.setValue(',');
|
|
103
|
+
await input.trigger('focusout');
|
|
104
|
+
expect(wrapper.vm.modelValue).toEqual(0);
|
|
105
|
+
|
|
106
|
+
await input.setValue('1,1');
|
|
107
|
+
await input.trigger('focusout');
|
|
108
|
+
expect(wrapper.vm.modelValue).toEqual(1.1);
|
|
109
|
+
expect(input.element.value).toEqual('1.1');
|
|
92
110
|
});
|
|
93
111
|
|
|
94
|
-
it('
|
|
112
|
+
it('update model with undefined when input is cleared', async () => {
|
|
95
113
|
const wrapper = mount(PlNumberField, {
|
|
96
114
|
props: {
|
|
97
115
|
modelValue: 10,
|
|
@@ -99,6 +117,7 @@ describe('NumberInput.vue', () => {
|
|
|
99
117
|
});
|
|
100
118
|
const input = wrapper.find('input');
|
|
101
119
|
await input.setValue('');
|
|
102
|
-
|
|
120
|
+
await input.trigger('focusout');
|
|
121
|
+
expect(wrapper.vm.modelValue).toEqual(undefined);
|
|
103
122
|
});
|
|
104
123
|
});
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
// background-color: rgb(111, 94, 94);
|
|
26
26
|
border-radius: 6px;
|
|
27
27
|
}
|
|
28
|
+
&__wrapper.withoutArrows {
|
|
29
|
+
padding-right: 12px;
|
|
30
|
+
}
|
|
28
31
|
|
|
29
32
|
&__icons {
|
|
30
33
|
// background-color: green;
|
|
@@ -67,6 +70,7 @@
|
|
|
67
70
|
border: none;
|
|
68
71
|
width: 100%;
|
|
69
72
|
background: unset;
|
|
73
|
+
text-overflow: ellipsis;
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
&__contour {
|