@snack-uikit/fields 0.29.1 → 0.29.2-preview-eb3592d8.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/README.md +8 -2
- package/dist/components/FieldDate/FieldDate.d.ts +10 -3
- package/dist/components/FieldDate/FieldDate.js +15 -15
- package/dist/components/FieldDate/constants.d.ts +12 -5
- package/dist/components/FieldDate/constants.js +36 -11
- package/dist/components/FieldDate/hooks/useDateField.d.ts +5 -3
- package/dist/components/FieldDate/hooks/useDateField.js +34 -23
- package/dist/components/FieldDate/hooks/useDateFieldHelpers.js +5 -5
- package/dist/components/FieldDate/hooks/useDateTimeFieldHelpers.d.ts +10 -0
- package/dist/components/FieldDate/hooks/useDateTimeFieldHelpers.js +75 -0
- package/dist/components/FieldDate/types.d.ts +3 -0
- package/dist/components/FieldDate/utils.d.ts +9 -4
- package/dist/components/FieldDate/utils.js +70 -8
- package/package.json +4 -4
- package/src/components/FieldDate/FieldDate.tsx +25 -17
- package/src/components/FieldDate/constants.ts +59 -15
- package/src/components/FieldDate/hooks/useDateField.ts +51 -28
- package/src/components/FieldDate/hooks/useDateFieldHelpers.ts +5 -5
- package/src/components/FieldDate/hooks/useDateTimeFieldHelpers.ts +109 -0
- package/src/components/FieldDate/types.ts +6 -0
- package/src/components/FieldDate/utils.ts +80 -10
package/README.md
CHANGED
|
@@ -161,6 +161,11 @@ const [isOpen, setIsOpen] = useState(false);
|
|
|
161
161
|
enableFuzzySearch={true}
|
|
162
162
|
/>;
|
|
163
163
|
```
|
|
164
|
+
## Особенности работы FieldSlider-a
|
|
165
|
+
|
|
166
|
+
Для корректной работы компонента, свойство `marks` должно быть либо **константой**, либо **мемоизировано**,
|
|
167
|
+
т.к. это на этой свойство завязана логика обновления ui.
|
|
168
|
+
|
|
164
169
|
## Особенности работы FieldStepper-a
|
|
165
170
|
|
|
166
171
|
FieldStepper в основном предназначен для работы с небольшими числами,
|
|
@@ -316,10 +321,11 @@ FieldStepper в основном предназначен для работы с
|
|
|
316
321
|
### Props
|
|
317
322
|
| name | type | default value | description |
|
|
318
323
|
|------|------|---------------|-------------|
|
|
319
|
-
|
|
|
324
|
+
| mode* | enum Mode: `"date"`, `"date-time"` | - | |
|
|
320
325
|
| open | `boolean` | - | Открыт date-picker |
|
|
321
326
|
| onOpenChange | `(value: boolean) => void` | - | Колбек открытия пикера |
|
|
322
|
-
|
|
|
327
|
+
| value | `Date` | - | Значение поля |
|
|
328
|
+
| onChange | `(value: Date) => void` | - | Колбек смены значения |
|
|
323
329
|
| showCopyButton | `boolean` | - | Отображение кнопки копирования |
|
|
324
330
|
| showClearButton | `boolean` | true | Отображение кнопки Очистки поля |
|
|
325
331
|
| locale | `Locale` | new Intl.Locale('ru-RU') | Текущая локаль календаря |
|
|
@@ -2,15 +2,18 @@ import { CalendarProps } from '@snack-uikit/calendar';
|
|
|
2
2
|
import { InputPrivateProps } from '@snack-uikit/input-private';
|
|
3
3
|
import { WithSupportProps } from '@snack-uikit/utils';
|
|
4
4
|
import { FieldDecoratorProps } from '../FieldDecorator';
|
|
5
|
-
|
|
5
|
+
import { Mode } from './types';
|
|
6
|
+
type InputProps = Pick<InputPrivateProps, 'id' | 'name' | 'disabled' | 'readonly' | 'onFocus' | 'onBlur'>;
|
|
6
7
|
type WrapperProps = Pick<FieldDecoratorProps, 'className' | 'label' | 'labelTooltip' | 'required' | 'caption' | 'hint' | 'showHintIcon' | 'size' | 'validationState' | 'labelTooltipPlacement' | 'error'>;
|
|
7
8
|
type FieldDateOwnProps = {
|
|
8
9
|
/** Открыт date-picker */
|
|
9
10
|
open?: boolean;
|
|
10
11
|
/** Колбек открытия пикера */
|
|
11
12
|
onOpenChange?(value: boolean): void;
|
|
13
|
+
/** Значение поля */
|
|
14
|
+
value?: Date;
|
|
12
15
|
/** Колбек смены значения */
|
|
13
|
-
onChange?(value:
|
|
16
|
+
onChange?(value: Date | undefined): void;
|
|
14
17
|
/** Отображение кнопки копирования */
|
|
15
18
|
showCopyButton?: boolean;
|
|
16
19
|
/**
|
|
@@ -20,6 +23,7 @@ type FieldDateOwnProps = {
|
|
|
20
23
|
showClearButton?: boolean;
|
|
21
24
|
/** Текущая локаль календаря */
|
|
22
25
|
locale?: Intl.Locale;
|
|
26
|
+
mode: Mode;
|
|
23
27
|
} & Pick<CalendarProps, 'buildCellProps'>;
|
|
24
28
|
export type FieldDateProps = WithSupportProps<FieldDateOwnProps & InputProps & WrapperProps>;
|
|
25
29
|
export declare const FieldDate: import("react").ForwardRefExoticComponent<{
|
|
@@ -29,8 +33,10 @@ export declare const FieldDate: import("react").ForwardRefExoticComponent<{
|
|
|
29
33
|
open?: boolean;
|
|
30
34
|
/** Колбек открытия пикера */
|
|
31
35
|
onOpenChange?(value: boolean): void;
|
|
36
|
+
/** Значение поля */
|
|
37
|
+
value?: Date;
|
|
32
38
|
/** Колбек смены значения */
|
|
33
|
-
onChange?(value:
|
|
39
|
+
onChange?(value: Date | undefined): void;
|
|
34
40
|
/** Отображение кнопки копирования */
|
|
35
41
|
showCopyButton?: boolean;
|
|
36
42
|
/**
|
|
@@ -40,5 +46,6 @@ export declare const FieldDate: import("react").ForwardRefExoticComponent<{
|
|
|
40
46
|
showClearButton?: boolean;
|
|
41
47
|
/** Текущая локаль календаря */
|
|
42
48
|
locale?: Intl.Locale;
|
|
49
|
+
mode: Mode;
|
|
43
50
|
} & Pick<CalendarProps, "buildCellProps"> & InputProps & WrapperProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
44
51
|
export {};
|
|
@@ -28,14 +28,14 @@ import { useDateField } from './hooks';
|
|
|
28
28
|
import { useFocusHandlers } from './hooks/useFocusHandlers';
|
|
29
29
|
import { useHandlers } from './hooks/useHandlers';
|
|
30
30
|
import styles from './styles.module.css';
|
|
31
|
-
import { parseDate } from './utils';
|
|
32
31
|
const CALENDAR_SIZE_MAP = {
|
|
33
32
|
[SIZE.S]: 's',
|
|
34
33
|
[SIZE.M]: 'm',
|
|
35
|
-
[SIZE.L]: '
|
|
34
|
+
[SIZE.L]: 'l',
|
|
36
35
|
};
|
|
37
36
|
export const FieldDate = forwardRef((_a, ref) => {
|
|
38
|
-
var
|
|
37
|
+
var _b;
|
|
38
|
+
var { id, name, value: valueProp, disabled = false, readonly = false, showCopyButton: showCopyButtonProp = true, showClearButton: showClearButtonProp = true, open, onOpenChange, onChange, onFocus, onBlur: onBlurProp, className, label, labelTooltip, labelTooltipPlacement, required = false, caption, hint, showHintIcon, size = SIZE.S, validationState = VALIDATION_STATE.Default, locale = DEFAULT_LOCALE, buildCellProps, error, mode } = _a, rest = __rest(_a, ["id", "name", "value", "disabled", "readonly", "showCopyButton", "showClearButton", "open", "onOpenChange", "onChange", "onFocus", "onBlur", "className", "label", "labelTooltip", "labelTooltipPlacement", "required", "caption", "hint", "showHintIcon", "size", "validationState", "locale", "buildCellProps", "error", "mode"]);
|
|
39
39
|
const [isOpen, setIsOpen] = useUncontrolledProp(open, false, onOpenChange);
|
|
40
40
|
const [pickerAutofocus, setPickerAutofocus] = useState(false);
|
|
41
41
|
const localRef = useRef(null);
|
|
@@ -55,7 +55,7 @@ export const FieldDate = forwardRef((_a, ref) => {
|
|
|
55
55
|
}, [setIsOpen]);
|
|
56
56
|
const handleClear = useCallback(() => {
|
|
57
57
|
var _a, _b, _c;
|
|
58
|
-
onChange && onChange(
|
|
58
|
+
onChange && onChange(undefined);
|
|
59
59
|
if ((_a = localRef.current) === null || _a === void 0 ? void 0 : _a.value) {
|
|
60
60
|
localRef.current.value = '';
|
|
61
61
|
}
|
|
@@ -68,8 +68,9 @@ export const FieldDate = forwardRef((_a, ref) => {
|
|
|
68
68
|
setIsOpen(false);
|
|
69
69
|
}
|
|
70
70
|
}, [onChange, required, setIsOpen]);
|
|
71
|
+
const valueToCopy = (_b = (mode === 'date' ? valueProp === null || valueProp === void 0 ? void 0 : valueProp.toLocaleDateString() : valueProp === null || valueProp === void 0 ? void 0 : valueProp.toLocaleString())) !== null && _b !== void 0 ? _b : '';
|
|
71
72
|
const clearButtonSettings = useClearButton({ clearButtonRef, showClearButton, size, onClear: handleClear });
|
|
72
|
-
const copyButtonSettings = useCopyButton({ copyButtonRef, showCopyButton, size, valueToCopy
|
|
73
|
+
const copyButtonSettings = useCopyButton({ copyButtonRef, showCopyButton, size, valueToCopy });
|
|
73
74
|
const calendarIcon = useMemo(() => ({
|
|
74
75
|
active: false,
|
|
75
76
|
show: true,
|
|
@@ -83,8 +84,9 @@ export const FieldDate = forwardRef((_a, ref) => {
|
|
|
83
84
|
readonly,
|
|
84
85
|
locale,
|
|
85
86
|
setIsOpen,
|
|
87
|
+
mode,
|
|
86
88
|
});
|
|
87
|
-
const setInputFocusFromButtons = useCallback(() => setInputFocus(SlotKey.Year), [setInputFocus]);
|
|
89
|
+
const setInputFocusFromButtons = useCallback(() => setInputFocus(mode === 'date' ? SlotKey.Year : SlotKey.Seconds), [mode, setInputFocus]);
|
|
88
90
|
const { postfixButtons, inputTabIndex, onInputKeyDown: navigationInputKeyDownHandler, setInitialTabIndices, } = useButtonNavigation({
|
|
89
91
|
setInputFocus: setInputFocusFromButtons,
|
|
90
92
|
inputRef: localRef,
|
|
@@ -93,12 +95,15 @@ export const FieldDate = forwardRef((_a, ref) => {
|
|
|
93
95
|
readonly,
|
|
94
96
|
submitKeys: ['Enter', 'Space', 'Tab'],
|
|
95
97
|
});
|
|
96
|
-
// TODO: do not hardcode locale here
|
|
97
98
|
const handleSelectDate = (date) => {
|
|
98
99
|
var _a;
|
|
99
|
-
onChange && onChange(date
|
|
100
|
+
onChange && onChange(date);
|
|
100
101
|
(_a = localRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
101
102
|
setIsOpen(false);
|
|
103
|
+
if (localRef.current) {
|
|
104
|
+
localRef.current.value =
|
|
105
|
+
mode === 'date' ? date.toLocaleDateString(DEFAULT_LOCALE) : date.toLocaleString(DEFAULT_LOCALE);
|
|
106
|
+
}
|
|
102
107
|
};
|
|
103
108
|
const handleCalendarFocusLeave = () => {
|
|
104
109
|
setInitialTabIndices();
|
|
@@ -120,11 +125,6 @@ export const FieldDate = forwardRef((_a, ref) => {
|
|
|
120
125
|
(_a = localRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
121
126
|
}
|
|
122
127
|
}, [open]);
|
|
123
|
-
useEffect(() => {
|
|
124
|
-
if (localRef.current) {
|
|
125
|
-
localRef.current.value = valueProp;
|
|
126
|
-
}
|
|
127
|
-
}, [valueProp]);
|
|
128
128
|
const onFocusByKeyboard = useCallback((e) => {
|
|
129
129
|
setInputFocus();
|
|
130
130
|
onFocus === null || onFocus === void 0 ? void 0 : onFocus(e);
|
|
@@ -146,10 +146,10 @@ export const FieldDate = forwardRef((_a, ref) => {
|
|
|
146
146
|
: {
|
|
147
147
|
open: showDropList,
|
|
148
148
|
onOpenChange: setIsOpen,
|
|
149
|
-
}), { content: _jsx("div", { className: styles.calendarWrapper, "data-size": size, children: _jsx(Calendar, { mode:
|
|
149
|
+
}), { content: _jsx("div", { className: styles.calendarWrapper, "data-size": size, children: _jsx(Calendar, { mode: mode, size: CALENDAR_SIZE_MAP[size], value: valueProp, onChangeValue: handleSelectDate, buildCellProps: buildCellProps, navigationStartRef: element => {
|
|
150
150
|
if (pickerAutofocus) {
|
|
151
151
|
element === null || element === void 0 ? void 0 : element.focus();
|
|
152
152
|
setPickerAutofocus(false);
|
|
153
153
|
}
|
|
154
|
-
}, onFocusLeave: handleCalendarFocusLeave, locale: locale, "data-test-id": 'field-date__calendar' }) }), children: _jsx(FieldContainerPrivate, { className: styles.container, size: size, validationState: fieldValidationState, disabled: disabled, readonly: readonly, variant: CONTAINER_VARIANT.SingleLine, focused: showDropList, inputRef: localRef, postfix: postfixButtons, children: _jsx(InputPrivate, { ref: mergeRefs(ref, localRef), "data-size": size, value: value || '', placeholder: mask, onChange: handleChange, onFocus: inputHandlers.onFocus, onMouseDown: inputHandlers.onMouseDown, onBlur: onBlur, onKeyDown: handleInputKeyDown, onClick: onClick, disabled: disabled, readonly: readonly, tabIndex: inputTabIndex, type: 'text', id: id, name: name, "data-test-id": 'field-date__input' }) }) })) })));
|
|
154
|
+
}, onFocusLeave: handleCalendarFocusLeave, locale: locale, "data-test-id": 'field-date__calendar', fitToContainer: false }) }), children: _jsx(FieldContainerPrivate, { className: styles.container, size: size, validationState: fieldValidationState, disabled: disabled, readonly: readonly, variant: CONTAINER_VARIANT.SingleLine, focused: showDropList, inputRef: localRef, postfix: postfixButtons, children: _jsx(InputPrivate, { ref: mergeRefs(ref, localRef), "data-size": size, value: value || '', placeholder: mask, onChange: handleChange, onFocus: inputHandlers.onFocus, onMouseDown: inputHandlers.onMouseDown, onBlur: onBlur, onKeyDown: handleInputKeyDown, onClick: onClick, disabled: disabled, readonly: readonly, tabIndex: inputTabIndex, type: 'text', id: id, name: name, "data-test-id": 'field-date__input' }) }) })) })));
|
|
155
155
|
});
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import { Slot } from './types';
|
|
1
|
+
import { Mode, Slot } from './types';
|
|
2
2
|
export declare enum SlotKey {
|
|
3
3
|
Day = "D",
|
|
4
4
|
Month = "M",
|
|
5
|
-
Year = "Y"
|
|
5
|
+
Year = "Y",
|
|
6
|
+
Hours = "h",
|
|
7
|
+
Minutes = "m",
|
|
8
|
+
Seconds = "s"
|
|
6
9
|
}
|
|
7
|
-
export declare const
|
|
10
|
+
export declare const MODES: {
|
|
11
|
+
readonly Date: "date";
|
|
12
|
+
readonly DateTime: "date-time";
|
|
13
|
+
};
|
|
14
|
+
export declare const MASK: Record<Mode, Record<string, string>>;
|
|
8
15
|
export declare const DEFAULT_LOCALE: Intl.Locale;
|
|
9
|
-
export declare const SLOTS: Record<SlotKey | string, Slot
|
|
10
|
-
export declare const SLOTS_PLACEHOLDER: Record<
|
|
16
|
+
export declare const SLOTS: Record<Mode, Record<SlotKey | string, Slot>>;
|
|
17
|
+
export declare const SLOTS_PLACEHOLDER: Record<Mode, Record<string, Partial<Record<SlotKey, string>>>>;
|
|
@@ -3,26 +3,51 @@ export var SlotKey;
|
|
|
3
3
|
SlotKey["Day"] = "D";
|
|
4
4
|
SlotKey["Month"] = "M";
|
|
5
5
|
SlotKey["Year"] = "Y";
|
|
6
|
+
SlotKey["Hours"] = "h";
|
|
7
|
+
SlotKey["Minutes"] = "m";
|
|
8
|
+
SlotKey["Seconds"] = "s";
|
|
6
9
|
})(SlotKey || (SlotKey = {}));
|
|
10
|
+
export const MODES = {
|
|
11
|
+
Date: 'date',
|
|
12
|
+
DateTime: 'date-time',
|
|
13
|
+
};
|
|
7
14
|
export const MASK = {
|
|
8
|
-
|
|
9
|
-
|
|
15
|
+
[MODES.Date]: {
|
|
16
|
+
'ru-RU': 'ДД.ММ.ГГГГ',
|
|
17
|
+
'en-US': 'DD.MM.YYYY',
|
|
18
|
+
},
|
|
19
|
+
[MODES.DateTime]: {
|
|
20
|
+
'ru-RU': 'ДД.ММ.ГГГГ, чч:мм:сс',
|
|
21
|
+
'en-US': 'DD.MM.YYYY, hh:mm:ss',
|
|
22
|
+
},
|
|
10
23
|
};
|
|
11
24
|
export const DEFAULT_LOCALE = new Intl.Locale('ru-RU');
|
|
12
|
-
|
|
25
|
+
const DATE_SLOTS = {
|
|
13
26
|
[SlotKey.Day]: { start: 0, end: 2, max: 31, min: 1 },
|
|
14
27
|
[SlotKey.Month]: { start: 3, end: 5, max: 12, min: 1 },
|
|
15
28
|
[SlotKey.Year]: { start: 6, end: 10, max: 2100, min: 1900 },
|
|
16
29
|
};
|
|
30
|
+
export const SLOTS = {
|
|
31
|
+
[MODES.Date]: DATE_SLOTS,
|
|
32
|
+
[MODES.DateTime]: Object.assign(Object.assign({}, DATE_SLOTS), { [SlotKey.Hours]: { start: 12, end: 14, max: 23, min: 0 }, [SlotKey.Minutes]: { start: 15, end: 17, max: 59, min: 0 }, [SlotKey.Seconds]: { start: 18, end: 20, max: 59, min: 0 } }),
|
|
33
|
+
};
|
|
34
|
+
const RU_DATE_SLOTS_PLACEHOLDER = {
|
|
35
|
+
[SlotKey.Day]: 'ДД',
|
|
36
|
+
[SlotKey.Month]: 'ММ',
|
|
37
|
+
[SlotKey.Year]: 'ГГГГ',
|
|
38
|
+
};
|
|
39
|
+
const EN_DATE_SLOTS_PLACEHOLDER = {
|
|
40
|
+
[SlotKey.Day]: 'DD',
|
|
41
|
+
[SlotKey.Month]: 'MM',
|
|
42
|
+
[SlotKey.Year]: 'YYYY',
|
|
43
|
+
};
|
|
17
44
|
export const SLOTS_PLACEHOLDER = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
[SlotKey.Year]: 'ГГГГ',
|
|
45
|
+
[MODES.Date]: {
|
|
46
|
+
'ru-RU': RU_DATE_SLOTS_PLACEHOLDER,
|
|
47
|
+
'en-US': EN_DATE_SLOTS_PLACEHOLDER,
|
|
22
48
|
},
|
|
23
|
-
|
|
24
|
-
[SlotKey.
|
|
25
|
-
[SlotKey.
|
|
26
|
-
[SlotKey.Year]: 'YYYY',
|
|
49
|
+
[MODES.DateTime]: {
|
|
50
|
+
'ru-RU': Object.assign(Object.assign({}, RU_DATE_SLOTS_PLACEHOLDER), { [SlotKey.Hours]: 'чч', [SlotKey.Minutes]: 'мм', [SlotKey.Seconds]: 'сс' }),
|
|
51
|
+
'en-US': Object.assign(Object.assign({}, EN_DATE_SLOTS_PLACEHOLDER), { [SlotKey.Hours]: 'hh', [SlotKey.Minutes]: 'mm', [SlotKey.Seconds]: 'ss' }),
|
|
27
52
|
},
|
|
28
53
|
};
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { ChangeEvent, FocusEventHandler, KeyboardEvent, RefObject } from 'react';
|
|
2
2
|
import { SlotKey } from '../constants';
|
|
3
|
+
import { Mode } from '../types';
|
|
3
4
|
type UseDateFieldProps = {
|
|
4
5
|
inputRef: RefObject<HTMLInputElement>;
|
|
5
|
-
onChange?(value:
|
|
6
|
+
onChange?(value: Date | undefined): void;
|
|
6
7
|
readonly?: boolean;
|
|
7
8
|
locale?: Intl.Locale;
|
|
8
9
|
setIsOpen(v: boolean): void;
|
|
10
|
+
mode: Mode;
|
|
9
11
|
};
|
|
10
|
-
type FocusSlot = SlotKey.Day | SlotKey.Year | 'auto';
|
|
11
|
-
export declare function useDateField({ inputRef, onChange, readonly, locale, setIsOpen }: UseDateFieldProps): {
|
|
12
|
+
type FocusSlot = SlotKey.Day | SlotKey.Year | SlotKey.Seconds | 'auto';
|
|
13
|
+
export declare function useDateField({ inputRef, onChange, readonly, locale, setIsOpen, mode, }: UseDateFieldProps): {
|
|
12
14
|
handleKeyDown: (e: KeyboardEvent<HTMLInputElement>) => void;
|
|
13
15
|
handleChange: (value: string, e?: ChangeEvent<HTMLInputElement> | undefined) => void;
|
|
14
16
|
handleClick: () => void;
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { useCallback, useMemo, useRef } from 'react';
|
|
2
2
|
import { isBrowser } from '@snack-uikit/utils';
|
|
3
3
|
import { DEFAULT_LOCALE, MASK, SlotKey, SLOTS, SLOTS_PLACEHOLDER } from '../constants';
|
|
4
|
-
import { getNextSlotKey, getPrevSlotKey, getSlotKey } from '../utils';
|
|
4
|
+
import { getNextSlotKey, getPrevSlotKey, getSlotKey, parseDate } from '../utils';
|
|
5
5
|
import { useDateFieldHelpers } from './useDateFieldHelpers';
|
|
6
|
-
|
|
6
|
+
import { useDateTimeFieldHelpers } from './useDateTimeFieldHelpers';
|
|
7
|
+
export function useDateField({ inputRef, onChange, readonly, locale = DEFAULT_LOCALE, setIsOpen, mode, }) {
|
|
7
8
|
var _a;
|
|
8
|
-
const
|
|
9
|
+
const dateHelpers = useDateFieldHelpers(inputRef);
|
|
10
|
+
const dateTimeHelpers = useDateTimeFieldHelpers(inputRef);
|
|
11
|
+
const { setFocus, updateSlot, getSlot, isLikeDate, isAllSelected, tryToCompleteInput, isValidInput } = mode === 'date' ? dateHelpers : dateTimeHelpers;
|
|
9
12
|
const focusSlotRef = useRef(SlotKey.Day);
|
|
10
|
-
const mask = useMemo(() => MASK[locale.baseName] || MASK[DEFAULT_LOCALE.baseName], [locale]);
|
|
11
|
-
const
|
|
13
|
+
const mask = useMemo(() => MASK[mode][locale.baseName] || MASK[mode][DEFAULT_LOCALE.baseName], [locale.baseName, mode]);
|
|
14
|
+
const getNextSlotKeyHandler = getNextSlotKey(mode);
|
|
15
|
+
const getPrevSlotKeyHandler = getPrevSlotKey(mode);
|
|
16
|
+
const slotsPlaceholder = useMemo(() => SLOTS_PLACEHOLDER[mode][locale.baseName] || SLOTS_PLACEHOLDER[mode][DEFAULT_LOCALE.baseName], [locale.baseName, mode]);
|
|
12
17
|
const setInputFocus = useCallback((focusSlot) => {
|
|
13
18
|
if (!inputRef.current || readonly) {
|
|
14
19
|
return;
|
|
@@ -31,42 +36,44 @@ export function useDateField({ inputRef, onChange, readonly, locale = DEFAULT_LO
|
|
|
31
36
|
setFocus(focusSlotValue);
|
|
32
37
|
return;
|
|
33
38
|
}
|
|
34
|
-
const slotKey = getSlotKey(inputRef.current.selectionStart);
|
|
39
|
+
const slotKey = getSlotKey(inputRef.current.selectionStart, mode);
|
|
35
40
|
if (slotKey) {
|
|
36
|
-
const { start, end } = SLOTS[slotKey];
|
|
41
|
+
const { start, end } = SLOTS[mode][slotKey];
|
|
37
42
|
inputRef.current.setSelectionRange(start, end);
|
|
38
43
|
}
|
|
39
|
-
}, [inputRef, isLikeDate, mask, readonly, setFocus]);
|
|
44
|
+
}, [inputRef, isLikeDate, mask, mode, readonly, setFocus]);
|
|
40
45
|
const handleClick = useCallback(() => {
|
|
41
46
|
setInputFocus('auto');
|
|
42
47
|
}, [setInputFocus]);
|
|
43
48
|
const handleChange = () => {
|
|
44
49
|
var _a;
|
|
45
|
-
onChange && isLikeDate() && onChange(((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.value) || '');
|
|
50
|
+
onChange && isLikeDate() && onChange(parseDate(((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.value) || ''));
|
|
46
51
|
};
|
|
47
52
|
const checkInputAndGoNext = useCallback((slotKey) => {
|
|
48
|
-
|
|
53
|
+
var _a, _b;
|
|
54
|
+
if (slotKey === (mode === 'date' ? SlotKey.Year : SlotKey.Seconds) && tryToCompleteInput()) {
|
|
49
55
|
return;
|
|
50
56
|
}
|
|
51
57
|
if (isValidInput()) {
|
|
52
|
-
setFocus(
|
|
58
|
+
setFocus(getNextSlotKeyHandler(slotKey));
|
|
53
59
|
return;
|
|
54
60
|
}
|
|
55
61
|
switch (slotKey) {
|
|
56
62
|
case SlotKey.Day:
|
|
57
|
-
updateSlot(SlotKey.Month,
|
|
63
|
+
updateSlot(SlotKey.Month, (_a = slotsPlaceholder === null || slotsPlaceholder === void 0 ? void 0 : slotsPlaceholder[SlotKey.Month]) !== null && _a !== void 0 ? _a : '');
|
|
58
64
|
setFocus(SlotKey.Month);
|
|
59
65
|
return;
|
|
60
66
|
case SlotKey.Year:
|
|
61
67
|
case SlotKey.Month:
|
|
62
|
-
updateSlot(SlotKey.Day,
|
|
68
|
+
updateSlot(SlotKey.Day, (_b = slotsPlaceholder === null || slotsPlaceholder === void 0 ? void 0 : slotsPlaceholder[SlotKey.Day]) !== null && _b !== void 0 ? _b : '');
|
|
63
69
|
setFocus(SlotKey.Day);
|
|
64
70
|
return;
|
|
65
71
|
default:
|
|
66
|
-
setFocus(
|
|
72
|
+
setFocus(getNextSlotKeyHandler(slotKey));
|
|
67
73
|
}
|
|
68
|
-
}, [tryToCompleteInput, isValidInput, setFocus,
|
|
74
|
+
}, [mode, tryToCompleteInput, isValidInput, setFocus, getNextSlotKeyHandler, updateSlot, slotsPlaceholder]);
|
|
69
75
|
const handleKeyDown = useCallback((e) => {
|
|
76
|
+
var _a;
|
|
70
77
|
if (inputRef.current && !readonly) {
|
|
71
78
|
if (e.key !== 'Tab') {
|
|
72
79
|
e.preventDefault();
|
|
@@ -83,21 +90,21 @@ export function useDateField({ inputRef, onChange, readonly, locale = DEFAULT_LO
|
|
|
83
90
|
tryToCompleteInput();
|
|
84
91
|
}
|
|
85
92
|
const clickIndex = inputRef.current.selectionStart;
|
|
86
|
-
const slotKey = getSlotKey(clickIndex);
|
|
93
|
+
const slotKey = getSlotKey(clickIndex, mode);
|
|
87
94
|
if (slotKey) {
|
|
88
95
|
const value = getSlot(slotKey);
|
|
89
|
-
const { max } = SLOTS[slotKey];
|
|
96
|
+
const { max } = SLOTS[mode][slotKey];
|
|
90
97
|
const numberValue = Number(value) || 0;
|
|
91
98
|
if (e.key === 'ArrowRight') {
|
|
92
|
-
if (isAllSelected() || slotKey === SlotKey.Year) {
|
|
99
|
+
if (isAllSelected() || slotKey === (mode === 'date' ? SlotKey.Year : SlotKey.Seconds)) {
|
|
93
100
|
inputRef.current.selectionStart = inputRef.current.value.length;
|
|
94
101
|
return;
|
|
95
102
|
}
|
|
96
|
-
setFocus(
|
|
103
|
+
setFocus(getNextSlotKeyHandler(slotKey));
|
|
97
104
|
return;
|
|
98
105
|
}
|
|
99
106
|
if (e.key === 'ArrowLeft') {
|
|
100
|
-
setFocus(
|
|
107
|
+
setFocus(getPrevSlotKeyHandler(slotKey));
|
|
101
108
|
return;
|
|
102
109
|
}
|
|
103
110
|
if (e.key === 'Backspace') {
|
|
@@ -106,7 +113,7 @@ export function useDateField({ inputRef, onChange, readonly, locale = DEFAULT_LO
|
|
|
106
113
|
setFocus(SlotKey.Day);
|
|
107
114
|
}
|
|
108
115
|
else {
|
|
109
|
-
updateSlot(slotKey,
|
|
116
|
+
updateSlot(slotKey, (_a = slotsPlaceholder[slotKey]) !== null && _a !== void 0 ? _a : '');
|
|
110
117
|
}
|
|
111
118
|
}
|
|
112
119
|
if (/^\d+$/.test(e.key)) {
|
|
@@ -144,21 +151,25 @@ export function useDateField({ inputRef, onChange, readonly, locale = DEFAULT_LO
|
|
|
144
151
|
}
|
|
145
152
|
}
|
|
146
153
|
}
|
|
147
|
-
|
|
154
|
+
const newDate = parseDate(isLikeDate() ? inputRef.current.value : '');
|
|
155
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(newDate);
|
|
148
156
|
}
|
|
149
157
|
}
|
|
150
158
|
}, [
|
|
151
159
|
checkInputAndGoNext,
|
|
160
|
+
getNextSlotKeyHandler,
|
|
161
|
+
getPrevSlotKeyHandler,
|
|
152
162
|
getSlot,
|
|
153
163
|
inputRef,
|
|
154
164
|
isAllSelected,
|
|
155
165
|
isLikeDate,
|
|
156
166
|
mask,
|
|
167
|
+
mode,
|
|
157
168
|
onChange,
|
|
158
169
|
readonly,
|
|
159
170
|
setFocus,
|
|
160
171
|
setIsOpen,
|
|
161
|
-
|
|
172
|
+
slotsPlaceholder,
|
|
162
173
|
tryToCompleteInput,
|
|
163
174
|
updateSlot,
|
|
164
175
|
]);
|
|
@@ -3,20 +3,20 @@ import { SlotKey, SLOTS } from '../constants';
|
|
|
3
3
|
export function useDateFieldHelpers(inputRef) {
|
|
4
4
|
const setFocus = useCallback((slotKey) => {
|
|
5
5
|
if (inputRef.current) {
|
|
6
|
-
const { start, end } = SLOTS[slotKey];
|
|
6
|
+
const { start, end } = SLOTS.date[slotKey];
|
|
7
7
|
inputRef.current.setSelectionRange(start, end);
|
|
8
8
|
}
|
|
9
9
|
}, [inputRef]);
|
|
10
10
|
const isAllSelected = useCallback(() => { var _a, _b, _c; return ((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.value.length) === ((_b = inputRef.current) === null || _b === void 0 ? void 0 : _b.selectionEnd) && ((_c = inputRef.current) === null || _c === void 0 ? void 0 : _c.selectionStart) === 0; }, [inputRef]);
|
|
11
11
|
const getSlot = useCallback((slotKey) => {
|
|
12
12
|
if (inputRef.current) {
|
|
13
|
-
return inputRef.current.value.slice(SLOTS[slotKey].start, SLOTS[slotKey].end);
|
|
13
|
+
return inputRef.current.value.slice(SLOTS.date[slotKey].start, SLOTS.date[slotKey].end);
|
|
14
14
|
}
|
|
15
15
|
return '';
|
|
16
16
|
}, [inputRef]);
|
|
17
17
|
const isLikeDate = useCallback(() => {
|
|
18
18
|
if (inputRef.current) {
|
|
19
|
-
return Object.keys(SLOTS).every(slotKey => getSlot(slotKey) && Number.isInteger(Number(getSlot(slotKey))));
|
|
19
|
+
return Object.keys(SLOTS.date).every(slotKey => getSlot(slotKey) && Number.isInteger(Number(getSlot(slotKey))));
|
|
20
20
|
}
|
|
21
21
|
return false;
|
|
22
22
|
}, [getSlot, inputRef]);
|
|
@@ -35,7 +35,7 @@ export function useDateFieldHelpers(inputRef) {
|
|
|
35
35
|
const day = parseInt(getSlot(SlotKey.Day), 10);
|
|
36
36
|
const month = parseInt(getSlot(SlotKey.Month), 10);
|
|
37
37
|
const year = parseInt(getSlot(SlotKey.Year), 10);
|
|
38
|
-
const { min, max } = SLOTS[SlotKey.Year];
|
|
38
|
+
const { min, max } = SLOTS.date[SlotKey.Year];
|
|
39
39
|
const isCompleted = Boolean(day && month && year >= min && year <= max);
|
|
40
40
|
if (isCompleted && inputRef.current) {
|
|
41
41
|
const lastPosition = (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.value.length;
|
|
@@ -46,7 +46,7 @@ export function useDateFieldHelpers(inputRef) {
|
|
|
46
46
|
}, [getSlot, inputRef]);
|
|
47
47
|
const updateSlot = useCallback((slotKey, slotValue) => {
|
|
48
48
|
if (inputRef.current) {
|
|
49
|
-
const { start, end, max } = SLOTS[slotKey];
|
|
49
|
+
const { start, end, max } = SLOTS.date[slotKey];
|
|
50
50
|
inputRef.current.value =
|
|
51
51
|
inputRef.current.value.slice(0, start) +
|
|
52
52
|
slotValue.toString().padStart(max.toString().length, '0') +
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
export declare function useDateTimeFieldHelpers(inputRef: RefObject<HTMLInputElement>): {
|
|
3
|
+
isAllSelected: () => boolean;
|
|
4
|
+
tryToCompleteInput: () => boolean;
|
|
5
|
+
getSlot: (slotKey: string) => string;
|
|
6
|
+
updateSlot: (slotKey: string, slotValue: number | string) => void;
|
|
7
|
+
setFocus: (slotKey: string) => void;
|
|
8
|
+
isLikeDate: () => boolean;
|
|
9
|
+
isValidInput: () => boolean;
|
|
10
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { SlotKey, SLOTS } from '../constants';
|
|
3
|
+
export function useDateTimeFieldHelpers(inputRef) {
|
|
4
|
+
const setFocus = useCallback((slotKey) => {
|
|
5
|
+
if (inputRef.current) {
|
|
6
|
+
const { start, end } = SLOTS['date-time'][slotKey];
|
|
7
|
+
inputRef.current.setSelectionRange(start, end);
|
|
8
|
+
}
|
|
9
|
+
}, [inputRef]);
|
|
10
|
+
const isAllSelected = useCallback(() => { var _a, _b, _c; return ((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.value.length) === ((_b = inputRef.current) === null || _b === void 0 ? void 0 : _b.selectionEnd) && ((_c = inputRef.current) === null || _c === void 0 ? void 0 : _c.selectionStart) === 0; }, [inputRef]);
|
|
11
|
+
const getSlot = useCallback((slotKey) => {
|
|
12
|
+
if (inputRef.current) {
|
|
13
|
+
return inputRef.current.value.slice(SLOTS['date-time'][slotKey].start, SLOTS['date-time'][slotKey].end);
|
|
14
|
+
}
|
|
15
|
+
return '';
|
|
16
|
+
}, [inputRef]);
|
|
17
|
+
const isLikeDate = useCallback(() => {
|
|
18
|
+
if (inputRef.current) {
|
|
19
|
+
return Object.keys(SLOTS['date-time']).every(slotKey => getSlot(slotKey) && Number.isInteger(Number(getSlot(slotKey))));
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}, [getSlot, inputRef]);
|
|
23
|
+
const isValidInput = useCallback(() => {
|
|
24
|
+
const day = parseInt(getSlot(SlotKey.Day), 10);
|
|
25
|
+
const month = parseInt(getSlot(SlotKey.Month), 10);
|
|
26
|
+
const year = parseInt(getSlot(SlotKey.Year), 10);
|
|
27
|
+
if (!month || !day) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
const date = new Date(year || /* високосный год = */ 2020, month - 1, day);
|
|
31
|
+
return date.getDate() === day;
|
|
32
|
+
}, [getSlot]);
|
|
33
|
+
const tryToCompleteInput = useCallback(() => {
|
|
34
|
+
var _a;
|
|
35
|
+
const day = parseInt(getSlot(SlotKey.Day), 10);
|
|
36
|
+
const month = parseInt(getSlot(SlotKey.Month), 10);
|
|
37
|
+
const year = parseInt(getSlot(SlotKey.Year), 10);
|
|
38
|
+
const hours = parseInt(getSlot(SlotKey.Hours), 10);
|
|
39
|
+
const minutes = parseInt(getSlot(SlotKey.Minutes), 10);
|
|
40
|
+
const seconds = parseInt(getSlot(SlotKey.Seconds), 10);
|
|
41
|
+
const { min, max } = SLOTS['date-time'][SlotKey.Year];
|
|
42
|
+
const isCompleted = Boolean(day &&
|
|
43
|
+
month &&
|
|
44
|
+
year >= min &&
|
|
45
|
+
year <= max &&
|
|
46
|
+
hours !== undefined &&
|
|
47
|
+
minutes !== undefined &&
|
|
48
|
+
seconds !== undefined);
|
|
49
|
+
if (isCompleted && inputRef.current) {
|
|
50
|
+
const lastPosition = (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.value.length;
|
|
51
|
+
inputRef.current.selectionStart = lastPosition;
|
|
52
|
+
inputRef.current.selectionEnd = lastPosition;
|
|
53
|
+
}
|
|
54
|
+
return isCompleted;
|
|
55
|
+
}, [getSlot, inputRef]);
|
|
56
|
+
const updateSlot = useCallback((slotKey, slotValue) => {
|
|
57
|
+
if (inputRef.current) {
|
|
58
|
+
const { start, end, max } = SLOTS['date-time'][slotKey];
|
|
59
|
+
inputRef.current.value =
|
|
60
|
+
inputRef.current.value.slice(0, start) +
|
|
61
|
+
slotValue.toString().padStart(max.toString().length, '0') +
|
|
62
|
+
inputRef.current.value.slice(end);
|
|
63
|
+
setFocus(slotKey);
|
|
64
|
+
}
|
|
65
|
+
}, [inputRef, setFocus]);
|
|
66
|
+
return {
|
|
67
|
+
isAllSelected,
|
|
68
|
+
tryToCompleteInput,
|
|
69
|
+
getSlot,
|
|
70
|
+
updateSlot,
|
|
71
|
+
setFocus,
|
|
72
|
+
isLikeDate,
|
|
73
|
+
isValidInput,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { SlotKey } from './constants';
|
|
2
|
-
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function
|
|
2
|
+
import { Mode } from './types';
|
|
3
|
+
export declare function getSlotKey(index: number | null, mode: Mode): SlotKey | null;
|
|
4
|
+
export declare function getNextSlotKeyWithDate(slotKey: string | null): SlotKey.Month | SlotKey.Year;
|
|
5
|
+
export declare function getPrevSlotKeyWithDate(slotKey: string | null): SlotKey.Day | SlotKey.Month;
|
|
6
|
+
export declare function getNextSlotKeyWithTime(slotKey: string | null): SlotKey.Month | SlotKey.Year | SlotKey.Hours | SlotKey.Minutes | SlotKey.Seconds;
|
|
7
|
+
export declare function getPrevSlotKeyWithTime(slotKey: string | null): SlotKey.Day | SlotKey.Month | SlotKey.Year | SlotKey.Hours | SlotKey.Minutes;
|
|
8
|
+
export declare function getNextSlotKey(mode: Mode): typeof getNextSlotKeyWithTime;
|
|
9
|
+
export declare function getPrevSlotKey(mode: Mode): typeof getPrevSlotKeyWithTime;
|
|
5
10
|
/**
|
|
6
11
|
* Преобразует строковое значение поля FieldDate в тип Date
|
|
7
12
|
* @function helper
|
|
8
13
|
*/
|
|
9
|
-
export declare function parseDate(
|
|
14
|
+
export declare function parseDate(value: string): Date | undefined;
|