@gravity-ui/date-components 1.0.0 → 1.1.1

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.
Files changed (69) hide show
  1. package/dist/cjs/components/Calendar/Calendar.css +9 -1
  2. package/dist/cjs/components/Calendar/CalendarBase.js +9 -9
  3. package/dist/cjs/components/Calendar/hooks/types.d.ts +5 -2
  4. package/dist/cjs/components/Calendar/hooks/useCalendarCellProps.js +21 -16
  5. package/dist/cjs/components/Calendar/hooks/useCalendarGridProps.js +2 -7
  6. package/dist/cjs/components/Calendar/hooks/useCalendarProps.js +6 -3
  7. package/dist/cjs/components/Calendar/hooks/useCalendarState.js +69 -26
  8. package/dist/cjs/components/Calendar/hooks/useRangeCalendarState.js +16 -11
  9. package/dist/cjs/components/Calendar/utils.d.ts +3 -1
  10. package/dist/cjs/components/Calendar/utils.js +7 -1
  11. package/dist/cjs/components/DateField/hooks/useDateFieldProps.js +23 -16
  12. package/dist/cjs/components/DateField/hooks/useDateFieldState.d.ts +2 -0
  13. package/dist/cjs/components/DateField/hooks/useDateFieldState.js +49 -166
  14. package/dist/cjs/components/DateField/types.d.ts +1 -1
  15. package/dist/cjs/components/DateField/utils.d.ts +12 -9
  16. package/dist/cjs/components/DateField/utils.js +168 -25
  17. package/dist/cjs/components/DatePicker/DatePicker.css +6 -0
  18. package/dist/cjs/components/DatePicker/DatePicker.d.ts +4 -1
  19. package/dist/cjs/components/DatePicker/DatePicker.js +6 -33
  20. package/dist/cjs/components/DatePicker/MobileCalendar.js +4 -4
  21. package/dist/cjs/components/DatePicker/hooks/useDatePickerProps.d.ts +20 -0
  22. package/dist/cjs/components/DatePicker/hooks/useDatePickerProps.js +134 -0
  23. package/dist/cjs/components/DatePicker/hooks/useDatePickerState.d.ts +4 -5
  24. package/dist/cjs/components/DatePicker/hooks/useDatePickerState.js +18 -8
  25. package/dist/cjs/components/DatePicker/index.d.ts +1 -0
  26. package/dist/cjs/components/DatePicker/index.js +1 -0
  27. package/dist/cjs/components/RelativeDatePicker/RelativeDatePicker.css +1 -0
  28. package/dist/cjs/components/RelativeDatePicker/RelativeDatePicker.d.ts +3 -0
  29. package/dist/cjs/components/RelativeDatePicker/RelativeDatePicker.js +1 -1
  30. package/dist/cjs/components/RelativeDatePicker/hooks/useRelativeDatePickerProps.js +35 -27
  31. package/dist/cjs/components/utils/dates.d.ts +9 -0
  32. package/dist/cjs/components/utils/dates.js +28 -0
  33. package/dist/esm/components/Calendar/Calendar.css +9 -1
  34. package/dist/esm/components/Calendar/CalendarBase.js +10 -10
  35. package/dist/esm/components/Calendar/hooks/types.d.ts +5 -2
  36. package/dist/esm/components/Calendar/hooks/useCalendarCellProps.js +21 -16
  37. package/dist/esm/components/Calendar/hooks/useCalendarGridProps.js +2 -7
  38. package/dist/esm/components/Calendar/hooks/useCalendarProps.js +6 -3
  39. package/dist/esm/components/Calendar/hooks/useCalendarState.js +70 -27
  40. package/dist/esm/components/Calendar/hooks/useRangeCalendarState.js +16 -11
  41. package/dist/esm/components/Calendar/utils.d.ts +3 -1
  42. package/dist/esm/components/Calendar/utils.js +6 -0
  43. package/dist/esm/components/DateField/hooks/useDateFieldProps.js +23 -16
  44. package/dist/esm/components/DateField/hooks/useDateFieldState.d.ts +2 -0
  45. package/dist/esm/components/DateField/hooks/useDateFieldState.js +41 -158
  46. package/dist/esm/components/DateField/types.d.ts +1 -1
  47. package/dist/esm/components/DateField/utils.d.ts +12 -9
  48. package/dist/esm/components/DateField/utils.js +162 -21
  49. package/dist/esm/components/DatePicker/DatePicker.css +6 -0
  50. package/dist/esm/components/DatePicker/DatePicker.d.ts +4 -1
  51. package/dist/esm/components/DatePicker/DatePicker.js +8 -35
  52. package/dist/esm/components/DatePicker/MobileCalendar.js +1 -1
  53. package/dist/esm/components/DatePicker/hooks/useDatePickerProps.d.ts +20 -0
  54. package/dist/esm/components/DatePicker/hooks/useDatePickerProps.js +127 -0
  55. package/dist/esm/components/DatePicker/hooks/useDatePickerState.d.ts +4 -5
  56. package/dist/esm/components/DatePicker/hooks/useDatePickerState.js +17 -7
  57. package/dist/esm/components/DatePicker/index.d.ts +1 -0
  58. package/dist/esm/components/DatePicker/index.js +1 -0
  59. package/dist/esm/components/RelativeDatePicker/RelativeDatePicker.css +1 -0
  60. package/dist/esm/components/RelativeDatePicker/RelativeDatePicker.d.ts +3 -0
  61. package/dist/esm/components/RelativeDatePicker/RelativeDatePicker.js +1 -1
  62. package/dist/esm/components/RelativeDatePicker/hooks/useRelativeDatePickerProps.js +35 -27
  63. package/dist/esm/components/utils/dates.d.ts +9 -0
  64. package/dist/esm/components/utils/dates.js +22 -0
  65. package/package.json +8 -7
  66. package/dist/cjs/components/DatePicker/DesktopCalendar.d.ts +0 -15
  67. package/dist/cjs/components/DatePicker/DesktopCalendar.js +0 -56
  68. package/dist/esm/components/DatePicker/DesktopCalendar.d.ts +0 -15
  69. package/dist/esm/components/DatePicker/DesktopCalendar.js +0 -48
