@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 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
- | value* | `string` | - | Значение input |
324
+ | mode* | enum Mode: `"date"`, `"date-time"` | - | |
320
325
  | open | `boolean` | - | Открыт date-picker |
321
326
  | onOpenChange | `(value: boolean) => void` | - | Колбек открытия пикера |
322
- | onChange | `(value: string) => void` | - | Колбек смены значения |
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
- type InputProps = Pick<InputPrivateProps, 'id' | 'name' | 'value' | 'disabled' | 'readonly' | 'onFocus' | 'onBlur'>;
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: string): void;
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: string): void;
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]: 'm',
34
+ [SIZE.L]: 'l',
36
35
  };
37
36
  export const FieldDate = forwardRef((_a, ref) => {
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 } = _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"]);
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: valueProp || '' });
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.toLocaleDateString(DEFAULT_LOCALE));
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: 'date', size: CALENDAR_SIZE_MAP[size], value: valueProp ? parseDate(valueProp) : undefined, onChangeValue: handleSelectDate, buildCellProps: buildCellProps, navigationStartRef: element => {
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 MASK: Record<string, string>;
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<string, Record<string, string>>;
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
- 'ru-RU': 'ДД.ММ.ГГГГ',
9
- 'en-US': 'DD.MM.YYYY',
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
- export const SLOTS = {
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
- 'ru-RU': {
19
- [SlotKey.Day]: 'ДД',
20
- [SlotKey.Month]: 'ММ',
21
- [SlotKey.Year]: 'ГГГГ',
45
+ [MODES.Date]: {
46
+ 'ru-RU': RU_DATE_SLOTS_PLACEHOLDER,
47
+ 'en-US': EN_DATE_SLOTS_PLACEHOLDER,
22
48
  },
23
- 'en-US': {
24
- [SlotKey.Day]: 'DD',
25
- [SlotKey.Month]: 'MM',
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: string): void;
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
- export function useDateField({ inputRef, onChange, readonly, locale = DEFAULT_LOCALE, setIsOpen }) {
6
+ import { useDateTimeFieldHelpers } from './useDateTimeFieldHelpers';
7
+ export function useDateField({ inputRef, onChange, readonly, locale = DEFAULT_LOCALE, setIsOpen, mode, }) {
7
8
  var _a;
8
- const { setFocus, updateSlot, getSlot, isLikeDate, isAllSelected, isValidInput, tryToCompleteInput } = useDateFieldHelpers(inputRef);
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 slotsPlaceHolder = useMemo(() => SLOTS_PLACEHOLDER[locale.baseName] || SLOTS_PLACEHOLDER[DEFAULT_LOCALE.baseName], [locale.baseName]);
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
- if (slotKey === SlotKey.Year && tryToCompleteInput()) {
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(getNextSlotKey(slotKey));
58
+ setFocus(getNextSlotKeyHandler(slotKey));
53
59
  return;
54
60
  }
55
61
  switch (slotKey) {
56
62
  case SlotKey.Day:
57
- updateSlot(SlotKey.Month, slotsPlaceHolder[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, slotsPlaceHolder[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(getNextSlotKey(slotKey));
72
+ setFocus(getNextSlotKeyHandler(slotKey));
67
73
  }
68
- }, [tryToCompleteInput, isValidInput, setFocus, slotsPlaceHolder, updateSlot]);
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(getNextSlotKey(slotKey));
103
+ setFocus(getNextSlotKeyHandler(slotKey));
97
104
  return;
98
105
  }
99
106
  if (e.key === 'ArrowLeft') {
100
- setFocus(getPrevSlotKey(slotKey));
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, slotsPlaceHolder[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
- onChange === null || onChange === void 0 ? void 0 : onChange(isLikeDate() ? inputRef.current.value : '');
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
- slotsPlaceHolder,
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,6 +1,9 @@
1
+ import { ValueOf } from '@snack-uikit/utils';
2
+ import { MODES } from './constants';
1
3
  export type Slot = {
2
4
  start: number;
3
5
  end: number;
4
6
  max: number;
5
7
  min: number;
6
8
  };
9
+ export type Mode = ValueOf<typeof MODES>;
@@ -1,9 +1,14 @@
1
1
  import { SlotKey } from './constants';
2
- export declare function getSlotKey(index: number | null): string | null;
3
- export declare function getNextSlotKey(slotKey: string | null): SlotKey.Month | SlotKey.Year;
4
- export declare function getPrevSlotKey(slotKey: string | null): SlotKey.Day | SlotKey.Month;
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(date: string): Date;
14
+ export declare function parseDate(value: string): Date | undefined;