@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
|
@@ -82,6 +82,11 @@
|
|
|
82
82
|
.g-date-calendar__content_animation_zoom-out.g-date-calendar__previous-state {
|
|
83
83
|
animation: calendar-zoom-out-hiding 0.25s ease forwards;
|
|
84
84
|
}
|
|
85
|
+
@media screen and (prefers-reduced-motion: reduce), (update: slow) {
|
|
86
|
+
.g-date-calendar__content[class] {
|
|
87
|
+
animation-duration: 0.001ms;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
85
90
|
@keyframes calendar-forward {
|
|
86
91
|
from {
|
|
87
92
|
transform: translateX(0);
|
|
@@ -142,10 +147,13 @@
|
|
|
142
147
|
display: grid;
|
|
143
148
|
gap: var(--_--calendar-days-gap);
|
|
144
149
|
}
|
|
145
|
-
.g-date-calendar__grid-rowgroup_mode_months, .g-date-calendar__grid-rowgroup_mode_years {
|
|
150
|
+
.g-date-calendar__grid-rowgroup_mode_months, .g-date-calendar__grid-rowgroup_mode_quarters, .g-date-calendar__grid-rowgroup_mode_years {
|
|
146
151
|
grid-row: 1/-1;
|
|
147
152
|
padding: 12px 0 0;
|
|
148
153
|
}
|
|
154
|
+
.g-date-calendar__grid-rowgroup-header {
|
|
155
|
+
align-self: center;
|
|
156
|
+
}
|
|
149
157
|
.g-date-calendar__grid-row {
|
|
150
158
|
display: grid;
|
|
151
159
|
grid-auto-columns: 1fr;
|
|
@@ -22,7 +22,8 @@ exports.CalendarBase = react_1.default.forwardRef(function Calendar(props, ref)
|
|
|
22
22
|
state.setFocused(true);
|
|
23
23
|
},
|
|
24
24
|
}));
|
|
25
|
-
return ((0, jsx_runtime_1.jsxs)("div", Object.assign({}, calendarProps, { className: b({ size: props.size }), children: [(0, jsx_runtime_1.jsxs)("div", { className: b('header'), children: [(0, jsx_runtime_1.jsx)(uikit_1.Button, Object.assign({}, modeButtonProps, { view: "flat", size: props.size, children: state.
|
|
25
|
+
return ((0, jsx_runtime_1.jsxs)("div", Object.assign({}, calendarProps, { className: b({ size: props.size }), children: [(0, jsx_runtime_1.jsxs)("div", { className: b('header'), children: [(0, jsx_runtime_1.jsx)(uikit_1.Button, Object.assign({}, modeButtonProps, { view: "flat", size: props.size, children: state.availableModes.indexOf(state.mode) + 1 ===
|
|
26
|
+
state.availableModes.length ? ((0, jsx_runtime_1.jsx)("span", { className: b('mode-label', b(`years-label`)), children: modeButtonProps.children }, "label")) : ([
|
|
26
27
|
(0, jsx_runtime_1.jsx)("span", { className: b('mode-label'), children: modeButtonProps.children }, "label"),
|
|
27
28
|
(0, jsx_runtime_1.jsx)(uikit_1.Button.Icon, { children: (0, jsx_runtime_1.jsx)(uikit_1.ArrowToggle, { direction: "bottom" }) }, "icon"),
|
|
28
29
|
]) })), (0, jsx_runtime_1.jsxs)("div", { className: b('controls'), children: [(0, jsx_runtime_1.jsx)(uikit_1.Button, Object.assign({}, previousButtonProps, { view: "flat", size: props.size, children: (0, jsx_runtime_1.jsx)(uikit_1.Button.Icon, { children: (0, jsx_runtime_1.jsx)(uikit_1.ArrowToggle, { direction: "left" }) }) })), (0, jsx_runtime_1.jsx)(uikit_1.Button, Object.assign({}, nextButtonProps, { view: "flat", size: props.size, children: (0, jsx_runtime_1.jsx)(uikit_1.Button.Icon, { children: (0, jsx_runtime_1.jsx)(uikit_1.ArrowToggle, { direction: "right" }) }) }))] })] }), (0, jsx_runtime_1.jsx)(CalendarGrid, { state: state })] })));
|
|
@@ -33,8 +34,7 @@ function CalendarGrid({ state }) {
|
|
|
33
34
|
const startDateChanged = !state.startDate.isSame(prevState.startDate, 'days');
|
|
34
35
|
let animation;
|
|
35
36
|
if (modeChanged) {
|
|
36
|
-
if ((
|
|
37
|
-
(state.mode === 'months' && prevState.mode === 'years')) {
|
|
37
|
+
if (utils_1.calendarLayouts.indexOf(prevState.mode) > utils_1.calendarLayouts.indexOf(state.mode)) {
|
|
38
38
|
animation = 'zoom-out';
|
|
39
39
|
}
|
|
40
40
|
else {
|
|
@@ -65,13 +65,13 @@ function Weekdays({ state }) {
|
|
|
65
65
|
}
|
|
66
66
|
function CalendarGridCells({ state }) {
|
|
67
67
|
const rowsInPeriod = state.mode === 'days' ? 6 : 4;
|
|
68
|
-
const columnsInRow = state.mode === 'days' ? 7 : 3;
|
|
68
|
+
const columnsInRow = state.mode === 'days' ? 7 : 3 + (state.mode === 'quarters' ? 1 : 0);
|
|
69
69
|
const days = (0, utils_1.getDaysInPeriod)(state.startDate, state.endDate, state.mode);
|
|
70
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: b('grid-rowgroup', { mode: state.mode }), role: "rowgroup", children: [...new Array(rowsInPeriod).keys()].map((rowIndex) => ((0, jsx_runtime_1.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: b('grid-rowgroup', { mode: state.mode }), role: "rowgroup", children: [...new Array(rowsInPeriod).keys()].map((rowIndex) => ((0, jsx_runtime_1.jsxs)("div", { className: b('grid-row'), role: "row", children: [state.mode === 'quarters' ? ((0, jsx_runtime_1.jsx)("span", { role: "rowheader", className: b('grid-rowgroup-header'), children: days[rowIndex * columnsInRow].format('YYYY') })) : null, days
|
|
71
|
+
.slice(rowIndex * columnsInRow, (rowIndex + 1) * columnsInRow)
|
|
72
|
+
.map((date) => {
|
|
73
|
+
return (0, jsx_runtime_1.jsx)(CalendarCell, { date: date, state: state }, date.unix());
|
|
74
|
+
})] }, rowIndex))) }));
|
|
75
75
|
}
|
|
76
76
|
function CalendarCell({ date, state }) {
|
|
77
77
|
const { cellProps, buttonProps, formattedDate, isDisabled, isSelected, isRangeSelection, isSelectionStart, isSelectionEnd, isOutsideCurrentRange, isUnavailable, isCurrent, isWeekend, } = (0, useCalendarCellProps_1.useCalendarCellProps)(date, state);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DateTime } from '@gravity-ui/date-utils';
|
|
2
2
|
import type { InputBase, RangeValue } from '../../types';
|
|
3
|
-
export type CalendarLayout = 'days' | 'months' | 'years';
|
|
3
|
+
export type CalendarLayout = 'days' | 'months' | 'quarters' | 'years';
|
|
4
4
|
export interface CalendarStateOptionsBase extends InputBase {
|
|
5
5
|
/** The minimum allowed date that a user may select. */
|
|
6
6
|
minValue?: DateTime;
|
|
@@ -27,6 +27,8 @@ export interface CalendarStateOptionsBase extends InputBase {
|
|
|
27
27
|
defaultMode?: CalendarLayout;
|
|
28
28
|
/** Handler that is called when the mode changes */
|
|
29
29
|
onUpdateMode?: (mode: CalendarLayout) => void;
|
|
30
|
+
/** Controls which modes to use */
|
|
31
|
+
modes?: Partial<Record<CalendarLayout, boolean>>;
|
|
30
32
|
}
|
|
31
33
|
interface CalendarStateBase {
|
|
32
34
|
/** Whether the calendar is disabled. */
|
|
@@ -45,7 +47,7 @@ interface CalendarStateBase {
|
|
|
45
47
|
/** Selects the currently focused date. */
|
|
46
48
|
selectFocusedDate(): void;
|
|
47
49
|
/** Selects the given date. */
|
|
48
|
-
selectDate(date: DateTime): void;
|
|
50
|
+
selectDate(date: DateTime, force?: boolean): void;
|
|
49
51
|
/** Moves focus to the next calendar date. */
|
|
50
52
|
focusNextCell(): void;
|
|
51
53
|
/** Moves focus to the previous calendar date. */
|
|
@@ -98,6 +100,7 @@ interface CalendarStateBase {
|
|
|
98
100
|
readonly mode: CalendarLayout;
|
|
99
101
|
/** Sets calendar layout */
|
|
100
102
|
setMode(mode: CalendarLayout): void;
|
|
103
|
+
readonly availableModes: CalendarLayout[];
|
|
101
104
|
readonly startDate: DateTime;
|
|
102
105
|
readonly endDate: DateTime;
|
|
103
106
|
}
|
|
@@ -27,16 +27,7 @@ function useCalendarCellProps(date, state) {
|
|
|
27
27
|
const isSelectable = !isDisabled && !isUnavailable;
|
|
28
28
|
const isCurrent = (0, date_utils_1.dateTime)().isSame(date, state.mode);
|
|
29
29
|
const isWeekend = state.isWeekend(date);
|
|
30
|
-
|
|
31
|
-
if (state.mode === 'days') {
|
|
32
|
-
label = `${date.format('dddd')}, ${date.format('LL')}`;
|
|
33
|
-
}
|
|
34
|
-
else if (state.mode === 'months') {
|
|
35
|
-
label = `${date.format('MMMM YYYY')}`;
|
|
36
|
-
}
|
|
37
|
-
else if (state.mode === 'years') {
|
|
38
|
-
label = `${date.format('YYYY')}`;
|
|
39
|
-
}
|
|
30
|
+
const label = getDateLabel(date, state.mode);
|
|
40
31
|
const cellProps = {
|
|
41
32
|
role: 'gridcell',
|
|
42
33
|
'aria-selected': isSelected ? 'true' : undefined,
|
|
@@ -51,12 +42,7 @@ function useCalendarCellProps(date, state) {
|
|
|
51
42
|
onClick: isSelectable
|
|
52
43
|
? () => {
|
|
53
44
|
state.setFocusedDate(date);
|
|
54
|
-
|
|
55
|
-
state.selectDate(date);
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
state.zoomIn();
|
|
59
|
-
}
|
|
45
|
+
state.selectDate(date);
|
|
60
46
|
}
|
|
61
47
|
: undefined,
|
|
62
48
|
onPointerEnter() {
|
|
@@ -77,6 +63,9 @@ function useCalendarCellProps(date, state) {
|
|
|
77
63
|
if (state.mode === 'months') {
|
|
78
64
|
formattedDate = date.format('MMM');
|
|
79
65
|
}
|
|
66
|
+
else if (state.mode === 'quarters') {
|
|
67
|
+
formattedDate = date.format('[Q]Q');
|
|
68
|
+
}
|
|
80
69
|
else if (state.mode === 'years') {
|
|
81
70
|
formattedDate = date.format('YYYY');
|
|
82
71
|
}
|
|
@@ -96,3 +85,19 @@ function useCalendarCellProps(date, state) {
|
|
|
96
85
|
};
|
|
97
86
|
}
|
|
98
87
|
exports.useCalendarCellProps = useCalendarCellProps;
|
|
88
|
+
function getDateLabel(date, mode) {
|
|
89
|
+
let label = '';
|
|
90
|
+
if (mode === 'days') {
|
|
91
|
+
label = `${date.format('dddd')}, ${date.format('LL')}`;
|
|
92
|
+
}
|
|
93
|
+
else if (mode === 'months') {
|
|
94
|
+
label = `${date.format('MMMM YYYY')}`;
|
|
95
|
+
}
|
|
96
|
+
else if (mode === 'quarters') {
|
|
97
|
+
label = `${date.format('[Q]Q YYYY')}`;
|
|
98
|
+
}
|
|
99
|
+
else if (mode === 'years') {
|
|
100
|
+
label = `${date.format('YYYY')}`;
|
|
101
|
+
}
|
|
102
|
+
return label;
|
|
103
|
+
}
|
|
@@ -8,7 +8,7 @@ function useCalendarGridProps(state) {
|
|
|
8
8
|
state.setFocused(isFocused);
|
|
9
9
|
},
|
|
10
10
|
});
|
|
11
|
-
const gridProps = Object.assign(Object.assign({ role: 'grid', 'aria-label': state.mode === 'years'
|
|
11
|
+
const gridProps = Object.assign(Object.assign({ role: 'grid', 'aria-label': state.mode === 'years' || state.mode === 'quarters'
|
|
12
12
|
? `${state.startDate.year()} — ${state.endDate.year()}`
|
|
13
13
|
: state.focusedDate.format(state.mode === 'days' ? 'MMMM YYYY' : 'YYYY'), 'aria-disabled': state.disabled ? 'true' : undefined, 'aria-readonly': state.readOnly ? 'true' : undefined }, focusWithinProps), { onKeyDown: (e) => {
|
|
14
14
|
if (e.key === 'ArrowRight') {
|
|
@@ -42,12 +42,7 @@ function useCalendarGridProps(state) {
|
|
|
42
42
|
state.zoomIn();
|
|
43
43
|
}
|
|
44
44
|
else if (e.key === 'Enter' || e.key === ' ') {
|
|
45
|
-
|
|
46
|
-
state.selectFocusedDate();
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
state.setMode(state.mode === 'months' ? 'days' : 'months');
|
|
50
|
-
}
|
|
45
|
+
state.selectDate(state.focusedDate);
|
|
51
46
|
}
|
|
52
47
|
} });
|
|
53
48
|
return {
|
|
@@ -10,7 +10,7 @@ const i18n_1 = require("../i18n");
|
|
|
10
10
|
const buttonDisabledClassName = 'yc-button_disabled g-button_disabled';
|
|
11
11
|
// eslint-disable-next-line complexity
|
|
12
12
|
function useCalendarProps(props, state) {
|
|
13
|
-
const title = state.mode === 'years'
|
|
13
|
+
const title = state.mode === 'years' || state.mode === 'quarters'
|
|
14
14
|
? `${state.startDate.year()} — ${state.endDate.year()}`
|
|
15
15
|
: state.focusedDate.format(state.mode === 'days' ? 'MMMM YYYY' : 'YYYY');
|
|
16
16
|
const { focusWithinProps } = (0, uikit_1.useFocusWithin)({
|
|
@@ -18,7 +18,10 @@ function useCalendarProps(props, state) {
|
|
|
18
18
|
onBlurWithin: props.onBlur,
|
|
19
19
|
});
|
|
20
20
|
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);
|
|
21
|
-
const
|
|
21
|
+
const modeIndex = state.availableModes.indexOf(state.mode);
|
|
22
|
+
const isModeLast = modeIndex + 1 === state.availableModes.length;
|
|
23
|
+
const isNextModeLast = modeIndex + 2 === state.availableModes.length;
|
|
24
|
+
const modeDisabled = state.disabled || isModeLast;
|
|
22
25
|
const modeButtonProps = {
|
|
23
26
|
disabled: state.disabled,
|
|
24
27
|
// FIXME: do not use button class name
|
|
@@ -27,7 +30,7 @@ function useCalendarProps(props, state) {
|
|
|
27
30
|
? undefined
|
|
28
31
|
: () => {
|
|
29
32
|
state.zoomOut();
|
|
30
|
-
if (
|
|
33
|
+
if (isNextModeLast) {
|
|
31
34
|
state.setFocused(true);
|
|
32
35
|
}
|
|
33
36
|
},
|
|
@@ -5,14 +5,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.useCalendarState = void 0;
|
|
7
7
|
const react_1 = __importDefault(require("react"));
|
|
8
|
-
const date_utils_1 = require("@gravity-ui/date-utils");
|
|
9
8
|
const useControlledState_1 = require("../../hooks/useControlledState");
|
|
9
|
+
const dates_1 = require("../../utils/dates");
|
|
10
10
|
const utils_1 = require("../utils");
|
|
11
|
+
const defaultModes = {
|
|
12
|
+
days: true,
|
|
13
|
+
months: true,
|
|
14
|
+
quarters: false,
|
|
15
|
+
years: true,
|
|
16
|
+
};
|
|
11
17
|
function useCalendarState(props) {
|
|
12
|
-
const { disabled, readOnly, minValue, maxValue, timeZone } = props;
|
|
18
|
+
const { disabled, readOnly, minValue, maxValue, timeZone, modes = defaultModes } = props;
|
|
13
19
|
const [value, setValue] = (0, useControlledState_1.useControlledState)(props.value, props.defaultValue, props.onUpdate);
|
|
14
20
|
const [mode, setMode] = (0, useControlledState_1.useControlledState)(props.mode, props.defaultMode, props.onUpdateMode);
|
|
15
|
-
const
|
|
21
|
+
const availableModes = utils_1.calendarLayouts.filter((l) => modes[l]);
|
|
22
|
+
const minMode = availableModes[0] || 'days';
|
|
23
|
+
const currentMode = mode && availableModes.includes(mode) ? mode : minMode;
|
|
16
24
|
const focusedValue = react_1.default.useMemo(() => {
|
|
17
25
|
if (!props.focusedValue) {
|
|
18
26
|
return props.focusedValue;
|
|
@@ -20,17 +28,18 @@ function useCalendarState(props) {
|
|
|
20
28
|
return (0, utils_1.constrainValue)(props.focusedValue, minValue, maxValue);
|
|
21
29
|
}, [props.focusedValue, minValue, maxValue]);
|
|
22
30
|
const defaultFocusedValue = react_1.default.useMemo(() => {
|
|
23
|
-
const defaultValue = (props.defaultFocusedValue ? props.defaultFocusedValue : value) ||
|
|
31
|
+
const defaultValue = (props.defaultFocusedValue ? props.defaultFocusedValue : value) ||
|
|
32
|
+
(0, dates_1.createPlaceholderValue)({ timeZone }).startOf(minMode);
|
|
24
33
|
return (0, utils_1.constrainValue)(defaultValue, minValue, maxValue);
|
|
25
|
-
}, [maxValue, minValue, props.defaultFocusedValue, timeZone, value]);
|
|
34
|
+
}, [maxValue, minValue, props.defaultFocusedValue, timeZone, value, minMode]);
|
|
26
35
|
const [focusedDateInner, setFocusedDate] = (0, useControlledState_1.useControlledState)(focusedValue, defaultFocusedValue, props.onFocusUpdate);
|
|
27
|
-
const focusedDate = focusedDateInner !== null && focusedDateInner !== void 0 ? focusedDateInner : (0, utils_1.constrainValue)((0,
|
|
36
|
+
const focusedDate = focusedDateInner !== null && focusedDateInner !== void 0 ? focusedDateInner : (0, utils_1.constrainValue)((0, dates_1.createPlaceholderValue)({ timeZone }).startOf(minMode), minValue, maxValue);
|
|
28
37
|
if (isInvalid(focusedDate, minValue, maxValue)) {
|
|
29
38
|
// If the focused date was moved to an invalid value, it can't be focused, so constrain it.
|
|
30
39
|
setFocusedDate((0, utils_1.constrainValue)(focusedDate, minValue, maxValue));
|
|
31
40
|
}
|
|
32
41
|
function focusCell(date) {
|
|
33
|
-
setFocusedDate((0, utils_1.constrainValue)(date, minValue, maxValue));
|
|
42
|
+
setFocusedDate((0, utils_1.constrainValue)(date.startOf(currentMode), minValue, maxValue));
|
|
34
43
|
}
|
|
35
44
|
const [isFocused, setFocused] = react_1.default.useState(props.autoFocus || false);
|
|
36
45
|
const startDate = getStartDate(focusedDate, currentMode);
|
|
@@ -41,13 +50,21 @@ function useCalendarState(props) {
|
|
|
41
50
|
value,
|
|
42
51
|
setValue,
|
|
43
52
|
timeZone,
|
|
44
|
-
selectDate(date) {
|
|
45
|
-
if (!disabled
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
selectDate(date, force = false) {
|
|
54
|
+
if (!disabled) {
|
|
55
|
+
if (!readOnly && (force || this.mode === minMode)) {
|
|
56
|
+
const newValue = (0, utils_1.constrainValue)(date, minValue, maxValue);
|
|
57
|
+
if (this.isCellUnavailable(newValue)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
setValue(newValue);
|
|
61
|
+
if (force && currentMode !== minMode) {
|
|
62
|
+
setMode(minMode);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
this.zoomIn();
|
|
49
67
|
}
|
|
50
|
-
setValue((0, utils_1.constrainValue)(newValue, minValue, maxValue));
|
|
51
68
|
}
|
|
52
69
|
},
|
|
53
70
|
minValue,
|
|
@@ -60,41 +77,53 @@ function useCalendarState(props) {
|
|
|
60
77
|
setFocused(true);
|
|
61
78
|
},
|
|
62
79
|
focusNextCell() {
|
|
63
|
-
focusCell(focusedDate.add(
|
|
80
|
+
focusCell(focusedDate.add(1, this.mode));
|
|
64
81
|
},
|
|
65
82
|
focusPreviousCell() {
|
|
66
|
-
focusCell(focusedDate.subtract(
|
|
83
|
+
focusCell(focusedDate.subtract(1, this.mode));
|
|
67
84
|
},
|
|
68
85
|
focusNextRow() {
|
|
69
86
|
if (this.mode === 'days') {
|
|
70
87
|
focusCell(focusedDate.add(1, 'week'));
|
|
71
88
|
}
|
|
89
|
+
else if (this.mode === 'quarters') {
|
|
90
|
+
focusCell(focusedDate.add(1, 'years'));
|
|
91
|
+
}
|
|
72
92
|
else {
|
|
73
|
-
focusCell(focusedDate.add(
|
|
93
|
+
focusCell(focusedDate.add(3, this.mode));
|
|
74
94
|
}
|
|
75
95
|
},
|
|
76
96
|
focusPreviousRow() {
|
|
77
97
|
if (this.mode === 'days') {
|
|
78
98
|
focusCell(focusedDate.subtract(1, 'week'));
|
|
79
99
|
}
|
|
100
|
+
else if (this.mode === 'quarters') {
|
|
101
|
+
focusCell(focusedDate.subtract(1, 'years'));
|
|
102
|
+
}
|
|
80
103
|
else {
|
|
81
|
-
focusCell(focusedDate.subtract(
|
|
104
|
+
focusCell(focusedDate.subtract(3, this.mode));
|
|
82
105
|
}
|
|
83
106
|
},
|
|
84
107
|
focusNextPage(larger) {
|
|
85
108
|
if (this.mode === 'days') {
|
|
86
109
|
focusCell(focusedDate.add({ months: larger ? 12 : 1 }));
|
|
87
110
|
}
|
|
111
|
+
else if (this.mode === 'quarters') {
|
|
112
|
+
focusCell(focusedDate.add(4, 'years'));
|
|
113
|
+
}
|
|
88
114
|
else {
|
|
89
|
-
focusCell(focusedDate.add(
|
|
115
|
+
focusCell(focusedDate.add(12, this.mode));
|
|
90
116
|
}
|
|
91
117
|
},
|
|
92
118
|
focusPreviousPage(larger) {
|
|
93
119
|
if (this.mode === 'days') {
|
|
94
120
|
focusCell(focusedDate.subtract({ months: larger ? 12 : 1 }));
|
|
95
121
|
}
|
|
122
|
+
else if (this.mode === 'quarters') {
|
|
123
|
+
focusCell(focusedDate.subtract(4, 'years'));
|
|
124
|
+
}
|
|
96
125
|
else {
|
|
97
|
-
focusCell(focusedDate.subtract(
|
|
126
|
+
focusCell(focusedDate.subtract(12, this.mode));
|
|
98
127
|
}
|
|
99
128
|
},
|
|
100
129
|
focusSectionStart() {
|
|
@@ -104,13 +133,19 @@ function useCalendarState(props) {
|
|
|
104
133
|
focusCell(getEndDate(focusedDate, this.mode));
|
|
105
134
|
},
|
|
106
135
|
zoomIn() {
|
|
107
|
-
|
|
136
|
+
const index = availableModes.indexOf(this.mode) - 1;
|
|
137
|
+
if (index >= 0) {
|
|
138
|
+
this.setMode(availableModes[index]);
|
|
139
|
+
}
|
|
108
140
|
},
|
|
109
141
|
zoomOut() {
|
|
110
|
-
|
|
142
|
+
const index = availableModes.indexOf(this.mode) + 1;
|
|
143
|
+
if (index < availableModes.length) {
|
|
144
|
+
this.setMode(availableModes[index]);
|
|
145
|
+
}
|
|
111
146
|
},
|
|
112
147
|
selectFocusedDate() {
|
|
113
|
-
this.selectDate(focusedDate);
|
|
148
|
+
this.selectDate(focusedDate, true);
|
|
114
149
|
},
|
|
115
150
|
isFocused,
|
|
116
151
|
setFocused,
|
|
@@ -118,11 +153,11 @@ function useCalendarState(props) {
|
|
|
118
153
|
return isInvalid(date, this.minValue, this.maxValue, this.mode);
|
|
119
154
|
},
|
|
120
155
|
isPreviousPageInvalid() {
|
|
121
|
-
const prev = this.startDate.
|
|
156
|
+
const prev = this.startDate.subtract(1, 'day');
|
|
122
157
|
return this.isInvalid(prev);
|
|
123
158
|
},
|
|
124
159
|
isNextPageInvalid() {
|
|
125
|
-
const next = this.endDate.add(
|
|
160
|
+
const next = this.endDate.endOf(this.mode).add(1, 'day');
|
|
126
161
|
return this.isInvalid(next);
|
|
127
162
|
},
|
|
128
163
|
isSelected(date) {
|
|
@@ -131,7 +166,7 @@ function useCalendarState(props) {
|
|
|
131
166
|
!this.isCellDisabled(date));
|
|
132
167
|
},
|
|
133
168
|
isCellUnavailable(date) {
|
|
134
|
-
if (this.mode ===
|
|
169
|
+
if (this.mode === minMode) {
|
|
135
170
|
return Boolean(props.isDateUnavailable && props.isDateUnavailable(date));
|
|
136
171
|
}
|
|
137
172
|
else {
|
|
@@ -149,6 +184,7 @@ function useCalendarState(props) {
|
|
|
149
184
|
},
|
|
150
185
|
mode: currentMode,
|
|
151
186
|
setMode,
|
|
187
|
+
availableModes,
|
|
152
188
|
};
|
|
153
189
|
}
|
|
154
190
|
exports.useCalendarState = useCalendarState;
|
|
@@ -159,6 +195,10 @@ function getStartDate(date, mode) {
|
|
|
159
195
|
if (mode === 'months') {
|
|
160
196
|
return date.startOf('year');
|
|
161
197
|
}
|
|
198
|
+
if (mode === 'quarters') {
|
|
199
|
+
const year = Math.floor(date.year() / 4) * 4;
|
|
200
|
+
return date.startOf('year').set('year', year);
|
|
201
|
+
}
|
|
162
202
|
const year = Math.floor(date.year() / 12) * 12;
|
|
163
203
|
return date.startOf('year').set('year', year);
|
|
164
204
|
}
|
|
@@ -167,9 +207,12 @@ function getEndDate(date, mode) {
|
|
|
167
207
|
return date.endOf('month').startOf('day');
|
|
168
208
|
}
|
|
169
209
|
if (mode === 'months') {
|
|
170
|
-
return date.endOf('
|
|
210
|
+
return date.endOf('year').startOf('month');
|
|
171
211
|
}
|
|
172
212
|
const startDate = getStartDate(date, mode);
|
|
213
|
+
if (mode === 'quarters') {
|
|
214
|
+
return startDate.add(15, 'quarters');
|
|
215
|
+
}
|
|
173
216
|
return startDate.add({ [mode]: 11 });
|
|
174
217
|
}
|
|
175
218
|
function isInvalid(date, minValue, maxValue, mode = 'days') {
|
|
@@ -26,8 +26,8 @@ function useRangeCalendarState(props) {
|
|
|
26
26
|
const [anchorDate, setAnchorDateState] = react_1.default.useState();
|
|
27
27
|
const calendar = (0, useCalendarState_1.useCalendarState)(Object.assign(Object.assign({}, calendarProps), { value: (_a = value === null || value === void 0 ? void 0 : value.start) !== null && _a !== void 0 ? _a : null }));
|
|
28
28
|
const highlightedRange = anchorDate
|
|
29
|
-
? makeRange(anchorDate, calendar.focusedDate)
|
|
30
|
-
: (_b = (value && makeRange(value.start, value.end))) !== null && _b !== void 0 ? _b : undefined;
|
|
29
|
+
? makeRange(anchorDate, calendar.focusedDate, calendar.mode)
|
|
30
|
+
: (_b = (value && makeRange(value.start, value.end, calendar.mode))) !== null && _b !== void 0 ? _b : undefined;
|
|
31
31
|
const selectDate = (date) => {
|
|
32
32
|
if (props.disabled || props.readOnly) {
|
|
33
33
|
return;
|
|
@@ -37,7 +37,7 @@ function useRangeCalendarState(props) {
|
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
39
|
if (anchorDate) {
|
|
40
|
-
const range = makeRange(anchorDate, date);
|
|
40
|
+
const range = makeRange(anchorDate, date, calendar.mode);
|
|
41
41
|
setValue(range);
|
|
42
42
|
setAnchorDateState(undefined);
|
|
43
43
|
}
|
|
@@ -68,15 +68,15 @@ function useRangeCalendarState(props) {
|
|
|
68
68
|
} });
|
|
69
69
|
}
|
|
70
70
|
exports.useRangeCalendarState = useRangeCalendarState;
|
|
71
|
-
function makeRange(start, end) {
|
|
71
|
+
function makeRange(start, end, mode) {
|
|
72
72
|
if (start.isBefore(end)) {
|
|
73
73
|
return {
|
|
74
74
|
start,
|
|
75
|
-
end,
|
|
75
|
+
end: end.endOf(mode),
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
78
|
return {
|
|
79
79
|
start: end,
|
|
80
|
-
end: start,
|
|
80
|
+
end: start.endOf(mode),
|
|
81
81
|
};
|
|
82
82
|
}
|
|
@@ -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[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getWeekDays = exports.getDaysInPeriod = exports.constrainValue = void 0;
|
|
3
|
+
exports.calendarLayouts = exports.getWeekDays = exports.getDaysInPeriod = exports.constrainValue = void 0;
|
|
4
4
|
const date_utils_1 = require("@gravity-ui/date-utils");
|
|
5
5
|
function constrainValue(value, minValue, maxValue) {
|
|
6
6
|
if (minValue && value.isBefore(minValue)) {
|
|
@@ -20,6 +20,11 @@ function getDaysInPeriod(startDate, _endDate, mode) {
|
|
|
20
20
|
days.push(currentDate.add({ days: i }));
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
+
else if (mode === 'quarters') {
|
|
24
|
+
for (let i = 0; i < 16; i++) {
|
|
25
|
+
days.push(startDate.add(i, 'quarters'));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
23
28
|
else {
|
|
24
29
|
for (let i = 0; i < 12; i++) {
|
|
25
30
|
days.push(startDate.add({ [mode]: i }));
|
|
@@ -38,3 +43,4 @@ function getWeekDays() {
|
|
|
38
43
|
return weekDays;
|
|
39
44
|
}
|
|
40
45
|
exports.getWeekDays = getWeekDays;
|
|
46
|
+
exports.calendarLayouts = ['days', 'months', 'quarters', 'years'];
|
|
@@ -121,11 +121,11 @@ function useDateFieldProps(state, props) {
|
|
|
121
121
|
}
|
|
122
122
|
else if (e.key === 'Home') {
|
|
123
123
|
e.preventDefault();
|
|
124
|
-
state.
|
|
124
|
+
state.decrementToMin();
|
|
125
125
|
}
|
|
126
126
|
else if (e.key === 'End') {
|
|
127
127
|
e.preventDefault();
|
|
128
|
-
state.
|
|
128
|
+
state.incrementToMax();
|
|
129
129
|
}
|
|
130
130
|
else if (e.key === 'ArrowUp' && !e.altKey) {
|
|
131
131
|
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. */
|
|
@@ -7,6 +7,7 @@ exports.useDateFieldState = void 0;
|
|
|
7
7
|
const react_1 = __importDefault(require("react"));
|
|
8
8
|
const date_utils_1 = require("@gravity-ui/date-utils");
|
|
9
9
|
const useControlledState_1 = require("../../hooks/useControlledState");
|
|
10
|
+
const dates_1 = require("../../utils/dates");
|
|
10
11
|
const utils_1 = require("../utils");
|
|
11
12
|
const EDITABLE_SEGMENTS = {
|
|
12
13
|
year: true,
|
|
@@ -36,7 +37,7 @@ function useDateFieldState(props) {
|
|
|
36
37
|
var _a;
|
|
37
38
|
const [value, setDate] = (0, useControlledState_1.useControlledState)(props.value, props.defaultValue, props.onUpdate);
|
|
38
39
|
const [placeholderDate, setPlaceholderDate] = react_1.default.useState(() => {
|
|
39
|
-
return (0,
|
|
40
|
+
return (0, dates_1.createPlaceholderValue)({
|
|
40
41
|
placeholderValue: props.placeholderValue,
|
|
41
42
|
timeZone: props.timeZone,
|
|
42
43
|
});
|
|
@@ -60,7 +61,7 @@ function useDateFieldState(props) {
|
|
|
60
61
|
Object.keys(validSegments).length === Object.keys(allSegments).length) {
|
|
61
62
|
validSegments = {};
|
|
62
63
|
setValidSegments(validSegments);
|
|
63
|
-
setPlaceholderDate((0,
|
|
64
|
+
setPlaceholderDate((0, dates_1.createPlaceholderValue)({ timeZone: props.timeZone }));
|
|
64
65
|
}
|
|
65
66
|
const displayValue = value &&
|
|
66
67
|
(0, date_utils_1.isValid)(value) &&
|
|
@@ -130,7 +131,7 @@ function useDateFieldState(props) {
|
|
|
130
131
|
}
|
|
131
132
|
const enteredKeys = react_1.default.useRef('');
|
|
132
133
|
const validationState = props.validationState ||
|
|
133
|
-
((0,
|
|
134
|
+
((0, dates_1.isInvalid)(value, props.minValue, props.maxValue) ? 'invalid' : undefined) ||
|
|
134
135
|
(value && ((_a = props.isDateUnavailable) === null || _a === void 0 ? void 0 : _a.call(props, value)) ? 'invalid' : undefined);
|
|
135
136
|
return {
|
|
136
137
|
value: value !== null && value !== void 0 ? value : null,
|
|
@@ -225,6 +226,32 @@ function useDateFieldState(props) {
|
|
|
225
226
|
adjustSection(sectionIndex, -(PAGE_STEP[this.sections[sectionIndex].type] || 1));
|
|
226
227
|
}
|
|
227
228
|
},
|
|
229
|
+
incrementToMax() {
|
|
230
|
+
if (this.readOnly || this.disabled) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
enteredKeys.current = '';
|
|
234
|
+
const sectionIndex = getCurrentEditableSectionIndex(this.sections, selectedSections);
|
|
235
|
+
if (sectionIndex !== -1) {
|
|
236
|
+
const section = this.sections[sectionIndex];
|
|
237
|
+
if (typeof section.maxValue === 'number') {
|
|
238
|
+
setSection(section, section.maxValue);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
decrementToMin() {
|
|
243
|
+
if (this.readOnly || this.disabled) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
enteredKeys.current = '';
|
|
247
|
+
const sectionIndex = getCurrentEditableSectionIndex(this.sections, selectedSections);
|
|
248
|
+
if (sectionIndex !== -1) {
|
|
249
|
+
const section = this.sections[sectionIndex];
|
|
250
|
+
if (typeof section.minValue === 'number') {
|
|
251
|
+
setSection(section, section.minValue);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
},
|
|
228
255
|
clearSection() {
|
|
229
256
|
if (this.readOnly || this.disabled) {
|
|
230
257
|
return;
|
|
@@ -241,7 +268,7 @@ function useDateFieldState(props) {
|
|
|
241
268
|
const section = this.sections[sectionIndex];
|
|
242
269
|
delete validSegments[section.type];
|
|
243
270
|
setValidSegments(Object.assign({}, validSegments));
|
|
244
|
-
const placeholder = (0,
|
|
271
|
+
const placeholder = (0, dates_1.createPlaceholderValue)({
|
|
245
272
|
placeholderValue: props.placeholderValue,
|
|
246
273
|
timeZone: props.timeZone,
|
|
247
274
|
});
|
|
@@ -276,7 +303,7 @@ function useDateFieldState(props) {
|
|
|
276
303
|
if (value !== null) {
|
|
277
304
|
setDate(null);
|
|
278
305
|
}
|
|
279
|
-
setValue((0,
|
|
306
|
+
setValue((0, dates_1.createPlaceholderValue)({
|
|
280
307
|
placeholderValue: props.placeholderValue,
|
|
281
308
|
timeZone: props.timeZone,
|
|
282
309
|
}));
|
|
@@ -394,7 +421,7 @@ function useDateFieldState(props) {
|
|
|
394
421
|
if ((0, date_utils_1.isValid)(date)) {
|
|
395
422
|
if (props.timeZone && !isDateStringWithTimeZone(str)) {
|
|
396
423
|
const time = parseDate({ input: str, format });
|
|
397
|
-
date = (0,
|
|
424
|
+
date = (0, dates_1.mergeDateTime)(date, time);
|
|
398
425
|
}
|
|
399
426
|
setDate(date);
|
|
400
427
|
return true;
|
|
@@ -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 {};
|