@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
|
@@ -6,7 +6,7 @@ const date_utils_1 = require("@gravity-ui/date-utils");
|
|
|
6
6
|
const icons_1 = require("@gravity-ui/icons");
|
|
7
7
|
const uikit_1 = require("@gravity-ui/uikit");
|
|
8
8
|
const cn_1 = require("../../utils/cn");
|
|
9
|
-
const
|
|
9
|
+
const dates_1 = require("../utils/dates");
|
|
10
10
|
const getButtonSizeForInput_1 = require("../utils/getButtonSizeForInput");
|
|
11
11
|
require("./MobileCalendar.css");
|
|
12
12
|
const b = (0, cn_1.block)('mobile-calendar');
|
|
@@ -31,15 +31,15 @@ function MobileCalendar({ props, state }) {
|
|
|
31
31
|
format: getDateFormat(type),
|
|
32
32
|
timeZone: props.timeZone,
|
|
33
33
|
})
|
|
34
|
-
: (0,
|
|
34
|
+
: (0, dates_1.createPlaceholderValue)({
|
|
35
35
|
placeholderValue: props.placeholderValue,
|
|
36
36
|
timeZone: props.timeZone,
|
|
37
37
|
});
|
|
38
38
|
if (state.hasTime) {
|
|
39
|
-
newDate = (0,
|
|
39
|
+
newDate = (0, dates_1.mergeDateTime)(newDate, localDate);
|
|
40
40
|
}
|
|
41
41
|
else if (state.dateValue) {
|
|
42
|
-
newDate = (0,
|
|
42
|
+
newDate = (0, dates_1.mergeDateTime)(newDate, state.dateValue);
|
|
43
43
|
}
|
|
44
44
|
else {
|
|
45
45
|
newDate = newDate.set('hours', 0).set('minutes', 0).set('seconds', 0);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ButtonProps, PopupProps, TextInputProps } from '@gravity-ui/uikit';
|
|
3
|
+
import type { Calendar } from '../../Calendar';
|
|
4
|
+
import type { DateFieldProps } from '../../DateField';
|
|
5
|
+
import type { DatePickerProps } from '../DatePicker';
|
|
6
|
+
import type { DatePickerState } from './useDatePickerState';
|
|
7
|
+
interface InnerRelativeDatePickerProps {
|
|
8
|
+
groupProps: React.HTMLAttributes<unknown> & {
|
|
9
|
+
ref: React.Ref<any>;
|
|
10
|
+
};
|
|
11
|
+
fieldProps: TextInputProps;
|
|
12
|
+
calendarButtonProps: ButtonProps & {
|
|
13
|
+
ref: React.Ref<HTMLButtonElement>;
|
|
14
|
+
};
|
|
15
|
+
popupProps: PopupProps;
|
|
16
|
+
calendarProps: React.ComponentProps<typeof Calendar>;
|
|
17
|
+
timeInputProps: DateFieldProps;
|
|
18
|
+
}
|
|
19
|
+
export declare function useDatePickerProps(state: DatePickerState, { onFocus, onBlur, ...props }: DatePickerProps): InnerRelativeDatePickerProps;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.useDatePickerProps = void 0;
|
|
18
|
+
const react_1 = __importDefault(require("react"));
|
|
19
|
+
const uikit_1 = require("@gravity-ui/uikit");
|
|
20
|
+
const DateField_1 = require("../../DateField");
|
|
21
|
+
const getButtonSizeForInput_1 = require("../../utils/getButtonSizeForInput");
|
|
22
|
+
const mergeProps_1 = require("../../utils/mergeProps");
|
|
23
|
+
const i18n_1 = require("../i18n");
|
|
24
|
+
function useDatePickerProps(state, _a) {
|
|
25
|
+
var _b;
|
|
26
|
+
var { onFocus, onBlur } = _a, props = __rest(_a, ["onFocus", "onBlur"]);
|
|
27
|
+
const [isActive, setActive] = react_1.default.useState(false);
|
|
28
|
+
const { focusWithinProps } = (0, uikit_1.useFocusWithin)({
|
|
29
|
+
onFocusWithin: onFocus,
|
|
30
|
+
onBlurWithin: onBlur,
|
|
31
|
+
onFocusWithinChange(isFocusWithin) {
|
|
32
|
+
setActive(isFocusWithin);
|
|
33
|
+
if (!isFocusWithin) {
|
|
34
|
+
state.setOpen(false);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
const { inputProps } = (0, DateField_1.useDateFieldProps)(state.dateFieldState, props);
|
|
39
|
+
let error;
|
|
40
|
+
let validationState = props.validationState;
|
|
41
|
+
if (validationState) {
|
|
42
|
+
error = validationState === 'invalid' ? props.errorMessage || true : undefined;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
validationState = state.dateFieldState.validationState;
|
|
46
|
+
error = validationState === 'invalid';
|
|
47
|
+
}
|
|
48
|
+
const inputRef = react_1.default.useRef(null);
|
|
49
|
+
const handleRef = (0, uikit_1.useForkRef)(inputRef, inputProps.controlRef);
|
|
50
|
+
const calendarRef = react_1.default.useRef(null);
|
|
51
|
+
const calendarButtonRef = react_1.default.useRef(null);
|
|
52
|
+
const groupRef = react_1.default.useRef(null);
|
|
53
|
+
function focusInput() {
|
|
54
|
+
setTimeout(() => {
|
|
55
|
+
var _a;
|
|
56
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
groupProps: Object.assign(Object.assign({ ref: groupRef, tabIndex: -1, role: 'group' }, focusWithinProps), { style: props.style, 'aria-disabled': state.disabled || undefined, onKeyDown: (e) => {
|
|
61
|
+
if (e.altKey && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {
|
|
62
|
+
e.preventDefault();
|
|
63
|
+
e.stopPropagation();
|
|
64
|
+
state.setOpen(true);
|
|
65
|
+
}
|
|
66
|
+
} }),
|
|
67
|
+
fieldProps: (0, mergeProps_1.mergeProps)(inputProps, state.dateFieldState.isEmpty && !isActive && props.placeholder
|
|
68
|
+
? { value: '' }
|
|
69
|
+
: undefined, { controlRef: handleRef, error }),
|
|
70
|
+
calendarButtonProps: {
|
|
71
|
+
ref: calendarButtonRef,
|
|
72
|
+
size: (0, getButtonSizeForInput_1.getButtonSizeForInput)(props.size),
|
|
73
|
+
disabled: state.disabled,
|
|
74
|
+
extraProps: {
|
|
75
|
+
'aria-label': (0, i18n_1.i18n)('Calendar'),
|
|
76
|
+
'aria-haspopup': 'dialog',
|
|
77
|
+
'aria-expanded': state.isOpen,
|
|
78
|
+
},
|
|
79
|
+
view: 'flat-secondary',
|
|
80
|
+
onClick: () => {
|
|
81
|
+
setActive(true);
|
|
82
|
+
state.setOpen(!state.isOpen);
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
popupProps: {
|
|
86
|
+
open: state.isOpen,
|
|
87
|
+
onEscapeKeyDown: () => {
|
|
88
|
+
state.setOpen(false);
|
|
89
|
+
focusInput();
|
|
90
|
+
},
|
|
91
|
+
onOutsideClick: (e) => {
|
|
92
|
+
var _a;
|
|
93
|
+
if (e.target !== calendarButtonRef.current) {
|
|
94
|
+
state.setOpen(false);
|
|
95
|
+
}
|
|
96
|
+
if (e.target && ((_a = groupRef.current) === null || _a === void 0 ? void 0 : _a.contains(e.target))) {
|
|
97
|
+
focusInput();
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
// @ts-expect-error focusTrap in popup was introduced in a newer version of uikit
|
|
101
|
+
focusTrap: true,
|
|
102
|
+
},
|
|
103
|
+
calendarProps: {
|
|
104
|
+
ref: calendarRef,
|
|
105
|
+
autoFocus: true,
|
|
106
|
+
size: props.size === 's' ? 'm' : props.size,
|
|
107
|
+
disabled: props.disabled,
|
|
108
|
+
readOnly: props.readOnly,
|
|
109
|
+
onUpdate: (d) => {
|
|
110
|
+
state.setDateValue(d);
|
|
111
|
+
if (!state.hasTime) {
|
|
112
|
+
focusInput();
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
defaultFocusedValue: (_b = state.dateValue) !== null && _b !== void 0 ? _b : undefined,
|
|
116
|
+
value: state.dateValue,
|
|
117
|
+
minValue: props.minValue,
|
|
118
|
+
maxValue: props.maxValue,
|
|
119
|
+
isDateUnavailable: props.isDateUnavailable,
|
|
120
|
+
timeZone: props.timeZone,
|
|
121
|
+
},
|
|
122
|
+
timeInputProps: {
|
|
123
|
+
value: state.timeValue,
|
|
124
|
+
onUpdate: state.setTimeValue,
|
|
125
|
+
format: state.timeFormat,
|
|
126
|
+
readOnly: state.readOnly,
|
|
127
|
+
disabled: state.disabled,
|
|
128
|
+
timeZone: props.timeZone,
|
|
129
|
+
hasClear: props.hasClear,
|
|
130
|
+
size: props.size,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
exports.useDatePickerProps = useDatePickerProps;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DateTime } from '@gravity-ui/date-utils';
|
|
2
|
-
import type {
|
|
2
|
+
import type { DateFieldState } from '../../DateField';
|
|
3
|
+
import type { DateFieldBase } from '../../types';
|
|
3
4
|
export type Granularity = 'day' | 'hour' | 'minute' | 'second';
|
|
4
5
|
export interface DatePickerState {
|
|
5
6
|
/** The currently selected date. */
|
|
@@ -36,10 +37,8 @@ export interface DatePickerState {
|
|
|
36
37
|
isOpen: boolean;
|
|
37
38
|
/** Sets whether the calendar popover is open. */
|
|
38
39
|
setOpen(isOpen: boolean): void;
|
|
40
|
+
dateFieldState: DateFieldState;
|
|
39
41
|
}
|
|
40
|
-
export interface DatePickerStateOptions extends
|
|
41
|
-
placeholderValue?: DateTime;
|
|
42
|
-
timeZone?: string;
|
|
43
|
-
format?: string;
|
|
42
|
+
export interface DatePickerStateOptions extends DateFieldBase {
|
|
44
43
|
}
|
|
45
44
|
export declare function useDatePickerState(props: DatePickerStateOptions): DatePickerState;
|
|
@@ -5,8 +5,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.useDatePickerState = void 0;
|
|
7
7
|
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const DateField_1 = require("../../DateField");
|
|
8
9
|
const utils_1 = require("../../DateField/utils");
|
|
9
10
|
const useControlledState_1 = require("../../hooks/useControlledState");
|
|
11
|
+
const dates_1 = require("../../utils/dates");
|
|
10
12
|
function useDatePickerState(props) {
|
|
11
13
|
const { disabled, readOnly } = props;
|
|
12
14
|
const [isOpen, setOpen] = react_1.default.useState(false);
|
|
@@ -44,7 +46,7 @@ function useDatePickerState(props) {
|
|
|
44
46
|
if (disabled || readOnly) {
|
|
45
47
|
return;
|
|
46
48
|
}
|
|
47
|
-
setValue(mergeDateTime(date, time));
|
|
49
|
+
setValue((0, dates_1.mergeDateTime)(date, time));
|
|
48
50
|
setSelectedDate(null);
|
|
49
51
|
setSelectedTime(null);
|
|
50
52
|
};
|
|
@@ -80,6 +82,19 @@ function useDatePickerState(props) {
|
|
|
80
82
|
setSelectedTime(newValue);
|
|
81
83
|
}
|
|
82
84
|
};
|
|
85
|
+
const dateFieldState = (0, DateField_1.useDateFieldState)({
|
|
86
|
+
value: value !== null && value !== void 0 ? value : null,
|
|
87
|
+
onUpdate: setValue,
|
|
88
|
+
disabled,
|
|
89
|
+
readOnly,
|
|
90
|
+
validationState: props.validationState,
|
|
91
|
+
minValue: props.minValue,
|
|
92
|
+
maxValue: props.maxValue,
|
|
93
|
+
isDateUnavailable: props.isDateUnavailable,
|
|
94
|
+
format,
|
|
95
|
+
placeholderValue: props.placeholderValue,
|
|
96
|
+
timeZone: props.timeZone,
|
|
97
|
+
});
|
|
83
98
|
return {
|
|
84
99
|
value: value !== null && value !== void 0 ? value : null,
|
|
85
100
|
setValue,
|
|
@@ -100,15 +115,10 @@ function useDatePickerState(props) {
|
|
|
100
115
|
}
|
|
101
116
|
setOpen(newIsOpen);
|
|
102
117
|
},
|
|
118
|
+
dateFieldState,
|
|
103
119
|
};
|
|
104
120
|
}
|
|
105
121
|
exports.useDatePickerState = useDatePickerState;
|
|
106
|
-
function mergeDateTime(date, time) {
|
|
107
|
-
return date
|
|
108
|
-
.set('hours', time.hour())
|
|
109
|
-
.set('minutes', time.minute())
|
|
110
|
-
.set('seconds', time.second());
|
|
111
|
-
}
|
|
112
122
|
function getPlaceholderTime(placeholderValue, timeZone) {
|
|
113
|
-
return (0,
|
|
123
|
+
return (0, dates_1.createPlaceholderValue)({ placeholderValue, timeZone });
|
|
114
124
|
}
|
|
@@ -16,3 +16,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./DatePicker"), exports);
|
|
18
18
|
__exportStar(require("./hooks/useDatePickerState"), exports);
|
|
19
|
+
__exportStar(require("./hooks/useDatePickerProps"), exports);
|
|
@@ -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;
|
|
@@ -27,6 +27,6 @@ function RelativeDatePicker(props) {
|
|
|
27
27
|
readOnly: props.readOnly,
|
|
28
28
|
placeholderValue: props.placeholderValue,
|
|
29
29
|
timeZone: props.timeZone,
|
|
30
|
-
} })), (0, jsx_runtime_1.jsx)(uikit_1.TextInput, Object.assign({}, fieldProps, { leftContent: (0, jsx_runtime_1.jsx)(uikit_1.Button, Object.assign({}, modeSwitcherProps, { children: (0, jsx_runtime_1.jsx)(uikit_1.Icon, { data: icons_1.Function }) })), rightContent: (0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [!isMobile && ((0, jsx_runtime_1.jsx)(uikit_1.Button, Object.assign({}, calendarButtonProps, { children: (0, jsx_runtime_1.jsx)(uikit_1.Icon, { data: icons_1.Calendar }) }))), isMobile && state.mode === 'absolute' && ((0, jsx_runtime_1.jsx)(MobileCalendar_1.MobileCalendarIcon, { state: state.datePickerState, props: { size: props.size } }))] }) })), !isMobile && ((0, jsx_runtime_1.jsx)(uikit_1.Popup, Object.assign({}, popupProps, { anchorRef: anchorRef, children: (0, jsx_runtime_1.jsxs)("div", { className: b('popup-content'), children: [(0, jsx_runtime_1.jsx)(Calendar_1.Calendar, Object.assign({}, calendarProps)), state.datePickerState.hasTime && ((0, jsx_runtime_1.jsx)("div", { className: b('time-field-wrapper'), children: (0, jsx_runtime_1.jsx)(DateField_1.DateField, Object.assign({}, timeInputProps)) }))] }) })))] })));
|
|
30
|
+
} })), (0, jsx_runtime_1.jsx)(uikit_1.TextInput, Object.assign({}, fieldProps, { leftContent: (0, jsx_runtime_1.jsx)(uikit_1.Button, Object.assign({}, modeSwitcherProps, { children: (0, jsx_runtime_1.jsx)(uikit_1.Icon, { data: icons_1.Function }) })), rightContent: (0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [!isMobile && ((0, jsx_runtime_1.jsx)(uikit_1.Button, Object.assign({}, calendarButtonProps, { children: (0, jsx_runtime_1.jsx)(uikit_1.Icon, { data: icons_1.Calendar }) }))), isMobile && state.mode === 'absolute' && ((0, jsx_runtime_1.jsx)(MobileCalendar_1.MobileCalendarIcon, { state: state.datePickerState, props: { size: props.size } }))] }) })), !isMobile && ((0, jsx_runtime_1.jsx)(uikit_1.Popup, Object.assign({}, popupProps, { anchorRef: anchorRef, children: (0, jsx_runtime_1.jsxs)("div", { className: b('popup-content'), children: [typeof props.children === 'function' ? (props.children(calendarProps)) : ((0, jsx_runtime_1.jsx)(Calendar_1.Calendar, Object.assign({}, calendarProps))), state.datePickerState.hasTime && ((0, jsx_runtime_1.jsx)("div", { className: b('time-field-wrapper'), children: (0, jsx_runtime_1.jsx)(DateField_1.DateField, Object.assign({}, timeInputProps)) }))] }) })))] })));
|
|
31
31
|
}
|
|
32
32
|
exports.RelativeDatePicker = RelativeDatePicker;
|
|
@@ -42,7 +42,7 @@ function useRelativeDatePickerProps(state, _a) {
|
|
|
42
42
|
onBlurWithin: onBlur,
|
|
43
43
|
onFocusWithinChange(isFocusWithin) {
|
|
44
44
|
if (!isFocusWithin) {
|
|
45
|
-
state.setActive(
|
|
45
|
+
state.setActive(false);
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
48
|
});
|
|
@@ -50,16 +50,12 @@ function useRelativeDatePickerProps(state, _a) {
|
|
|
50
50
|
if (!state.isActive && isOpen) {
|
|
51
51
|
setOpen(false);
|
|
52
52
|
}
|
|
53
|
-
const [prevActive, setPrevActive] = react_1.default.useState(state.isActive);
|
|
54
|
-
if (prevActive !== state.isActive) {
|
|
55
|
-
setPrevActive(state.isActive);
|
|
56
|
-
if (state.isActive && !isOpen) {
|
|
57
|
-
setOpen(true);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
53
|
const commonInputProps = {
|
|
61
54
|
onFocus: () => {
|
|
62
|
-
state.
|
|
55
|
+
if (!state.isActive) {
|
|
56
|
+
state.setActive(true);
|
|
57
|
+
setOpen(true);
|
|
58
|
+
}
|
|
63
59
|
},
|
|
64
60
|
};
|
|
65
61
|
const { inputProps } = (0, DateField_1.useDateFieldProps)(dateFieldState, Object.assign(Object.assign({}, props), { value: undefined, defaultValue: undefined, onUpdate: undefined }));
|
|
@@ -68,6 +64,7 @@ function useRelativeDatePickerProps(state, _a) {
|
|
|
68
64
|
value: relativeDateState.text,
|
|
69
65
|
onUpdate: relativeDateState.setText,
|
|
70
66
|
hasClear: props.hasClear && !relativeDateState.readOnly,
|
|
67
|
+
placeholder: props.placeholder,
|
|
71
68
|
size: props.size,
|
|
72
69
|
};
|
|
73
70
|
let error;
|
|
@@ -82,13 +79,33 @@ function useRelativeDatePickerProps(state, _a) {
|
|
|
82
79
|
: dateFieldState.validationState;
|
|
83
80
|
error = validationState === 'invalid';
|
|
84
81
|
}
|
|
85
|
-
const wasActiveBeforeClickRef = react_1.default.useRef(state.isActive);
|
|
86
82
|
const inputRef = react_1.default.useRef(null);
|
|
87
83
|
const handleRef = (0, uikit_1.useForkRef)(inputRef, mode === 'relative' ? relativeDateProps.controlRef : inputProps.controlRef);
|
|
88
84
|
const calendarRef = react_1.default.useRef(null);
|
|
85
|
+
function focusCalendar() {
|
|
86
|
+
setTimeout(() => {
|
|
87
|
+
var _a;
|
|
88
|
+
(_a = calendarRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function focusInput() {
|
|
92
|
+
setTimeout(() => {
|
|
93
|
+
var _a;
|
|
94
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
95
|
+
});
|
|
96
|
+
}
|
|
89
97
|
return {
|
|
90
|
-
groupProps: Object.assign({ role: 'group' }, focusWithinProps),
|
|
91
|
-
|
|
98
|
+
groupProps: Object.assign(Object.assign({ tabIndex: -1, role: 'group' }, focusWithinProps), { onKeyDown: (e) => {
|
|
99
|
+
if (e.altKey && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {
|
|
100
|
+
e.preventDefault();
|
|
101
|
+
e.stopPropagation();
|
|
102
|
+
setOpen(true);
|
|
103
|
+
focusCalendar();
|
|
104
|
+
}
|
|
105
|
+
} }),
|
|
106
|
+
fieldProps: (0, mergeProps_1.mergeProps)(commonInputProps, mode === 'relative' ? relativeDateProps : inputProps, mode === 'absolute' && dateFieldState.isEmpty && !state.isActive && props.placeholder
|
|
107
|
+
? { value: '' }
|
|
108
|
+
: undefined, { controlRef: handleRef, error }),
|
|
92
109
|
modeSwitcherProps: {
|
|
93
110
|
size: (0, getButtonSizeForInput_1.getButtonSizeForInput)(props.size),
|
|
94
111
|
disabled: state.readOnly || state.disabled,
|
|
@@ -109,7 +126,7 @@ function useRelativeDatePickerProps(state, _a) {
|
|
|
109
126
|
else if (relativeDateState.parsedDate) {
|
|
110
127
|
setFocusedDate(relativeDateState.parsedDate);
|
|
111
128
|
}
|
|
112
|
-
|
|
129
|
+
focusInput();
|
|
113
130
|
},
|
|
114
131
|
},
|
|
115
132
|
calendarButtonProps: {
|
|
@@ -121,29 +138,20 @@ function useRelativeDatePickerProps(state, _a) {
|
|
|
121
138
|
'aria-expanded': isOpen,
|
|
122
139
|
},
|
|
123
140
|
view: 'flat-secondary',
|
|
124
|
-
onFocus: () => {
|
|
125
|
-
wasActiveBeforeClickRef.current = state.isActive;
|
|
126
|
-
},
|
|
127
141
|
onClick: () => {
|
|
128
142
|
state.setActive(true);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
setTimeout(() => {
|
|
133
|
-
var _a;
|
|
134
|
-
(_a = calendarRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
135
|
-
});
|
|
136
|
-
}
|
|
143
|
+
setOpen(!isOpen);
|
|
144
|
+
if (!isOpen) {
|
|
145
|
+
focusCalendar();
|
|
137
146
|
}
|
|
138
|
-
wasActiveBeforeClickRef.current = state.isActive;
|
|
139
147
|
},
|
|
140
148
|
},
|
|
141
149
|
popupProps: {
|
|
142
150
|
open: isOpen,
|
|
143
151
|
onEscapeKeyDown: () => {
|
|
144
152
|
setOpen(false);
|
|
153
|
+
focusInput();
|
|
145
154
|
},
|
|
146
|
-
restoreFocus: true,
|
|
147
155
|
},
|
|
148
156
|
calendarProps: {
|
|
149
157
|
ref: calendarRef,
|
|
@@ -154,7 +162,7 @@ function useRelativeDatePickerProps(state, _a) {
|
|
|
154
162
|
datePickerState.setDateValue(v);
|
|
155
163
|
if (!state.datePickerState.hasTime) {
|
|
156
164
|
setOpen(false);
|
|
157
|
-
|
|
165
|
+
focusInput();
|
|
158
166
|
}
|
|
159
167
|
},
|
|
160
168
|
focusedValue: focusedDate,
|
|
@@ -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,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mergeDateTime = exports.isInvalid = exports.createPlaceholderValue = void 0;
|
|
4
|
+
const date_utils_1 = require("@gravity-ui/date-utils");
|
|
5
|
+
function createPlaceholderValue({ placeholderValue, timeZone }) {
|
|
6
|
+
return (placeholderValue !== null && placeholderValue !== void 0 ? placeholderValue : (0, date_utils_1.dateTime)({ timeZone }).set('hour', 0).set('minute', 0).set('second', 0));
|
|
7
|
+
}
|
|
8
|
+
exports.createPlaceholderValue = createPlaceholderValue;
|
|
9
|
+
function isInvalid(value, minValue, maxValue) {
|
|
10
|
+
if (!value) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
if (minValue && value.isBefore(minValue)) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (maxValue && maxValue.isBefore(value)) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
exports.isInvalid = isInvalid;
|
|
22
|
+
function mergeDateTime(date, time) {
|
|
23
|
+
return date
|
|
24
|
+
.set('hours', time.hour())
|
|
25
|
+
.set('minutes', time.minute())
|
|
26
|
+
.set('seconds', time.second());
|
|
27
|
+
}
|
|
28
|
+
exports.mergeDateTime = mergeDateTime;
|
|
@@ -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;
|
|
@@ -5,7 +5,7 @@ import { block } from '../../utils/cn';
|
|
|
5
5
|
import { useCalendarCellProps } from './hooks/useCalendarCellProps';
|
|
6
6
|
import { useCalendarGridProps } from './hooks/useCalendarGridProps';
|
|
7
7
|
import { useCalendarProps } from './hooks/useCalendarProps';
|
|
8
|
-
import { getDaysInPeriod, getWeekDays } from './utils';
|
|
8
|
+
import { calendarLayouts, getDaysInPeriod, getWeekDays } from './utils';
|
|
9
9
|
import './Calendar.css';
|
|
10
10
|
const b = block('calendar');
|
|
11
11
|
export const CalendarBase = React.forwardRef(function Calendar(props, ref) {
|
|
@@ -16,7 +16,8 @@ export const CalendarBase = React.forwardRef(function Calendar(props, ref) {
|
|
|
16
16
|
state.setFocused(true);
|
|
17
17
|
},
|
|
18
18
|
}));
|
|
19
|
-
return (_jsxs("div", Object.assign({}, calendarProps, { className: b({ size: props.size }), children: [_jsxs("div", { className: b('header'), children: [_jsx(Button, Object.assign({}, modeButtonProps, { view: "flat", size: props.size, children: state.
|
|
19
|
+
return (_jsxs("div", Object.assign({}, calendarProps, { className: b({ size: props.size }), children: [_jsxs("div", { className: b('header'), children: [_jsx(Button, Object.assign({}, modeButtonProps, { view: "flat", size: props.size, children: state.availableModes.indexOf(state.mode) + 1 ===
|
|
20
|
+
state.availableModes.length ? (_jsx("span", { className: b('mode-label', b(`years-label`)), children: modeButtonProps.children }, "label")) : ([
|
|
20
21
|
_jsx("span", { className: b('mode-label'), children: modeButtonProps.children }, "label"),
|
|
21
22
|
_jsx(Button.Icon, { children: _jsx(ArrowToggle, { direction: "bottom" }) }, "icon"),
|
|
22
23
|
]) })), _jsxs("div", { className: b('controls'), children: [_jsx(Button, Object.assign({}, previousButtonProps, { view: "flat", size: props.size, children: _jsx(Button.Icon, { children: _jsx(ArrowToggle, { direction: "left" }) }) })), _jsx(Button, Object.assign({}, nextButtonProps, { view: "flat", size: props.size, children: _jsx(Button.Icon, { children: _jsx(ArrowToggle, { direction: "right" }) }) }))] })] }), _jsx(CalendarGrid, { state: state })] })));
|
|
@@ -27,8 +28,7 @@ function CalendarGrid({ state }) {
|
|
|
27
28
|
const startDateChanged = !state.startDate.isSame(prevState.startDate, 'days');
|
|
28
29
|
let animation;
|
|
29
30
|
if (modeChanged) {
|
|
30
|
-
if ((
|
|
31
|
-
(state.mode === 'months' && prevState.mode === 'years')) {
|
|
31
|
+
if (calendarLayouts.indexOf(prevState.mode) > calendarLayouts.indexOf(state.mode)) {
|
|
32
32
|
animation = 'zoom-out';
|
|
33
33
|
}
|
|
34
34
|
else {
|
|
@@ -59,13 +59,13 @@ function Weekdays({ state }) {
|
|
|
59
59
|
}
|
|
60
60
|
function CalendarGridCells({ state }) {
|
|
61
61
|
const rowsInPeriod = state.mode === 'days' ? 6 : 4;
|
|
62
|
-
const columnsInRow = state.mode === 'days' ? 7 : 3;
|
|
62
|
+
const columnsInRow = state.mode === 'days' ? 7 : 3 + (state.mode === 'quarters' ? 1 : 0);
|
|
63
63
|
const days = getDaysInPeriod(state.startDate, state.endDate, state.mode);
|
|
64
|
-
return (_jsx("div", { className: b('grid-rowgroup', { mode: state.mode }), role: "rowgroup", children: [...new Array(rowsInPeriod).keys()].map((rowIndex) => (
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
return (_jsx("div", { className: b('grid-rowgroup', { mode: state.mode }), role: "rowgroup", children: [...new Array(rowsInPeriod).keys()].map((rowIndex) => (_jsxs("div", { className: b('grid-row'), role: "row", children: [state.mode === 'quarters' ? (_jsx("span", { role: "rowheader", className: b('grid-rowgroup-header'), children: days[rowIndex * columnsInRow].format('YYYY') })) : null, days
|
|
65
|
+
.slice(rowIndex * columnsInRow, (rowIndex + 1) * columnsInRow)
|
|
66
|
+
.map((date) => {
|
|
67
|
+
return _jsx(CalendarCell, { date: date, state: state }, date.unix());
|
|
68
|
+
})] }, rowIndex))) }));
|
|
69
69
|
}
|
|
70
70
|
function CalendarCell({ date, state }) {
|
|
71
71
|
const { cellProps, buttonProps, formattedDate, isDisabled, isSelected, isRangeSelection, isSelectionStart, isSelectionEnd, isOutsideCurrentRange, isUnavailable, isCurrent, isWeekend, } = 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
|
}
|
|
@@ -21,16 +21,7 @@ export function useCalendarCellProps(date, state) {
|
|
|
21
21
|
const isSelectable = !isDisabled && !isUnavailable;
|
|
22
22
|
const isCurrent = dateTime().isSame(date, state.mode);
|
|
23
23
|
const isWeekend = state.isWeekend(date);
|
|
24
|
-
|
|
25
|
-
if (state.mode === 'days') {
|
|
26
|
-
label = `${date.format('dddd')}, ${date.format('LL')}`;
|
|
27
|
-
}
|
|
28
|
-
else if (state.mode === 'months') {
|
|
29
|
-
label = `${date.format('MMMM YYYY')}`;
|
|
30
|
-
}
|
|
31
|
-
else if (state.mode === 'years') {
|
|
32
|
-
label = `${date.format('YYYY')}`;
|
|
33
|
-
}
|
|
24
|
+
const label = getDateLabel(date, state.mode);
|
|
34
25
|
const cellProps = {
|
|
35
26
|
role: 'gridcell',
|
|
36
27
|
'aria-selected': isSelected ? 'true' : undefined,
|
|
@@ -45,12 +36,7 @@ export function useCalendarCellProps(date, state) {
|
|
|
45
36
|
onClick: isSelectable
|
|
46
37
|
? () => {
|
|
47
38
|
state.setFocusedDate(date);
|
|
48
|
-
|
|
49
|
-
state.selectDate(date);
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
state.zoomIn();
|
|
53
|
-
}
|
|
39
|
+
state.selectDate(date);
|
|
54
40
|
}
|
|
55
41
|
: undefined,
|
|
56
42
|
onPointerEnter() {
|
|
@@ -71,6 +57,9 @@ export function useCalendarCellProps(date, state) {
|
|
|
71
57
|
if (state.mode === 'months') {
|
|
72
58
|
formattedDate = date.format('MMM');
|
|
73
59
|
}
|
|
60
|
+
else if (state.mode === 'quarters') {
|
|
61
|
+
formattedDate = date.format('[Q]Q');
|
|
62
|
+
}
|
|
74
63
|
else if (state.mode === 'years') {
|
|
75
64
|
formattedDate = date.format('YYYY');
|
|
76
65
|
}
|
|
@@ -89,3 +78,19 @@ export function useCalendarCellProps(date, state) {
|
|
|
89
78
|
isWeekend,
|
|
90
79
|
};
|
|
91
80
|
}
|
|
81
|
+
function getDateLabel(date, mode) {
|
|
82
|
+
let label = '';
|
|
83
|
+
if (mode === 'days') {
|
|
84
|
+
label = `${date.format('dddd')}, ${date.format('LL')}`;
|
|
85
|
+
}
|
|
86
|
+
else if (mode === 'months') {
|
|
87
|
+
label = `${date.format('MMMM YYYY')}`;
|
|
88
|
+
}
|
|
89
|
+
else if (mode === 'quarters') {
|
|
90
|
+
label = `${date.format('[Q]Q YYYY')}`;
|
|
91
|
+
}
|
|
92
|
+
else if (mode === 'years') {
|
|
93
|
+
label = `${date.format('YYYY')}`;
|
|
94
|
+
}
|
|
95
|
+
return label;
|
|
96
|
+
}
|