@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.
- 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 +16 -11
- 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 +23 -16
- package/dist/cjs/components/DateField/hooks/useDateFieldState.d.ts +2 -0
- package/dist/cjs/components/DateField/hooks/useDateFieldState.js +49 -166
- package/dist/cjs/components/DateField/types.d.ts +1 -1
- package/dist/cjs/components/DateField/utils.d.ts +12 -9
- package/dist/cjs/components/DateField/utils.js +168 -25
- package/dist/cjs/components/DatePicker/DatePicker.css +6 -0
- package/dist/cjs/components/DatePicker/DatePicker.d.ts +4 -1
- package/dist/cjs/components/DatePicker/DatePicker.js +6 -33
- package/dist/cjs/components/DatePicker/MobileCalendar.js +4 -4
- package/dist/cjs/components/DatePicker/hooks/useDatePickerProps.d.ts +20 -0
- package/dist/cjs/components/DatePicker/hooks/useDatePickerProps.js +134 -0
- package/dist/cjs/components/DatePicker/hooks/useDatePickerState.d.ts +4 -5
- package/dist/cjs/components/DatePicker/hooks/useDatePickerState.js +18 -8
- package/dist/cjs/components/DatePicker/index.d.ts +1 -0
- package/dist/cjs/components/DatePicker/index.js +1 -0
- package/dist/cjs/components/RelativeDatePicker/RelativeDatePicker.css +1 -0
- 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 +35 -27
- 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 +16 -11
- 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 +23 -16
- package/dist/esm/components/DateField/hooks/useDateFieldState.d.ts +2 -0
- package/dist/esm/components/DateField/hooks/useDateFieldState.js +41 -158
- package/dist/esm/components/DateField/types.d.ts +1 -1
- package/dist/esm/components/DateField/utils.d.ts +12 -9
- package/dist/esm/components/DateField/utils.js +162 -21
- package/dist/esm/components/DatePicker/DatePicker.css +6 -0
- package/dist/esm/components/DatePicker/DatePicker.d.ts +4 -1
- package/dist/esm/components/DatePicker/DatePicker.js +8 -35
- package/dist/esm/components/DatePicker/MobileCalendar.js +1 -1
- package/dist/esm/components/DatePicker/hooks/useDatePickerProps.d.ts +20 -0
- package/dist/esm/components/DatePicker/hooks/useDatePickerProps.js +127 -0
- package/dist/esm/components/DatePicker/hooks/useDatePickerState.d.ts +4 -5
- package/dist/esm/components/DatePicker/hooks/useDatePickerState.js +17 -7
- package/dist/esm/components/DatePicker/index.d.ts +1 -0
- package/dist/esm/components/DatePicker/index.js +1 -0
- package/dist/esm/components/RelativeDatePicker/RelativeDatePicker.css +1 -0
- 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 +35 -27
- package/dist/esm/components/utils/dates.d.ts +9 -0
- package/dist/esm/components/utils/dates.js +22 -0
- package/package.json +8 -7
- package/dist/cjs/components/DatePicker/DesktopCalendar.d.ts +0 -15
- package/dist/cjs/components/DatePicker/DesktopCalendar.js +0 -56
- package/dist/esm/components/DatePicker/DesktopCalendar.d.ts +0 -15
- package/dist/esm/components/DatePicker/DesktopCalendar.js +0 -48
|
@@ -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,10 +26,18 @@ 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;
|
|
31
|
-
const
|
|
32
|
-
|
|
29
|
+
? makeRange(anchorDate, calendar.focusedDate, calendar.mode)
|
|
30
|
+
: (_b = (value && makeRange(value.start, value.end, calendar.mode))) !== null && _b !== void 0 ? _b : undefined;
|
|
31
|
+
const minMode = calendar.availableModes[0];
|
|
32
|
+
const selectDate = (date, force = false) => {
|
|
33
|
+
if (props.disabled) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (!force && calendar.mode !== minMode) {
|
|
37
|
+
calendar.zoomIn();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (props.readOnly) {
|
|
33
41
|
return;
|
|
34
42
|
}
|
|
35
43
|
date = (0, utils_1.constrainValue)(date, props.minValue, props.maxValue);
|
|
@@ -37,7 +45,7 @@ function useRangeCalendarState(props) {
|
|
|
37
45
|
return;
|
|
38
46
|
}
|
|
39
47
|
if (anchorDate) {
|
|
40
|
-
const range = makeRange(anchorDate, date);
|
|
48
|
+
const range = makeRange(anchorDate, date, calendar.mode);
|
|
41
49
|
setValue(range);
|
|
42
50
|
setAnchorDateState(undefined);
|
|
43
51
|
}
|
|
@@ -49,9 +57,6 @@ function useRangeCalendarState(props) {
|
|
|
49
57
|
setValue,
|
|
50
58
|
selectDate,
|
|
51
59
|
anchorDate, setAnchorDate: setAnchorDateState, highlightedRange,
|
|
52
|
-
selectFocusedDate() {
|
|
53
|
-
selectDate(calendar.focusedDate);
|
|
54
|
-
},
|
|
55
60
|
isSelected(date) {
|
|
56
61
|
if (!highlightedRange) {
|
|
57
62
|
return false;
|
|
@@ -68,15 +73,15 @@ function useRangeCalendarState(props) {
|
|
|
68
73
|
} });
|
|
69
74
|
}
|
|
70
75
|
exports.useRangeCalendarState = useRangeCalendarState;
|
|
71
|
-
function makeRange(start, end) {
|
|
76
|
+
function makeRange(start, end, mode) {
|
|
72
77
|
if (start.isBefore(end)) {
|
|
73
78
|
return {
|
|
74
79
|
start,
|
|
75
|
-
end,
|
|
80
|
+
end: end.endOf(mode),
|
|
76
81
|
};
|
|
77
82
|
}
|
|
78
83
|
return {
|
|
79
84
|
start: end,
|
|
80
|
-
end: start,
|
|
85
|
+
end: start.endOf(mode),
|
|
81
86
|
};
|
|
82
87
|
}
|
|
@@ -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'];
|
|
@@ -86,12 +86,16 @@ function useDateFieldProps(state, props) {
|
|
|
86
86
|
if (state.selectedSectionIndexes !== null) {
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
89
|
-
const input =
|
|
89
|
+
const input = e.target;
|
|
90
|
+
const isAutofocus = !inputRef.current;
|
|
90
91
|
setTimeout(() => {
|
|
91
92
|
if (!input || input !== inputRef.current) {
|
|
92
93
|
return;
|
|
93
94
|
}
|
|
94
|
-
if (
|
|
95
|
+
if (isAutofocus) {
|
|
96
|
+
state.focusSectionInPosition(0);
|
|
97
|
+
}
|
|
98
|
+
else if (
|
|
95
99
|
// avoid selecting all sections when focusing empty field without value
|
|
96
100
|
input.value.length &&
|
|
97
101
|
Number(input.selectionEnd) - Number(input.selectionStart) ===
|
|
@@ -121,11 +125,11 @@ function useDateFieldProps(state, props) {
|
|
|
121
125
|
}
|
|
122
126
|
else if (e.key === 'Home') {
|
|
123
127
|
e.preventDefault();
|
|
124
|
-
state.
|
|
128
|
+
state.decrementToMin();
|
|
125
129
|
}
|
|
126
130
|
else if (e.key === 'End') {
|
|
127
131
|
e.preventDefault();
|
|
128
|
-
state.
|
|
132
|
+
state.incrementToMax();
|
|
129
133
|
}
|
|
130
134
|
else if (e.key === 'ArrowUp' && !e.altKey) {
|
|
131
135
|
e.preventDefault();
|
|
@@ -169,32 +173,35 @@ function useDateFieldProps(state, props) {
|
|
|
169
173
|
onBeforeInput(e) {
|
|
170
174
|
e.preventDefault();
|
|
171
175
|
// @ts-expect-error
|
|
172
|
-
const key = e.
|
|
173
|
-
|
|
174
|
-
if (key != null) {
|
|
176
|
+
const key = e.data;
|
|
177
|
+
if (key !== undefined && key !== null) {
|
|
175
178
|
state.onInput(key);
|
|
176
179
|
}
|
|
177
180
|
},
|
|
178
181
|
onPaste(e) {
|
|
182
|
+
e.preventDefault();
|
|
179
183
|
if (state.readOnly) {
|
|
180
|
-
e.preventDefault();
|
|
181
184
|
return;
|
|
182
185
|
}
|
|
183
186
|
const pastedValue = e.clipboardData.getData('text');
|
|
184
|
-
if (state.
|
|
185
|
-
e.preventDefault();
|
|
186
|
-
}
|
|
187
|
-
else if (state.selectedSectionIndexes &&
|
|
187
|
+
if (state.selectedSectionIndexes &&
|
|
188
188
|
state.selectedSectionIndexes.startIndex ===
|
|
189
189
|
state.selectedSectionIndexes.endIndex) {
|
|
190
190
|
const activeSection = state.sections[state.selectedSectionIndexes.startIndex];
|
|
191
|
+
const digitsOnly = /^\d+$/.test(pastedValue);
|
|
192
|
+
const lettersOnly = /^[a-zA-Z]+$/.test(pastedValue);
|
|
191
193
|
const isValidValue = Boolean(activeSection) &&
|
|
192
|
-
((activeSection.contentType === 'digit' &&
|
|
193
|
-
activeSection.contentType === 'letter');
|
|
194
|
-
if (
|
|
195
|
-
|
|
194
|
+
((activeSection.contentType === 'digit' && digitsOnly) ||
|
|
195
|
+
(activeSection.contentType === 'letter' && lettersOnly));
|
|
196
|
+
if (isValidValue) {
|
|
197
|
+
state.onInput(pastedValue);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (digitsOnly || lettersOnly) {
|
|
201
|
+
return;
|
|
196
202
|
}
|
|
197
203
|
}
|
|
204
|
+
state.setValueFromString(pastedValue);
|
|
198
205
|
},
|
|
199
206
|
},
|
|
200
207
|
},
|
|
@@ -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. */
|