@gravity-ui/date-components 1.0.0 → 1.1.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/cjs/components/Calendar/Calendar.css +9 -1
- package/dist/cjs/components/Calendar/CalendarBase.js +9 -9
- package/dist/cjs/components/Calendar/hooks/types.d.ts +5 -2
- package/dist/cjs/components/Calendar/hooks/useCalendarCellProps.js +21 -16
- package/dist/cjs/components/Calendar/hooks/useCalendarGridProps.js +2 -7
- package/dist/cjs/components/Calendar/hooks/useCalendarProps.js +6 -3
- package/dist/cjs/components/Calendar/hooks/useCalendarState.js +69 -26
- package/dist/cjs/components/Calendar/hooks/useRangeCalendarState.js +6 -6
- package/dist/cjs/components/Calendar/utils.d.ts +3 -1
- package/dist/cjs/components/Calendar/utils.js +7 -1
- package/dist/cjs/components/DateField/hooks/useDateFieldProps.js +2 -2
- package/dist/cjs/components/DateField/hooks/useDateFieldState.d.ts +2 -0
- package/dist/cjs/components/DateField/hooks/useDateFieldState.js +33 -6
- package/dist/cjs/components/DateField/types.d.ts +1 -1
- package/dist/cjs/components/DateField/utils.d.ts +0 -9
- package/dist/cjs/components/DateField/utils.js +1 -25
- package/dist/cjs/components/DatePicker/DatePicker.d.ts +4 -1
- package/dist/cjs/components/DatePicker/DatePicker.js +2 -2
- package/dist/cjs/components/DatePicker/DesktopCalendar.d.ts +3 -1
- package/dist/cjs/components/DatePicker/DesktopCalendar.js +17 -8
- package/dist/cjs/components/DatePicker/MobileCalendar.js +4 -4
- package/dist/cjs/components/DatePicker/hooks/useDatePickerState.js +3 -8
- package/dist/cjs/components/RelativeDatePicker/RelativeDatePicker.d.ts +3 -0
- package/dist/cjs/components/RelativeDatePicker/RelativeDatePicker.js +1 -1
- package/dist/cjs/components/RelativeDatePicker/hooks/useRelativeDatePickerProps.js +7 -1
- package/dist/cjs/components/utils/dates.d.ts +9 -0
- package/dist/cjs/components/utils/dates.js +28 -0
- package/dist/esm/components/Calendar/Calendar.css +9 -1
- package/dist/esm/components/Calendar/CalendarBase.js +10 -10
- package/dist/esm/components/Calendar/hooks/types.d.ts +5 -2
- package/dist/esm/components/Calendar/hooks/useCalendarCellProps.js +21 -16
- package/dist/esm/components/Calendar/hooks/useCalendarGridProps.js +2 -7
- package/dist/esm/components/Calendar/hooks/useCalendarProps.js +6 -3
- package/dist/esm/components/Calendar/hooks/useCalendarState.js +70 -27
- package/dist/esm/components/Calendar/hooks/useRangeCalendarState.js +6 -6
- package/dist/esm/components/Calendar/utils.d.ts +3 -1
- package/dist/esm/components/Calendar/utils.js +6 -0
- package/dist/esm/components/DateField/hooks/useDateFieldProps.js +2 -2
- package/dist/esm/components/DateField/hooks/useDateFieldState.d.ts +2 -0
- package/dist/esm/components/DateField/hooks/useDateFieldState.js +28 -1
- package/dist/esm/components/DateField/types.d.ts +1 -1
- package/dist/esm/components/DateField/utils.d.ts +0 -9
- package/dist/esm/components/DateField/utils.js +0 -21
- package/dist/esm/components/DatePicker/DatePicker.d.ts +4 -1
- package/dist/esm/components/DatePicker/DatePicker.js +2 -2
- package/dist/esm/components/DatePicker/DesktopCalendar.d.ts +3 -1
- package/dist/esm/components/DatePicker/DesktopCalendar.js +17 -8
- package/dist/esm/components/DatePicker/MobileCalendar.js +1 -1
- package/dist/esm/components/DatePicker/hooks/useDatePickerState.js +2 -7
- package/dist/esm/components/RelativeDatePicker/RelativeDatePicker.d.ts +3 -0
- package/dist/esm/components/RelativeDatePicker/RelativeDatePicker.js +1 -1
- package/dist/esm/components/RelativeDatePicker/hooks/useRelativeDatePickerProps.js +7 -1
- package/dist/esm/components/utils/dates.d.ts +9 -0
- package/dist/esm/components/utils/dates.js +22 -0
- package/package.json +1 -1
|
@@ -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 {
|
|
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
|
|
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) ||
|
|
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(
|
|
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
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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(
|
|
74
|
+
focusCell(focusedDate.add(1, this.mode));
|
|
58
75
|
},
|
|
59
76
|
focusPreviousCell() {
|
|
60
|
-
focusCell(focusedDate.subtract(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
130
|
+
const index = availableModes.indexOf(this.mode) - 1;
|
|
131
|
+
if (index >= 0) {
|
|
132
|
+
this.setMode(availableModes[index]);
|
|
133
|
+
}
|
|
102
134
|
},
|
|
103
135
|
zoomOut() {
|
|
104
|
-
|
|
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.
|
|
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(
|
|
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 ===
|
|
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('
|
|
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,8 +20,8 @@ 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;
|
|
23
|
+
? makeRange(anchorDate, calendar.focusedDate, calendar.mode)
|
|
24
|
+
: (_b = (value && makeRange(value.start, value.end, calendar.mode))) !== null && _b !== void 0 ? _b : undefined;
|
|
25
25
|
const selectDate = (date) => {
|
|
26
26
|
if (props.disabled || props.readOnly) {
|
|
27
27
|
return;
|
|
@@ -31,7 +31,7 @@ export function useRangeCalendarState(props) {
|
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
33
|
if (anchorDate) {
|
|
34
|
-
const range = makeRange(anchorDate, date);
|
|
34
|
+
const range = makeRange(anchorDate, date, calendar.mode);
|
|
35
35
|
setValue(range);
|
|
36
36
|
setAnchorDateState(undefined);
|
|
37
37
|
}
|
|
@@ -61,15 +61,15 @@ export function useRangeCalendarState(props) {
|
|
|
61
61
|
}
|
|
62
62
|
} });
|
|
63
63
|
}
|
|
64
|
-
function makeRange(start, end) {
|
|
64
|
+
function makeRange(start, end, mode) {
|
|
65
65
|
if (start.isBefore(end)) {
|
|
66
66
|
return {
|
|
67
67
|
start,
|
|
68
|
-
end,
|
|
68
|
+
end: end.endOf(mode),
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
71
|
return {
|
|
72
72
|
start: end,
|
|
73
|
-
end: start,
|
|
73
|
+
end: start.endOf(mode),
|
|
74
74
|
};
|
|
75
75
|
}
|
|
@@ -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:
|
|
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'];
|
|
@@ -115,11 +115,11 @@ export function useDateFieldProps(state, props) {
|
|
|
115
115
|
}
|
|
116
116
|
else if (e.key === 'Home') {
|
|
117
117
|
e.preventDefault();
|
|
118
|
-
state.
|
|
118
|
+
state.decrementToMin();
|
|
119
119
|
}
|
|
120
120
|
else if (e.key === 'End') {
|
|
121
121
|
e.preventDefault();
|
|
122
|
-
state.
|
|
122
|
+
state.incrementToMax();
|
|
123
123
|
}
|
|
124
124
|
else if (e.key === 'ArrowUp' && !e.altKey) {
|
|
125
125
|
e.preventDefault();
|
|
@@ -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. */
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { dateTime, isValid } from '@gravity-ui/date-utils';
|
|
3
3
|
import { useControlledState } from '../../hooks/useControlledState';
|
|
4
|
-
import { createPlaceholderValue, isInvalid, mergeDateTime
|
|
4
|
+
import { createPlaceholderValue, isInvalid, mergeDateTime } from '../../utils/dates';
|
|
5
|
+
import { splitFormatIntoSections } from '../utils';
|
|
5
6
|
const EDITABLE_SEGMENTS = {
|
|
6
7
|
year: true,
|
|
7
8
|
month: true,
|
|
@@ -219,6 +220,32 @@ export function useDateFieldState(props) {
|
|
|
219
220
|
adjustSection(sectionIndex, -(PAGE_STEP[this.sections[sectionIndex].type] || 1));
|
|
220
221
|
}
|
|
221
222
|
},
|
|
223
|
+
incrementToMax() {
|
|
224
|
+
if (this.readOnly || this.disabled) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
enteredKeys.current = '';
|
|
228
|
+
const sectionIndex = getCurrentEditableSectionIndex(this.sections, selectedSections);
|
|
229
|
+
if (sectionIndex !== -1) {
|
|
230
|
+
const section = this.sections[sectionIndex];
|
|
231
|
+
if (typeof section.maxValue === 'number') {
|
|
232
|
+
setSection(section, section.maxValue);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
decrementToMin() {
|
|
237
|
+
if (this.readOnly || this.disabled) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
enteredKeys.current = '';
|
|
241
|
+
const sectionIndex = getCurrentEditableSectionIndex(this.sections, selectedSections);
|
|
242
|
+
if (sectionIndex !== -1) {
|
|
243
|
+
const section = this.sections[sectionIndex];
|
|
244
|
+
if (typeof section.minValue === 'number') {
|
|
245
|
+
setSection(section, section.minValue);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
},
|
|
222
249
|
clearSection() {
|
|
223
250
|
if (this.readOnly || this.disabled) {
|
|
224
251
|
return;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type DateFieldSectionType =
|
|
1
|
+
export type DateFieldSectionType = Extract<Intl.DateTimeFormatPartTypes, 'day' | 'dayPeriod' | 'hour' | 'literal' | 'minute' | 'month' | 'second' | 'timeZoneName' | 'weekday' | 'year' | 'unknown'>;
|
|
2
2
|
export type DateFormatTokenMap = {
|
|
3
3
|
[formatToken: string]: DateFieldSectionType | {
|
|
4
4
|
sectionType: DateFieldSectionType;
|
|
@@ -1,14 +1,5 @@
|
|
|
1
|
-
import type { DateTime } from '@gravity-ui/date-utils';
|
|
2
1
|
import type { DateFieldSectionWithoutPosition } from './types';
|
|
3
2
|
export declare const isMac: boolean;
|
|
4
3
|
export declare const CtrlCmd: string;
|
|
5
4
|
export declare function expandFormat(format: string): string;
|
|
6
5
|
export declare function splitFormatIntoSections(format: string): DateFieldSectionWithoutPosition[];
|
|
7
|
-
interface PlaceholderValueOptions {
|
|
8
|
-
placeholderValue?: DateTime;
|
|
9
|
-
timeZone?: string;
|
|
10
|
-
}
|
|
11
|
-
export declare function createPlaceholderValue({ placeholderValue, timeZone }: PlaceholderValueOptions): DateTime;
|
|
12
|
-
export declare function isInvalid(value: DateTime | null | undefined, minValue: DateTime | undefined, maxValue: DateTime | undefined): boolean;
|
|
13
|
-
export declare function mergeDateTime(date: DateTime, time: DateTime): DateTime;
|
|
14
|
-
export {};
|
|
@@ -245,24 +245,3 @@ function getSectionOptions(section, token) {
|
|
|
245
245
|
}
|
|
246
246
|
return undefined;
|
|
247
247
|
}
|
|
248
|
-
export function createPlaceholderValue({ placeholderValue, timeZone }) {
|
|
249
|
-
return (placeholderValue !== null && placeholderValue !== void 0 ? placeholderValue : dateTime({ timeZone }).set('hour', 0).set('minute', 0).set('second', 0));
|
|
250
|
-
}
|
|
251
|
-
export function isInvalid(value, minValue, maxValue) {
|
|
252
|
-
if (!value) {
|
|
253
|
-
return false;
|
|
254
|
-
}
|
|
255
|
-
if (minValue && value.isBefore(minValue)) {
|
|
256
|
-
return true;
|
|
257
|
-
}
|
|
258
|
-
if (maxValue && maxValue.isBefore(value)) {
|
|
259
|
-
return true;
|
|
260
|
-
}
|
|
261
|
-
return false;
|
|
262
|
-
}
|
|
263
|
-
export function mergeDateTime(date, time) {
|
|
264
|
-
return date
|
|
265
|
-
.set('hours', time.hour())
|
|
266
|
-
.set('minutes', time.minute())
|
|
267
|
-
.set('seconds', time.second());
|
|
268
|
-
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { CalendarProps } from '../Calendar';
|
|
1
3
|
import type { AccessibilityProps, DateFieldBase, DomProps, FocusableProps, KeyboardEvents, StyleProps, TextInputProps } from '../types';
|
|
2
4
|
import './DatePicker.css';
|
|
3
5
|
export interface DatePickerProps extends DateFieldBase, TextInputProps, FocusableProps, KeyboardEvents, DomProps, StyleProps, AccessibilityProps {
|
|
6
|
+
children?: (props: CalendarProps) => React.ReactNode;
|
|
4
7
|
}
|
|
5
|
-
export declare function DatePicker({ value, defaultValue, onUpdate, className, onFocus, onBlur, ...props }: DatePickerProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export declare function DatePicker({ value, defaultValue, onUpdate, className, onFocus, onBlur, children, ...props }: DatePickerProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -19,7 +19,7 @@ import { useDatePickerState } from './hooks/useDatePickerState';
|
|
|
19
19
|
import { b } from './utils';
|
|
20
20
|
import './DatePicker.css';
|
|
21
21
|
export function DatePicker(_a) {
|
|
22
|
-
var { value, defaultValue, onUpdate, className, onFocus, onBlur } = _a, props = __rest(_a, ["value", "defaultValue", "onUpdate", "className", "onFocus", "onBlur"]);
|
|
22
|
+
var { value, defaultValue, onUpdate, className, onFocus, onBlur, children } = _a, props = __rest(_a, ["value", "defaultValue", "onUpdate", "className", "onFocus", "onBlur", "children"]);
|
|
23
23
|
const anchorRef = React.useRef(null);
|
|
24
24
|
const state = useDatePickerState(Object.assign(Object.assign({}, props), { value,
|
|
25
25
|
defaultValue,
|
|
@@ -55,5 +55,5 @@ export function DatePicker(_a) {
|
|
|
55
55
|
e.stopPropagation();
|
|
56
56
|
state.setOpen(true);
|
|
57
57
|
}
|
|
58
|
-
}, children: [isMobile ? (_jsx(MobileCalendar, { props: props, state: state })) : (_jsx(DesktopCalendar, { anchorRef: anchorRef, props: props, state: state })), _jsx(TextInput, Object.assign({}, inputProps, { value: fieldState.isEmpty && !isActive && props.placeholder ? '' : inputProps.value, className: b('field', { mobile: isMobile }), hasClear: !isMobile && inputProps.hasClear, rightContent: isMobile ? (_jsx(MobileCalendarIcon, { props: props, state: state })) : (_jsx(DesktopCalendarButton, { props: props, state: state })) }))] })));
|
|
58
|
+
}, children: [isMobile ? (_jsx(MobileCalendar, { props: props, state: state })) : (_jsx(DesktopCalendar, { anchorRef: anchorRef, props: props, state: state, renderCalendar: children })), _jsx(TextInput, Object.assign({}, inputProps, { value: fieldState.isEmpty && !isActive && props.placeholder ? '' : inputProps.value, className: b('field', { mobile: isMobile }), hasClear: !isMobile && inputProps.hasClear, rightContent: isMobile ? (_jsx(MobileCalendarIcon, { props: props, state: state })) : (_jsx(DesktopCalendarButton, { props: props, state: state })) }))] })));
|
|
59
59
|
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { type CalendarProps } from '../Calendar';
|
|
2
3
|
import type { DatePickerProps } from './DatePicker';
|
|
3
4
|
import type { DatePickerState } from './hooks/useDatePickerState';
|
|
4
5
|
interface DesktopCalendarProps {
|
|
5
6
|
anchorRef: React.RefObject<HTMLElement>;
|
|
6
7
|
props: DatePickerProps;
|
|
7
8
|
state: DatePickerState;
|
|
9
|
+
renderCalendar?: (props: CalendarProps) => React.ReactNode;
|
|
8
10
|
}
|
|
9
|
-
export declare function DesktopCalendar({ anchorRef, props, state }: DesktopCalendarProps): import("react/jsx-runtime").JSX.Element | null;
|
|
11
|
+
export declare function DesktopCalendar({ anchorRef, props, state, renderCalendar }: DesktopCalendarProps): import("react/jsx-runtime").JSX.Element | null;
|
|
10
12
|
interface DesktopCalendarButtonProps {
|
|
11
13
|
props: DatePickerProps;
|
|
12
14
|
state: DatePickerState;
|
|
@@ -7,7 +7,7 @@ import { DateField } from '../DateField';
|
|
|
7
7
|
import { getButtonSizeForInput } from '../utils/getButtonSizeForInput';
|
|
8
8
|
import { i18n } from './i18n';
|
|
9
9
|
import { b } from './utils';
|
|
10
|
-
export function DesktopCalendar({ anchorRef, props, state }) {
|
|
10
|
+
export function DesktopCalendar({ anchorRef, props, state, renderCalendar }) {
|
|
11
11
|
var _a;
|
|
12
12
|
const { focusWithinProps } = useFocusWithin({
|
|
13
13
|
isDisabled: !state.isOpen,
|
|
@@ -18,15 +18,24 @@ export function DesktopCalendar({ anchorRef, props, state }) {
|
|
|
18
18
|
if (!state.hasDate) {
|
|
19
19
|
return null;
|
|
20
20
|
}
|
|
21
|
+
const calendarProps = {
|
|
22
|
+
autoFocus: true,
|
|
23
|
+
size: props.size === 's' ? 'm' : props.size,
|
|
24
|
+
disabled: props.disabled,
|
|
25
|
+
readOnly: props.readOnly,
|
|
26
|
+
onUpdate: (d) => {
|
|
27
|
+
state.setDateValue(d);
|
|
28
|
+
},
|
|
29
|
+
defaultFocusedValue: (_a = state.dateValue) !== null && _a !== void 0 ? _a : undefined,
|
|
30
|
+
value: state.dateValue,
|
|
31
|
+
minValue: props.minValue,
|
|
32
|
+
maxValue: props.maxValue,
|
|
33
|
+
isDateUnavailable: props.isDateUnavailable,
|
|
34
|
+
timeZone: props.timeZone,
|
|
35
|
+
};
|
|
21
36
|
return (_jsx(Popup, { open: state.isOpen, anchorRef: anchorRef, onClose: () => {
|
|
22
37
|
state.setOpen(false);
|
|
23
|
-
}, restoreFocus: true, children: _jsxs("div", Object.assign({}, focusWithinProps, { className: b('popup-content'), children: [_jsx(Calendar
|
|
24
|
-
// eslint-disable-next-line jsx-a11y/no-autofocus
|
|
25
|
-
, {
|
|
26
|
-
// eslint-disable-next-line jsx-a11y/no-autofocus
|
|
27
|
-
autoFocus: true, size: props.size === 's' ? 'm' : props.size, disabled: props.disabled, readOnly: props.readOnly, onUpdate: (d) => {
|
|
28
|
-
state.setDateValue(d);
|
|
29
|
-
}, defaultFocusedValue: (_a = state.dateValue) !== null && _a !== void 0 ? _a : undefined, value: state.dateValue, minValue: props.minValue, maxValue: props.maxValue, isDateUnavailable: props.isDateUnavailable, timeZone: props.timeZone }), state.hasTime && (_jsx("div", { className: b('time-field-wrapper'), children: _jsx(DateField, { className: b('time-field'), size: props.size, readOnly: props.readOnly, value: state.timeValue, onUpdate: state.setTimeValue, placeholderValue: props.placeholderValue, minValue: props.minValue, maxValue: props.maxValue, timeZone: props.timeZone, format: state.timeFormat }) }))] })) }));
|
|
38
|
+
}, restoreFocus: true, children: _jsxs("div", Object.assign({}, focusWithinProps, { className: b('popup-content'), tabIndex: -1, children: [typeof renderCalendar === 'function' ? (renderCalendar(calendarProps)) : (_jsx(Calendar, Object.assign({}, calendarProps))), state.hasTime && (_jsx("div", { className: b('time-field-wrapper'), children: _jsx(DateField, { className: b('time-field'), size: props.size, readOnly: props.readOnly, value: state.timeValue, onUpdate: state.setTimeValue, placeholderValue: props.placeholderValue, minValue: props.minValue, maxValue: props.maxValue, timeZone: props.timeZone, format: state.timeFormat }) }))] })) }));
|
|
30
39
|
}
|
|
31
40
|
export function DesktopCalendarButton({ props, state }) {
|
|
32
41
|
const wasOpenBeforeClickRef = React.useRef(false);
|
|
@@ -3,7 +3,7 @@ import { dateTime } from '@gravity-ui/date-utils';
|
|
|
3
3
|
import { Calendar as CalendarIcon } from '@gravity-ui/icons';
|
|
4
4
|
import { Icon } from '@gravity-ui/uikit';
|
|
5
5
|
import { block } from '../../utils/cn';
|
|
6
|
-
import { createPlaceholderValue, mergeDateTime } from '../
|
|
6
|
+
import { createPlaceholderValue, mergeDateTime } from '../utils/dates';
|
|
7
7
|
import { getButtonSizeForInput } from '../utils/getButtonSizeForInput';
|
|
8
8
|
import './MobileCalendar.css';
|
|
9
9
|
const b = block('mobile-calendar');
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { splitFormatIntoSections } from '../../DateField/utils';
|
|
3
3
|
import { useControlledState } from '../../hooks/useControlledState';
|
|
4
|
+
import { createPlaceholderValue, mergeDateTime } from '../../utils/dates';
|
|
4
5
|
export function useDatePickerState(props) {
|
|
5
6
|
const { disabled, readOnly } = props;
|
|
6
7
|
const [isOpen, setOpen] = React.useState(false);
|
|
@@ -96,12 +97,6 @@ export function useDatePickerState(props) {
|
|
|
96
97
|
},
|
|
97
98
|
};
|
|
98
99
|
}
|
|
99
|
-
function mergeDateTime(date, time) {
|
|
100
|
-
return date
|
|
101
|
-
.set('hours', time.hour())
|
|
102
|
-
.set('minutes', time.minute())
|
|
103
|
-
.set('seconds', time.second());
|
|
104
|
-
}
|
|
105
100
|
function getPlaceholderTime(placeholderValue, timeZone) {
|
|
106
101
|
return createPlaceholderValue({ placeholderValue, timeZone });
|
|
107
102
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { CalendarProps } from '../Calendar';
|
|
1
3
|
import type { AccessibilityProps, DomProps, FocusableProps, KeyboardEvents, StyleProps, TextInputProps } from '../types';
|
|
2
4
|
import type { RelativeDatePickerStateOptions } from './hooks/useRelativeDatePickerState';
|
|
3
5
|
import './RelativeDatePicker.css';
|
|
4
6
|
export interface RelativeDatePickerProps extends RelativeDatePickerStateOptions, TextInputProps, FocusableProps, KeyboardEvents, DomProps, StyleProps, AccessibilityProps {
|
|
7
|
+
children?: (props: CalendarProps) => React.ReactNode;
|
|
5
8
|
}
|
|
6
9
|
export declare function RelativeDatePicker(props: RelativeDatePickerProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -21,5 +21,5 @@ export function RelativeDatePicker(props) {
|
|
|
21
21
|
readOnly: props.readOnly,
|
|
22
22
|
placeholderValue: props.placeholderValue,
|
|
23
23
|
timeZone: props.timeZone,
|
|
24
|
-
} })), _jsx(TextInput, Object.assign({}, fieldProps, { leftContent: _jsx(Button, Object.assign({}, modeSwitcherProps, { children: _jsx(Icon, { data: FunctionIcon }) })), rightContent: _jsxs(React.Fragment, { children: [!isMobile && (_jsx(Button, Object.assign({}, calendarButtonProps, { children: _jsx(Icon, { data: CalendarIcon }) }))), isMobile && state.mode === 'absolute' && (_jsx(MobileCalendarIcon, { state: state.datePickerState, props: { size: props.size } }))] }) })), !isMobile && (_jsx(Popup, Object.assign({}, popupProps, { anchorRef: anchorRef, children: _jsxs("div", { className: b('popup-content'), children: [_jsx(Calendar, Object.assign({}, calendarProps)), state.datePickerState.hasTime && (_jsx("div", { className: b('time-field-wrapper'), children: _jsx(DateField, Object.assign({}, timeInputProps)) }))] }) })))] })));
|
|
24
|
+
} })), _jsx(TextInput, Object.assign({}, fieldProps, { leftContent: _jsx(Button, Object.assign({}, modeSwitcherProps, { children: _jsx(Icon, { data: FunctionIcon }) })), rightContent: _jsxs(React.Fragment, { children: [!isMobile && (_jsx(Button, Object.assign({}, calendarButtonProps, { children: _jsx(Icon, { data: CalendarIcon }) }))), isMobile && state.mode === 'absolute' && (_jsx(MobileCalendarIcon, { state: state.datePickerState, props: { size: props.size } }))] }) })), !isMobile && (_jsx(Popup, Object.assign({}, popupProps, { anchorRef: anchorRef, children: _jsxs("div", { className: b('popup-content'), children: [typeof props.children === 'function' ? (props.children(calendarProps)) : (_jsx(Calendar, Object.assign({}, calendarProps))), state.datePickerState.hasTime && (_jsx("div", { className: b('time-field-wrapper'), children: _jsx(DateField, Object.assign({}, timeInputProps)) }))] }) })))] })));
|
|
25
25
|
}
|
|
@@ -81,7 +81,13 @@ export function useRelativeDatePickerProps(state, _a) {
|
|
|
81
81
|
const handleRef = useForkRef(inputRef, mode === 'relative' ? relativeDateProps.controlRef : inputProps.controlRef);
|
|
82
82
|
const calendarRef = React.useRef(null);
|
|
83
83
|
return {
|
|
84
|
-
groupProps: Object.assign({ role: 'group' }, focusWithinProps),
|
|
84
|
+
groupProps: Object.assign(Object.assign({ role: 'group' }, focusWithinProps), { onKeyDown: (e) => {
|
|
85
|
+
if (e.altKey && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {
|
|
86
|
+
e.preventDefault();
|
|
87
|
+
e.stopPropagation();
|
|
88
|
+
setOpen(true);
|
|
89
|
+
}
|
|
90
|
+
} }),
|
|
85
91
|
fieldProps: mergeProps(commonInputProps, mode === 'relative' ? relativeDateProps : inputProps, { controlRef: handleRef, error }),
|
|
86
92
|
modeSwitcherProps: {
|
|
87
93
|
size: getButtonSizeForInput(props.size),
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DateTime } from '@gravity-ui/date-utils';
|
|
2
|
+
interface PlaceholderValueOptions {
|
|
3
|
+
placeholderValue?: DateTime;
|
|
4
|
+
timeZone?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function createPlaceholderValue({ placeholderValue, timeZone }: PlaceholderValueOptions): DateTime;
|
|
7
|
+
export declare function isInvalid(value: DateTime | null | undefined, minValue: DateTime | undefined, maxValue: DateTime | undefined): boolean;
|
|
8
|
+
export declare function mergeDateTime(date: DateTime, time: DateTime): DateTime;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { dateTime } from '@gravity-ui/date-utils';
|
|
2
|
+
export function createPlaceholderValue({ placeholderValue, timeZone }) {
|
|
3
|
+
return (placeholderValue !== null && placeholderValue !== void 0 ? placeholderValue : dateTime({ timeZone }).set('hour', 0).set('minute', 0).set('second', 0));
|
|
4
|
+
}
|
|
5
|
+
export function isInvalid(value, minValue, maxValue) {
|
|
6
|
+
if (!value) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
if (minValue && value.isBefore(minValue)) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
if (maxValue && maxValue.isBefore(value)) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
export function mergeDateTime(date, time) {
|
|
18
|
+
return date
|
|
19
|
+
.set('hours', time.hour())
|
|
20
|
+
.set('minutes', time.minute())
|
|
21
|
+
.set('seconds', time.second());
|
|
22
|
+
}
|