@@ -6,7 +6,7 @@ export function useCalendarGridProps(state) {
6
6
  state.setFocused(isFocused);
7
7
  },
8
8
  });
9
- const gridProps = Object.assign(Object.assign({ role: 'grid', 'aria-label': state.mode === 'years'
9
+ const gridProps = Object.assign(Object.assign({ role: 'grid', 'aria-label': state.mode === 'years' || state.mode === 'quarters'
10
10
  ? `${state.startDate.year()} — ${state.endDate.year()}`
11
11
  : state.focusedDate.format(state.mode === 'days' ? 'MMMM YYYY' : 'YYYY'), 'aria-disabled': state.disabled ? 'true' : undefined, 'aria-readonly': state.readOnly ? 'true' : undefined }, focusWithinProps), { onKeyDown: (e) => {
12
12
  if (e.key === 'ArrowRight') {
@@ -40,12 +40,7 @@ export function useCalendarGridProps(state) {
40
40
  state.zoomIn();
41
41
  }
42
42
  else if (e.key === 'Enter' || e.key === ' ') {
43
- if (state.mode === 'days') {
44
- state.selectFocusedDate();
45
- }
46
- else {
47
- state.setMode(state.mode === 'months' ? 'days' : 'months');
48
- }
43
+ state.selectDate(state.focusedDate);
49
44
  }
50
45
  } });
51
46
  return {
@@ -4,7 +4,7 @@ import { i18n } from '../i18n';
4
4
  const buttonDisabledClassName = 'yc-button_disabled g-button_disabled';
5
5
  // eslint-disable-next-line complexity
6
6
  export function useCalendarProps(props, state) {
7
- const title = state.mode === 'years'
7
+ const title = state.mode === 'years' || state.mode === 'quarters'
8
8
  ? `${state.startDate.year()} — ${state.endDate.year()}`
9
9
  : state.focusedDate.format(state.mode === 'days' ? 'MMMM YYYY' : 'YYYY');
10
10
  const { focusWithinProps } = useFocusWithin({
@@ -12,7 +12,10 @@ export function useCalendarProps(props, state) {
12
12
  onBlurWithin: props.onBlur,
13
13
  });
14
14
  const calendarProps = Object.assign({ role: 'group', id: props.id, 'aria-label': [props['aria-label'], title].filter(Boolean).join(', '), 'aria-labelledby': props['aria-labelledby'] || undefined, 'aria-describedby': props['aria-describedby'] || undefined, 'aria-details': props['aria-details'] || undefined, 'aria-disabled': state.disabled || undefined }, focusWithinProps);
15
- const modeDisabled = state.disabled || state.mode === 'years';
15
+ const modeIndex = state.availableModes.indexOf(state.mode);
16
+ const isModeLast = modeIndex + 1 === state.availableModes.length;
17
+ const isNextModeLast = modeIndex + 2 === state.availableModes.length;
18
+ const modeDisabled = state.disabled || isModeLast;
16
19
  const modeButtonProps = {
17
20
  disabled: state.disabled,
18
21
  // FIXME: do not use button class name
@@ -21,7 +24,7 @@ export function useCalendarProps(props, state) {
21
24
  ? undefined
22
25
  : () => {
23
26
  state.zoomOut();
24
- if (state.mode === 'months') {
27
+ if (isNextModeLast) {
25
28
  state.setFocused(true);
26
29
  }
27
30
  },
@@ -1,12 +1,20 @@
1
1
  import React from 'react';
2
- import { dateTime } from '@gravity-ui/date-utils';
3
2
  import { useControlledState } from '../../hooks/useControlledState';
4
- import { constrainValue } from '../utils';
3
+ import { createPlaceholderValue } from '../../utils/dates';
4
+ import { calendarLayouts, constrainValue } from '../utils';
5
+ const defaultModes = {
6
+ days: true,
7
+ months: true,
8
+ quarters: false,
9
+ years: true,
10
+ };
5
11
  export function useCalendarState(props) {
6
- const { disabled, readOnly, minValue, maxValue, timeZone } = props;
12
+ const { disabled, readOnly, minValue, maxValue, timeZone, modes = defaultModes } = props;
7
13
  const [value, setValue] = useControlledState(props.value, props.defaultValue, props.onUpdate);
8
14
  const [mode, setMode] = useControlledState(props.mode, props.defaultMode, props.onUpdateMode);
9
- const currentMode = mode || 'days';
15
+ const availableModes = calendarLayouts.filter((l) => modes[l]);
16
+ const minMode = availableModes[0] || 'days';
17
+ const currentMode = mode && availableModes.includes(mode) ? mode : minMode;
10
18
  const focusedValue = React.useMemo(() => {
11
19
  if (!props.focusedValue) {
12
20
  return props.focusedValue;
@@ -14,17 +22,18 @@ export function useCalendarState(props) {
14
22
  return constrainValue(props.focusedValue, minValue, maxValue);
15
23
  }, [props.focusedValue, minValue, maxValue]);
16
24
  const defaultFocusedValue = React.useMemo(() => {
17
- const defaultValue = (props.defaultFocusedValue ? props.defaultFocusedValue : value) || dateTime({ timeZone });
25
+ const defaultValue = (props.defaultFocusedValue ? props.defaultFocusedValue : value) ||
26
+ createPlaceholderValue({ timeZone }).startOf(minMode);
18
27
  return constrainValue(defaultValue, minValue, maxValue);
19
- }, [maxValue, minValue, props.defaultFocusedValue, timeZone, value]);
28
+ }, [maxValue, minValue, props.defaultFocusedValue, timeZone, value, minMode]);
20
29
  const [focusedDateInner, setFocusedDate] = useControlledState(focusedValue, defaultFocusedValue, props.onFocusUpdate);
21
- const focusedDate = focusedDateInner !== null && focusedDateInner !== void 0 ? focusedDateInner : constrainValue(dateTime({ timeZone }), minValue, maxValue);
30
+ const focusedDate = focusedDateInner !== null && focusedDateInner !== void 0 ? focusedDateInner : constrainValue(createPlaceholderValue({ timeZone }).startOf(minMode), minValue, maxValue);
22
31
  if (isInvalid(focusedDate, minValue, maxValue)) {
23
32
  // If the focused date was moved to an invalid value, it can't be focused, so constrain it.
24
33
  setFocusedDate(constrainValue(focusedDate, minValue, maxValue));
25
34
  }
26
35
  function focusCell(date) {
27
- setFocusedDate(constrainValue(date, minValue, maxValue));
36
+ setFocusedDate(constrainValue(date.startOf(currentMode), minValue, maxValue));
28
37
  }
29
38
  const [isFocused, setFocused] = React.useState(props.autoFocus || false);
30
39
  const startDate = getStartDate(focusedDate, currentMode);
@@ -35,13 +44,21 @@ export function useCalendarState(props) {
35
44
  value,
36
45
  setValue,
37
46
  timeZone,
38
- selectDate(date) {
39
- if (!disabled && !readOnly) {
40
- const newValue = constrainValue(date, minValue, maxValue);
41
- if (this.isCellUnavailable(newValue)) {
42
- return;
47
+ selectDate(date, force = false) {
48
+ if (!disabled) {
49
+ if (!readOnly && (force || this.mode === minMode)) {
50
+ const newValue = constrainValue(date, minValue, maxValue);
51
+ if (this.isCellUnavailable(newValue)) {
52
+ return;
53
+ }
54
+ setValue(newValue);
55
+ if (force && currentMode !== minMode) {
56
+ setMode(minMode);
57
+ }
58
+ }
59
+ else {
60
+ this.zoomIn();
43
61
  }
44
- setValue(constrainValue(newValue, minValue, maxValue));
45
62
  }
46
63
  },
47
64
  minValue,
@@ -54,41 +71,53 @@ export function useCalendarState(props) {
54
71
  setFocused(true);
55
72
  },
56
73
  focusNextCell() {
57
- focusCell(focusedDate.add({ [this.mode]: 1 }));
74
+ focusCell(focusedDate.add(1, this.mode));
58
75
  },
59
76
  focusPreviousCell() {
60
- focusCell(focusedDate.subtract({ [this.mode]: 1 }));
77
+ focusCell(focusedDate.subtract(1, this.mode));
61
78
  },
62
79
  focusNextRow() {
63
80
  if (this.mode === 'days') {
64
81
  focusCell(focusedDate.add(1, 'week'));
65
82
  }
83
+ else if (this.mode === 'quarters') {
84
+ focusCell(focusedDate.add(1, 'years'));
85
+ }
66
86
  else {
67
- focusCell(focusedDate.add({ [this.mode]: 3 }));
87
+ focusCell(focusedDate.add(3, this.mode));
68
88
  }
69
89
  },
70
90
  focusPreviousRow() {
71
91
  if (this.mode === 'days') {
72
92
  focusCell(focusedDate.subtract(1, 'week'));
73
93
  }
94
+ else if (this.mode === 'quarters') {
95
+ focusCell(focusedDate.subtract(1, 'years'));
96
+ }
74
97
  else {
75
- focusCell(focusedDate.subtract({ [this.mode]: 3 }));
98
+ focusCell(focusedDate.subtract(3, this.mode));
76
99
  }
77
100
  },
78
101
  focusNextPage(larger) {
79
102
  if (this.mode === 'days') {
80
103
  focusCell(focusedDate.add({ months: larger ? 12 : 1 }));
81
104
  }
105
+ else if (this.mode === 'quarters') {
106
+ focusCell(focusedDate.add(4, 'years'));
107
+ }
82
108
  else {
83
- focusCell(focusedDate.add({ [this.mode]: 12 }));
109
+ focusCell(focusedDate.add(12, this.mode));
84
110
  }
85
111
  },
86
112
  focusPreviousPage(larger) {
87
113
  if (this.mode === 'days') {
88
114
  focusCell(focusedDate.subtract({ months: larger ? 12 : 1 }));
89
115
  }
116
+ else if (this.mode === 'quarters') {
117
+ focusCell(focusedDate.subtract(4, 'years'));
118
+ }
90
119
  else {
91
- focusCell(focusedDate.subtract({ [this.mode]: 12 }));
120
+ focusCell(focusedDate.subtract(12, this.mode));
92
121
  }
93
122
  },
94
123
  focusSectionStart() {
@@ -98,13 +127,19 @@ export function useCalendarState(props) {
98
127
  focusCell(getEndDate(focusedDate, this.mode));
99
128
  },
100
129
  zoomIn() {
101
- this.setMode(this.mode === 'years' ? 'months' : 'days');
130
+ const index = availableModes.indexOf(this.mode) - 1;
131
+ if (index >= 0) {
132
+ this.setMode(availableModes[index]);
133
+ }
102
134
  },
103
135
  zoomOut() {
104
- this.setMode(this.mode === 'days' ? 'months' : 'years');
136
+ const index = availableModes.indexOf(this.mode) + 1;
137
+ if (index < availableModes.length) {
138
+ this.setMode(availableModes[index]);
139
+ }
105
140
  },
106
141
  selectFocusedDate() {
107
- this.selectDate(focusedDate);
142
+ this.selectDate(focusedDate, true);
108
143
  },
109
144
  isFocused,
110
145
  setFocused,
@@ -112,11 +147,11 @@ export function useCalendarState(props) {
112
147
  return isInvalid(date, this.minValue, this.maxValue, this.mode);
113
148
  },
114
149
  isPreviousPageInvalid() {
115
- const prev = this.startDate.add({ days: -1 });
150
+ const prev = this.startDate.subtract(1, 'day');
116
151
  return this.isInvalid(prev);
117
152
  },
118
153
  isNextPageInvalid() {
119
- const next = this.endDate.add({ days: 1 });
154
+ const next = this.endDate.endOf(this.mode).add(1, 'day');
120
155
  return this.isInvalid(next);
121
156
  },
122
157
  isSelected(date) {
@@ -125,7 +160,7 @@ export function useCalendarState(props) {
125
160
  !this.isCellDisabled(date));
126
161
  },
127
162
  isCellUnavailable(date) {
128
- if (this.mode === 'days') {
163
+ if (this.mode === minMode) {
129
164
  return Boolean(props.isDateUnavailable && props.isDateUnavailable(date));
130
165
  }
131
166
  else {
@@ -143,6 +178,7 @@ export function useCalendarState(props) {
143
178
  },
144
179
  mode: currentMode,
145
180
  setMode,
181
+ availableModes,
146
182
  };
147
183
  }
148
184
  function getStartDate(date, mode) {
@@ -152,6 +188,10 @@ function getStartDate(date, mode) {
152
188
  if (mode === 'months') {
153
189
  return date.startOf('year');
154
190
  }
191
+ if (mode === 'quarters') {
192
+ const year = Math.floor(date.year() / 4) * 4;
193
+ return date.startOf('year').set('year', year);
194
+ }
155
195
  const year = Math.floor(date.year() / 12) * 12;
156
196
  return date.startOf('year').set('year', year);
157
197
  }
@@ -160,9 +200,12 @@ function getEndDate(date, mode) {
160
200
  return date.endOf('month').startOf('day');
161
201
  }
162
202
  if (mode === 'months') {
163
- return date.endOf('month').startOf('day');
203
+ return date.endOf('year').startOf('month');
164
204
  }
165
205
  const startDate = getStartDate(date, mode);
206
+ if (mode === 'quarters') {
207
+ return startDate.add(15, 'quarters');
208
+ }
166
209
  return startDate.add({ [mode]: 11 });
167
210
  }
168
211
  function isInvalid(date, minValue, maxValue, mode = 'days') {
@@ -20,10 +20,18 @@ export function useRangeCalendarState(props) {
20
20
  const [anchorDate, setAnchorDateState] = React.useState();
21
21
  const calendar = useCalendarState(Object.assign(Object.assign({}, calendarProps), { value: (_a = value === null || value === void 0 ? void 0 : value.start) !== null && _a !== void 0 ? _a : null }));
22
22
  const highlightedRange = anchorDate
23
- ? makeRange(anchorDate, calendar.focusedDate)
24
- : (_b = (value && makeRange(value.start, value.end))) !== null && _b !== void 0 ? _b : undefined;
25
- const selectDate = (date) => {
26
- if (props.disabled || props.readOnly) {
23
+ ? makeRange(anchorDate, calendar.focusedDate, calendar.mode)
24
+ : (_b = (value && makeRange(value.start, value.end, calendar.mode))) !== null && _b !== void 0 ? _b : undefined;
25
+ const minMode = calendar.availableModes[0];
26
+ const selectDate = (date, force = false) => {
27
+ if (props.disabled) {
28
+ return;
29
+ }
30
+ if (!force && calendar.mode !== minMode) {
31
+ calendar.zoomIn();
32
+ return;
33
+ }
34
+ if (props.readOnly) {
27
35
  return;
28
36
  }
29
37
  date = constrainValue(date, props.minValue, props.maxValue);
@@ -31,7 +39,7 @@ export function useRangeCalendarState(props) {
31
39
  return;
32
40
  }
33
41
  if (anchorDate) {
34
- const range = makeRange(anchorDate, date);
42
+ const range = makeRange(anchorDate, date, calendar.mode);
35
43
  setValue(range);
36
44
  setAnchorDateState(undefined);
37
45
  }
@@ -43,9 +51,6 @@ export function useRangeCalendarState(props) {
43
51
  setValue,
44
52
  selectDate,
45
53
  anchorDate, setAnchorDate: setAnchorDateState, highlightedRange,
46
- selectFocusedDate() {
47
- selectDate(calendar.focusedDate);
48
- },
49
54
  isSelected(date) {
50
55
  if (!highlightedRange) {
51
56
  return false;
@@ -61,15 +66,15 @@ export function useRangeCalendarState(props) {
61
66
  }
62
67
  } });
63
68
  }
64
- function makeRange(start, end) {
69
+ function makeRange(start, end, mode) {
65
70
  if (start.isBefore(end)) {
66
71
  return {
67
72
  start,
68
- end,
73
+ end: end.endOf(mode),
69
74
  };
70
75
  }
71
76
  return {
72
77
  start: end,
73
- end: start,
78
+ end: start.endOf(mode),
74
79
  };
75
80
  }
@@ -1,4 +1,6 @@
1
1
  import type { DateTime } from '@gravity-ui/date-utils';
2
+ import type { CalendarLayout } from './hooks/types';
2
3
  export declare function constrainValue(value: DateTime, minValue: DateTime | undefined, maxValue: DateTime | undefined): DateTime;
3
- export declare function getDaysInPeriod(startDate: DateTime, _endDate: DateTime, mode: 'days' | 'months' | 'years'): DateTime[];
4
+ export declare function getDaysInPeriod(startDate: DateTime, _endDate: DateTime, mode: CalendarLayout): DateTime[];
4
5
  export declare function getWeekDays(): DateTime[];
6
+ export declare const calendarLayouts: CalendarLayout[];
@@ -16,6 +16,11 @@ export function getDaysInPeriod(startDate, _endDate, mode) {
16
16
  days.push(currentDate.add({ days: i }));
17
17
  }
18
18
  }
19
+ else if (mode === 'quarters') {
20
+ for (let i = 0; i < 16; i++) {
21
+ days.push(startDate.add(i, 'quarters'));
22
+ }
23
+ }
19
24
  else {
20
25
  for (let i = 0; i < 12; i++) {
21
26
  days.push(startDate.add({ [mode]: i }));
@@ -32,3 +37,4 @@ export function getWeekDays() {
32
37
  }
33
38
  return weekDays;
34
39
  }
40
+ export const calendarLayouts = ['days', 'months', 'quarters', 'years'];
@@ -80,12 +80,16 @@ export function useDateFieldProps(state, props) {
80
80
  if (state.selectedSectionIndexes !== null) {
81
81
  return;
82
82
  }
83
- const input = inputRef.current;
83
+ const input = e.target;
84
+ const isAutofocus = !inputRef.current;
84
85
  setTimeout(() => {
85
86
  if (!input || input !== inputRef.current) {
86
87
  return;
87
88
  }
88
- if (
89
+ if (isAutofocus) {
90
+ state.focusSectionInPosition(0);
91
+ }
92
+ else if (
89
93
  // avoid selecting all sections when focusing empty field without value
90
94
  input.value.length &&
91
95
  Number(input.selectionEnd) - Number(input.selectionStart) ===
@@ -115,11 +119,11 @@ export function useDateFieldProps(state, props) {
115
119
  }
116
120
  else if (e.key === 'Home') {
117
121
  e.preventDefault();
118
- state.focusFirstSection();
122
+ state.decrementToMin();
119
123
  }
120
124
  else if (e.key === 'End') {
121
125
  e.preventDefault();
122
- state.focusLastSection();
126
+ state.incrementToMax();
123
127
  }
124
128
  else if (e.key === 'ArrowUp' && !e.altKey) {
125
129
  e.preventDefault();
@@ -163,32 +167,35 @@ export function useDateFieldProps(state, props) {
163
167
  onBeforeInput(e) {
164
168
  e.preventDefault();
165
169
  // @ts-expect-error
166
- const key = e.nativeEvent.data;
167
- // eslint-disable-next-line no-eq-null, eqeqeq
168
- if (key != null) {
170
+ const key = e.data;
171
+ if (key !== undefined && key !== null) {
169
172
  state.onInput(key);
170
173
  }
171
174
  },
172
175
  onPaste(e) {
176
+ e.preventDefault();
173
177
  if (state.readOnly) {
174
- e.preventDefault();
175
178
  return;
176
179
  }
177
180
  const pastedValue = e.clipboardData.getData('text');
178
- if (state.setValueFromString(pastedValue)) {
179
- e.preventDefault();
180
- }
181
- else if (state.selectedSectionIndexes &&
181
+ if (state.selectedSectionIndexes &&
182
182
  state.selectedSectionIndexes.startIndex ===
183
183
  state.selectedSectionIndexes.endIndex) {
184
184
  const activeSection = state.sections[state.selectedSectionIndexes.startIndex];
185
+ const digitsOnly = /^\d+$/.test(pastedValue);
186
+ const lettersOnly = /^[a-zA-Z]+$/.test(pastedValue);
185
187
  const isValidValue = Boolean(activeSection) &&
186
- ((activeSection.contentType === 'digit' && /^\d+$/.test(pastedValue)) ||
187
- activeSection.contentType === 'letter');
188
- if (!isValidValue) {
189
- e.preventDefault();
188
+ ((activeSection.contentType === 'digit' && digitsOnly) ||
189
+ (activeSection.contentType === 'letter' && lettersOnly));
190
+ if (isValidValue) {
191
+ state.onInput(pastedValue);
192
+ return;
193
+ }
194
+ if (digitsOnly || lettersOnly) {
195
+ return;
190
196
  }
191
197
  }
198
+ state.setValueFromString(pastedValue);
192
199
  },
193
200
  },
194
201
  },
@@ -56,6 +56,8 @@ export type DateFieldState = {
56
56
  * Upon reaching the minimum or maximum value, the value wraps around to the opposite limit.
57
57
  */
58
58
  decrementPage: () => void;
59
+ incrementToMax: () => void;
60
+ decrementToMin: () => void;
59
61
  /** Clears the value of the currently selected segment, reverting it to the placeholder. */
60
62
  clearSection: () => void;
61
63
  /** Clears all segments, reverting them to the placeholder. */