@snack-uikit/fields 0.18.3 → 0.18.4-preview-5d3667ec.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/dist/components/FieldSlider/FieldSlider.js +63 -19
- package/dist/components/FieldSlider/helpers/getClosestMark.d.ts +2 -2
- package/dist/components/FieldSlider/helpers/getClosestMark.js +3 -3
- package/dist/components/FieldSlider/helpers/index.d.ts +1 -0
- package/dist/components/FieldSlider/helpers/index.js +1 -0
- package/dist/components/FieldSlider/helpers/isMarkObject.d.ts +8 -0
- package/dist/components/FieldSlider/helpers/isMarkObject.js +3 -0
- package/package.json +4 -4
- package/src/components/FieldSlider/FieldSlider.tsx +93 -19
- package/src/components/FieldSlider/helpers/getClosestMark.ts +7 -3
- package/src/components/FieldSlider/helpers/index.ts +1 -0
- package/src/components/FieldSlider/helpers/isMarkObject.ts +13 -0
|
@@ -11,7 +11,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
import mergeRefs from 'merge-refs';
|
|
14
|
-
import { forwardRef, useEffect, useRef, useState } from 'react';
|
|
14
|
+
import { forwardRef, useCallback, useEffect, useMemo, useRef, useState, } from 'react';
|
|
15
15
|
import { InputPrivate, SIZE } from '@snack-uikit/input-private';
|
|
16
16
|
import { Slider } from '@snack-uikit/slider';
|
|
17
17
|
import { extractSupportProps } from '@snack-uikit/utils';
|
|
@@ -19,7 +19,7 @@ import { CONTAINER_VARIANT, VALIDATION_STATE } from '../../constants';
|
|
|
19
19
|
import { FieldContainerPrivate } from '../../helperComponents';
|
|
20
20
|
import { useValueControl } from '../../hooks';
|
|
21
21
|
import { FieldDecorator } from '../FieldDecorator';
|
|
22
|
-
import { generateAllowedValues, getClosestMark, getTextFieldValue } from './helpers';
|
|
22
|
+
import { generateAllowedValues, getClosestMark, getTextFieldValue, isMarkObject } from './helpers';
|
|
23
23
|
import styles from './styles.module.css';
|
|
24
24
|
const getDefaultValue = (range, min, max, value) => {
|
|
25
25
|
if (range) {
|
|
@@ -32,6 +32,14 @@ const getDefaultValue = (range, min, max, value) => {
|
|
|
32
32
|
};
|
|
33
33
|
export const FieldSlider = forwardRef((_a, ref) => {
|
|
34
34
|
var { id, name, min, max, step, marks, showScaleBar = true, value: valueProp, range = false, disabled = false, readonly = false, onChange: onChangeProp, onFocus, onBlur, className, label, labelTooltip, labelTooltipPlacement, required, hint, showHintIcon, size = SIZE.S, postfixIcon, textInputFormatter } = _a, rest = __rest(_a, ["id", "name", "min", "max", "step", "marks", "showScaleBar", "value", "range", "disabled", "readonly", "onChange", "onFocus", "onBlur", "className", "label", "labelTooltip", "labelTooltipPlacement", "required", "hint", "showHintIcon", "size", "postfixIcon", "textInputFormatter"]);
|
|
35
|
+
const getMarkValue = useCallback((key) => {
|
|
36
|
+
const mark = marks[key];
|
|
37
|
+
if (isMarkObject(mark)) {
|
|
38
|
+
return mark.label;
|
|
39
|
+
}
|
|
40
|
+
return mark;
|
|
41
|
+
}, [marks]);
|
|
42
|
+
const hasMarksEqualToValues = useMemo(() => Object.keys(marks).every(key => key === getMarkValue(key)), [getMarkValue, marks]);
|
|
35
43
|
const [value = getDefaultValue(range, min, max, valueProp), onChange] = useValueControl({
|
|
36
44
|
value: valueProp,
|
|
37
45
|
defaultValue: getDefaultValue(range, min, max, valueProp),
|
|
@@ -40,46 +48,82 @@ export const FieldSlider = forwardRef((_a, ref) => {
|
|
|
40
48
|
const [textFieldInputValue, setTextFieldInputValue] = useState(getTextFieldValue(value, textInputFormatter));
|
|
41
49
|
const localRef = useRef(null);
|
|
42
50
|
const onTextFieldChange = (textFieldValue) => {
|
|
43
|
-
const numValue =
|
|
44
|
-
if (Number.isNaN(numValue)) {
|
|
51
|
+
const numValue = parseInt(textFieldValue);
|
|
52
|
+
if (textFieldValue && Number.isNaN(numValue)) {
|
|
45
53
|
return;
|
|
46
54
|
}
|
|
47
55
|
setTextFieldInputValue(textFieldValue);
|
|
48
56
|
};
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
|
|
57
|
+
const handleNonEqualMarksSliderChange = (textFieldNumValue) => {
|
|
58
|
+
const handleChange = (key) => {
|
|
59
|
+
setTextFieldInputValue(String(getMarkValue(key)));
|
|
60
|
+
onChange(Number(key));
|
|
61
|
+
};
|
|
62
|
+
const allowedValues = Object.keys(marks).map(key => ({
|
|
63
|
+
key,
|
|
64
|
+
value: parseInt(String(getMarkValue(key))),
|
|
65
|
+
}));
|
|
66
|
+
const suitableEntry = allowedValues.find(entry => entry.value === textFieldNumValue);
|
|
67
|
+
if (suitableEntry) {
|
|
68
|
+
handleChange(suitableEntry.key);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const actualMin = parseInt(String(getMarkValue(min)));
|
|
72
|
+
const actualMax = parseInt(String(getMarkValue(max)));
|
|
73
|
+
if (textFieldNumValue < actualMin) {
|
|
74
|
+
handleChange(min);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (textFieldNumValue > actualMax) {
|
|
78
|
+
handleChange(max);
|
|
52
79
|
return;
|
|
53
80
|
}
|
|
81
|
+
const { mark } = getClosestMark(textFieldNumValue, allowedValues, mark => mark.value);
|
|
82
|
+
handleChange(mark.key);
|
|
83
|
+
};
|
|
84
|
+
const handleEqualMarksSliderChange = (textFieldNumValue) => {
|
|
85
|
+
const handleChange = (value) => {
|
|
86
|
+
setTextFieldInputValue(String(value));
|
|
87
|
+
onChange(value);
|
|
88
|
+
};
|
|
54
89
|
if (textFieldNumValue < min) {
|
|
55
|
-
|
|
56
|
-
onChange(min);
|
|
90
|
+
handleChange(min);
|
|
57
91
|
return;
|
|
58
92
|
}
|
|
59
93
|
if (textFieldNumValue > max) {
|
|
60
|
-
|
|
61
|
-
onChange(max);
|
|
94
|
+
handleChange(max);
|
|
62
95
|
return;
|
|
63
96
|
}
|
|
64
97
|
if (step === null) {
|
|
65
98
|
const allowedValues = Object.keys(marks).map(Number);
|
|
66
99
|
if (allowedValues.includes(textFieldNumValue)) {
|
|
67
|
-
|
|
100
|
+
setTextFieldInputValue(String(textFieldNumValue));
|
|
101
|
+
handleChange(textFieldNumValue);
|
|
68
102
|
return;
|
|
69
103
|
}
|
|
70
|
-
const { mark } = getClosestMark(textFieldNumValue, allowedValues);
|
|
71
|
-
|
|
72
|
-
onChange(mark);
|
|
104
|
+
const { mark } = getClosestMark(textFieldNumValue, allowedValues, mark => mark);
|
|
105
|
+
handleChange(mark);
|
|
73
106
|
return;
|
|
74
107
|
}
|
|
75
108
|
const allowedValues = generateAllowedValues(min, max, step);
|
|
76
109
|
if (allowedValues.includes(textFieldNumValue)) {
|
|
77
|
-
|
|
110
|
+
handleChange(textFieldNumValue);
|
|
78
111
|
return;
|
|
79
112
|
}
|
|
80
|
-
const { mark } = getClosestMark(textFieldNumValue, allowedValues);
|
|
81
|
-
|
|
82
|
-
|
|
113
|
+
const { mark } = getClosestMark(textFieldNumValue, allowedValues, mark => mark);
|
|
114
|
+
handleChange(mark);
|
|
115
|
+
};
|
|
116
|
+
const handleTextValueChange = () => {
|
|
117
|
+
const textFieldNumValue = parseInt(textFieldInputValue);
|
|
118
|
+
if (Number.isNaN(textFieldNumValue)) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (hasMarksEqualToValues) {
|
|
122
|
+
handleEqualMarksSliderChange(textFieldNumValue);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
handleNonEqualMarksSliderChange(textFieldNumValue);
|
|
126
|
+
}
|
|
83
127
|
};
|
|
84
128
|
const onTextFieldBlur = (e) => {
|
|
85
129
|
onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const getDiff = (value, mark) => Math.abs(mark - value);
|
|
2
|
-
export const getClosestMark = (value, marks) => marks.reduce((accResult, mark) => {
|
|
3
|
-
const diff = getDiff(value, mark);
|
|
2
|
+
export const getClosestMark = (value, marks, getMarkValue) => marks.reduce((accResult, mark) => {
|
|
3
|
+
const diff = getDiff(value, getMarkValue(mark));
|
|
4
4
|
if (diff < accResult.lowestDiff) {
|
|
5
5
|
return {
|
|
6
6
|
lowestDiff: diff,
|
|
@@ -9,6 +9,6 @@ export const getClosestMark = (value, marks) => marks.reduce((accResult, mark) =
|
|
|
9
9
|
}
|
|
10
10
|
return accResult;
|
|
11
11
|
}, {
|
|
12
|
-
lowestDiff: getDiff(value, marks[0]),
|
|
12
|
+
lowestDiff: getDiff(value, getMarkValue(marks[0])),
|
|
13
13
|
mark: marks[0],
|
|
14
14
|
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { SliderProps as SliderComponentProps } from '@snack-uikit/slider';
|
|
3
|
+
type Marks = NonNullable<SliderComponentProps['marks']>;
|
|
4
|
+
type MarkObject = {
|
|
5
|
+
label: ReactNode;
|
|
6
|
+
};
|
|
7
|
+
export declare function isMarkObject(mark: Marks[keyof Marks]): mark is MarkObject;
|
|
8
|
+
export {};
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
6
|
"title": "Fields",
|
|
7
|
-
"version": "0.18.
|
|
7
|
+
"version": "0.18.4-preview-5d3667ec.0",
|
|
8
8
|
"sideEffects": [
|
|
9
9
|
"*.css",
|
|
10
10
|
"*.woff",
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
"@snack-uikit/droplist": "0.13.15",
|
|
38
38
|
"@snack-uikit/icons": "0.20.1",
|
|
39
39
|
"@snack-uikit/input-private": "3.1.1",
|
|
40
|
-
"@snack-uikit/list": "0.11.0",
|
|
40
|
+
"@snack-uikit/list": "0.11.1-preview-5d3667ec.0",
|
|
41
41
|
"@snack-uikit/scroll": "0.5.2",
|
|
42
|
-
"@snack-uikit/slider": "0.1.
|
|
42
|
+
"@snack-uikit/slider": "0.1.8-preview-5d3667ec.0",
|
|
43
43
|
"@snack-uikit/tag": "0.9.0",
|
|
44
44
|
"@snack-uikit/tooltip": "0.13.1",
|
|
45
45
|
"@snack-uikit/truncate-string": "0.4.12",
|
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@snack-uikit/locale": "*"
|
|
58
58
|
},
|
|
59
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "311bf31807d9b8cc975e80caf14ec35108256337"
|
|
60
60
|
}
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import mergeRefs from 'merge-refs';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
FocusEvent,
|
|
4
|
+
forwardRef,
|
|
5
|
+
KeyboardEvent,
|
|
6
|
+
ReactElement,
|
|
7
|
+
useCallback,
|
|
8
|
+
useEffect,
|
|
9
|
+
useMemo,
|
|
10
|
+
useRef,
|
|
11
|
+
useState,
|
|
12
|
+
} from 'react';
|
|
3
13
|
|
|
4
14
|
import { InputPrivate, InputPrivateProps, SIZE } from '@snack-uikit/input-private';
|
|
5
15
|
import { Slider, SliderProps as SliderComponentProps } from '@snack-uikit/slider';
|
|
@@ -9,7 +19,7 @@ import { CONTAINER_VARIANT, VALIDATION_STATE } from '../../constants';
|
|
|
9
19
|
import { FieldContainerPrivate } from '../../helperComponents';
|
|
10
20
|
import { useValueControl } from '../../hooks';
|
|
11
21
|
import { FieldDecorator, FieldDecoratorProps } from '../FieldDecorator';
|
|
12
|
-
import { generateAllowedValues, getClosestMark, getTextFieldValue } from './helpers';
|
|
22
|
+
import { generateAllowedValues, getClosestMark, getTextFieldValue, isMarkObject } from './helpers';
|
|
13
23
|
import styles from './styles.module.scss';
|
|
14
24
|
import { TextInputFormatter } from './types';
|
|
15
25
|
|
|
@@ -81,6 +91,24 @@ export const FieldSlider = forwardRef<HTMLInputElement, FieldSliderProps>(
|
|
|
81
91
|
},
|
|
82
92
|
ref,
|
|
83
93
|
) => {
|
|
94
|
+
const getMarkValue = useCallback(
|
|
95
|
+
(key: keyof SliderProps['marks']) => {
|
|
96
|
+
const mark = marks[key];
|
|
97
|
+
|
|
98
|
+
if (isMarkObject(mark)) {
|
|
99
|
+
return mark.label;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return mark;
|
|
103
|
+
},
|
|
104
|
+
[marks],
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const hasMarksEqualToValues = useMemo(
|
|
108
|
+
() => Object.keys(marks).every(key => key === getMarkValue(key)),
|
|
109
|
+
[getMarkValue, marks],
|
|
110
|
+
);
|
|
111
|
+
|
|
84
112
|
const [value = getDefaultValue(range, min, max, valueProp), onChange] = useValueControl<number | number[]>({
|
|
85
113
|
value: valueProp,
|
|
86
114
|
defaultValue: getDefaultValue(range, min, max, valueProp),
|
|
@@ -93,54 +121,100 @@ export const FieldSlider = forwardRef<HTMLInputElement, FieldSliderProps>(
|
|
|
93
121
|
const localRef = useRef<HTMLInputElement>(null);
|
|
94
122
|
|
|
95
123
|
const onTextFieldChange = (textFieldValue: string) => {
|
|
96
|
-
const numValue =
|
|
97
|
-
|
|
124
|
+
const numValue = parseInt(textFieldValue);
|
|
125
|
+
|
|
126
|
+
if (textFieldValue && Number.isNaN(numValue)) {
|
|
98
127
|
return;
|
|
99
128
|
}
|
|
100
129
|
|
|
101
130
|
setTextFieldInputValue(textFieldValue);
|
|
102
131
|
};
|
|
103
132
|
|
|
104
|
-
const
|
|
105
|
-
const
|
|
106
|
-
|
|
133
|
+
const handleNonEqualMarksSliderChange = (textFieldNumValue: number) => {
|
|
134
|
+
const handleChange = (key: string | number) => {
|
|
135
|
+
setTextFieldInputValue(String(getMarkValue(key)));
|
|
136
|
+
onChange(Number(key));
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const allowedValues = Object.keys(marks).map(key => ({
|
|
140
|
+
key,
|
|
141
|
+
value: parseInt(String(getMarkValue(key))),
|
|
142
|
+
}));
|
|
143
|
+
const suitableEntry = allowedValues.find(entry => entry.value === textFieldNumValue);
|
|
144
|
+
|
|
145
|
+
if (suitableEntry) {
|
|
146
|
+
handleChange(suitableEntry.key);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const actualMin = parseInt(String(getMarkValue(min)));
|
|
151
|
+
const actualMax = parseInt(String(getMarkValue(max)));
|
|
152
|
+
|
|
153
|
+
if (textFieldNumValue < actualMin) {
|
|
154
|
+
handleChange(min);
|
|
107
155
|
return;
|
|
108
156
|
}
|
|
109
157
|
|
|
158
|
+
if (textFieldNumValue > actualMax) {
|
|
159
|
+
handleChange(max);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const { mark } = getClosestMark(textFieldNumValue, allowedValues, mark => mark.value);
|
|
164
|
+
handleChange(mark.key);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const handleEqualMarksSliderChange = (textFieldNumValue: number) => {
|
|
168
|
+
const handleChange = (value: number) => {
|
|
169
|
+
setTextFieldInputValue(String(value));
|
|
170
|
+
onChange(value);
|
|
171
|
+
};
|
|
172
|
+
|
|
110
173
|
if (textFieldNumValue < min) {
|
|
111
|
-
|
|
112
|
-
onChange(min);
|
|
174
|
+
handleChange(min);
|
|
113
175
|
return;
|
|
114
176
|
}
|
|
115
177
|
|
|
116
178
|
if (textFieldNumValue > max) {
|
|
117
|
-
|
|
118
|
-
onChange(max);
|
|
179
|
+
handleChange(max);
|
|
119
180
|
return;
|
|
120
181
|
}
|
|
121
182
|
|
|
122
183
|
if (step === null) {
|
|
123
184
|
const allowedValues = Object.keys(marks).map(Number);
|
|
124
185
|
if (allowedValues.includes(textFieldNumValue)) {
|
|
125
|
-
|
|
186
|
+
setTextFieldInputValue(String(textFieldNumValue));
|
|
187
|
+
handleChange(textFieldNumValue);
|
|
126
188
|
return;
|
|
127
189
|
}
|
|
128
190
|
|
|
129
|
-
const { mark } = getClosestMark(textFieldNumValue, allowedValues);
|
|
130
|
-
|
|
131
|
-
onChange(mark);
|
|
191
|
+
const { mark } = getClosestMark(textFieldNumValue, allowedValues, mark => mark);
|
|
192
|
+
handleChange(mark);
|
|
132
193
|
return;
|
|
133
194
|
}
|
|
134
195
|
|
|
135
196
|
const allowedValues = generateAllowedValues(min, max, step);
|
|
136
197
|
if (allowedValues.includes(textFieldNumValue)) {
|
|
137
|
-
|
|
198
|
+
handleChange(textFieldNumValue);
|
|
138
199
|
return;
|
|
139
200
|
}
|
|
140
201
|
|
|
141
|
-
const { mark } = getClosestMark(textFieldNumValue, allowedValues);
|
|
142
|
-
|
|
143
|
-
|
|
202
|
+
const { mark } = getClosestMark(textFieldNumValue, allowedValues, mark => mark);
|
|
203
|
+
handleChange(mark);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const handleTextValueChange = () => {
|
|
207
|
+
const textFieldNumValue = parseInt(textFieldInputValue);
|
|
208
|
+
|
|
209
|
+
if (Number.isNaN(textFieldNumValue)) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (hasMarksEqualToValues) {
|
|
214
|
+
handleEqualMarksSliderChange(textFieldNumValue);
|
|
215
|
+
} else {
|
|
216
|
+
handleNonEqualMarksSliderChange(textFieldNumValue);
|
|
217
|
+
}
|
|
144
218
|
};
|
|
145
219
|
|
|
146
220
|
const onTextFieldBlur = (e: FocusEvent<HTMLInputElement, Element>) => {
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
const getDiff = (value: number, mark: number): number => Math.abs(mark - value);
|
|
2
2
|
|
|
3
|
-
export const getClosestMark = (
|
|
3
|
+
export const getClosestMark = <T>(
|
|
4
|
+
value: number,
|
|
5
|
+
marks: T[],
|
|
6
|
+
getMarkValue: (value: T) => number,
|
|
7
|
+
): { lowestDiff: number; mark: T } =>
|
|
4
8
|
marks.reduce(
|
|
5
9
|
(accResult, mark) => {
|
|
6
|
-
const diff = getDiff(value, mark);
|
|
10
|
+
const diff = getDiff(value, getMarkValue(mark));
|
|
7
11
|
if (diff < accResult.lowestDiff) {
|
|
8
12
|
return {
|
|
9
13
|
lowestDiff: diff,
|
|
@@ -14,7 +18,7 @@ export const getClosestMark = (value: number, marks: number[]): { lowestDiff: nu
|
|
|
14
18
|
return accResult;
|
|
15
19
|
},
|
|
16
20
|
{
|
|
17
|
-
lowestDiff: getDiff(value, marks[0]),
|
|
21
|
+
lowestDiff: getDiff(value, getMarkValue(marks[0])),
|
|
18
22
|
mark: marks[0],
|
|
19
23
|
},
|
|
20
24
|
);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import { SliderProps as SliderComponentProps } from '@snack-uikit/slider';
|
|
4
|
+
|
|
5
|
+
type Marks = NonNullable<SliderComponentProps['marks']>;
|
|
6
|
+
|
|
7
|
+
type MarkObject = {
|
|
8
|
+
label: ReactNode;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function isMarkObject(mark: Marks[keyof Marks]): mark is MarkObject {
|
|
12
|
+
return Boolean(mark && typeof mark === 'object' && 'label' in mark);
|
|
13
|
+
}
|