@gravity-ui/date-components 1.1.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/hooks/useRangeCalendarState.js +10 -5
- package/dist/cjs/components/DateField/hooks/useDateFieldProps.js +21 -14
- package/dist/cjs/components/DateField/hooks/useDateFieldState.js +16 -160
- package/dist/cjs/components/DateField/utils.d.ts +13 -1
- package/dist/cjs/components/DateField/utils.js +168 -1
- package/dist/cjs/components/DatePicker/DatePicker.css +6 -0
- package/dist/cjs/components/DatePicker/DatePicker.d.ts +2 -2
- package/dist/cjs/components/DatePicker/DatePicker.js +6 -33
- 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 +15 -0
- 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/hooks/useRelativeDatePickerProps.js +29 -27
- package/dist/esm/components/Calendar/hooks/useRangeCalendarState.js +10 -5
- package/dist/esm/components/DateField/hooks/useDateFieldProps.js +21 -14
- package/dist/esm/components/DateField/hooks/useDateFieldState.js +14 -158
- package/dist/esm/components/DateField/utils.d.ts +13 -1
- package/dist/esm/components/DateField/utils.js +162 -0
- package/dist/esm/components/DatePicker/DatePicker.css +6 -0
- package/dist/esm/components/DatePicker/DatePicker.d.ts +2 -2
- package/dist/esm/components/DatePicker/DatePicker.js +8 -35
- 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 +15 -0
- 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/hooks/useRelativeDatePickerProps.js +29 -27
- package/package.json +8 -7
- package/dist/cjs/components/DatePicker/DesktopCalendar.d.ts +0 -17
- package/dist/cjs/components/DatePicker/DesktopCalendar.js +0 -65
- package/dist/esm/components/DatePicker/DesktopCalendar.d.ts +0 -17
- package/dist/esm/components/DatePicker/DesktopCalendar.js +0 -57
|
@@ -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,6 +5,7 @@ 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");
|
|
10
11
|
const dates_1 = require("../../utils/dates");
|
|
@@ -81,6 +82,19 @@ function useDatePickerState(props) {
|
|
|
81
82
|
setSelectedTime(newValue);
|
|
82
83
|
}
|
|
83
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
|
+
});
|
|
84
98
|
return {
|
|
85
99
|
value: value !== null && value !== void 0 ? value : null,
|
|
86
100
|
setValue,
|
|
@@ -101,6 +115,7 @@ function useDatePickerState(props) {
|
|
|
101
115
|
}
|
|
102
116
|
setOpen(newIsOpen);
|
|
103
117
|
},
|
|
118
|
+
dateFieldState,
|
|
104
119
|
};
|
|
105
120
|
}
|
|
106
121
|
exports.useDatePickerState = useDatePickerState;
|
|
@@ -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);
|
|
@@ -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,19 +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(Object.assign({ role: 'group' }, focusWithinProps), { onKeyDown: (e) => {
|
|
98
|
+
groupProps: Object.assign(Object.assign({ tabIndex: -1, role: 'group' }, focusWithinProps), { onKeyDown: (e) => {
|
|
91
99
|
if (e.altKey && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {
|
|
92
100
|
e.preventDefault();
|
|
93
101
|
e.stopPropagation();
|
|
94
102
|
setOpen(true);
|
|
103
|
+
focusCalendar();
|
|
95
104
|
}
|
|
96
105
|
} }),
|
|
97
|
-
fieldProps: (0, mergeProps_1.mergeProps)(commonInputProps, mode === 'relative' ? relativeDateProps : inputProps,
|
|
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 }),
|
|
98
109
|
modeSwitcherProps: {
|
|
99
110
|
size: (0, getButtonSizeForInput_1.getButtonSizeForInput)(props.size),
|
|
100
111
|
disabled: state.readOnly || state.disabled,
|
|
@@ -115,7 +126,7 @@ function useRelativeDatePickerProps(state, _a) {
|
|
|
115
126
|
else if (relativeDateState.parsedDate) {
|
|
116
127
|
setFocusedDate(relativeDateState.parsedDate);
|
|
117
128
|
}
|
|
118
|
-
|
|
129
|
+
focusInput();
|
|
119
130
|
},
|
|
120
131
|
},
|
|
121
132
|
calendarButtonProps: {
|
|
@@ -127,29 +138,20 @@ function useRelativeDatePickerProps(state, _a) {
|
|
|
127
138
|
'aria-expanded': isOpen,
|
|
128
139
|
},
|
|
129
140
|
view: 'flat-secondary',
|
|
130
|
-
onFocus: () => {
|
|
131
|
-
wasActiveBeforeClickRef.current = state.isActive;
|
|
132
|
-
},
|
|
133
141
|
onClick: () => {
|
|
134
142
|
state.setActive(true);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
setTimeout(() => {
|
|
139
|
-
var _a;
|
|
140
|
-
(_a = calendarRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
+
setOpen(!isOpen);
|
|
144
|
+
if (!isOpen) {
|
|
145
|
+
focusCalendar();
|
|
143
146
|
}
|
|
144
|
-
wasActiveBeforeClickRef.current = state.isActive;
|
|
145
147
|
},
|
|
146
148
|
},
|
|
147
149
|
popupProps: {
|
|
148
150
|
open: isOpen,
|
|
149
151
|
onEscapeKeyDown: () => {
|
|
150
152
|
setOpen(false);
|
|
153
|
+
focusInput();
|
|
151
154
|
},
|
|
152
|
-
restoreFocus: true,
|
|
153
155
|
},
|
|
154
156
|
calendarProps: {
|
|
155
157
|
ref: calendarRef,
|
|
@@ -160,7 +162,7 @@ function useRelativeDatePickerProps(state, _a) {
|
|
|
160
162
|
datePickerState.setDateValue(v);
|
|
161
163
|
if (!state.datePickerState.hasTime) {
|
|
162
164
|
setOpen(false);
|
|
163
|
-
|
|
165
|
+
focusInput();
|
|
164
166
|
}
|
|
165
167
|
},
|
|
166
168
|
focusedValue: focusedDate,
|
|
@@ -22,8 +22,16 @@ export function useRangeCalendarState(props) {
|
|
|
22
22
|
const highlightedRange = anchorDate
|
|
23
23
|
? makeRange(anchorDate, calendar.focusedDate, calendar.mode)
|
|
24
24
|
: (_b = (value && makeRange(value.start, value.end, calendar.mode))) !== null && _b !== void 0 ? _b : undefined;
|
|
25
|
-
const
|
|
26
|
-
|
|
25
|
+
const minMode = calendar.availableModes[0];
|
|
26
|
+
const selectDate = (date, force = false) => {
|
|
27
|
+
if (props.disabled) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (!force && calendar.mode !== minMode) {
|
|
31
|
+
calendar.zoomIn();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (props.readOnly) {
|
|
27
35
|
return;
|
|
28
36
|
}
|
|
29
37
|
date = constrainValue(date, props.minValue, props.maxValue);
|
|
@@ -43,9 +51,6 @@ export function useRangeCalendarState(props) {
|
|
|
43
51
|
setValue,
|
|
44
52
|
selectDate,
|
|
45
53
|
anchorDate, setAnchorDate: setAnchorDateState, highlightedRange,
|
|
46
|
-
selectFocusedDate() {
|
|
47
|
-
selectDate(calendar.focusedDate);
|
|
48
|
-
},
|
|
49
54
|
isSelected(date) {
|
|
50
55
|
if (!highlightedRange) {
|
|
51
56
|
return false;
|
|
@@ -80,12 +80,16 @@ export function useDateFieldProps(state, props) {
|
|
|
80
80
|
if (state.selectedSectionIndexes !== null) {
|
|
81
81
|
return;
|
|
82
82
|
}
|
|
83
|
-
const input =
|
|
83
|
+
const input = e.target;
|
|
84
|
+
const isAutofocus = !inputRef.current;
|
|
84
85
|
setTimeout(() => {
|
|
85
86
|
if (!input || input !== inputRef.current) {
|
|
86
87
|
return;
|
|
87
88
|
}
|
|
88
|
-
if (
|
|
89
|
+
if (isAutofocus) {
|
|
90
|
+
state.focusSectionInPosition(0);
|
|
91
|
+
}
|
|
92
|
+
else if (
|
|
89
93
|
// avoid selecting all sections when focusing empty field without value
|
|
90
94
|
input.value.length &&
|
|
91
95
|
Number(input.selectionEnd) - Number(input.selectionStart) ===
|
|
@@ -163,32 +167,35 @@ export function useDateFieldProps(state, props) {
|
|
|
163
167
|
onBeforeInput(e) {
|
|
164
168
|
e.preventDefault();
|
|
165
169
|
// @ts-expect-error
|
|
166
|
-
const key = e.
|
|
167
|
-
|
|
168
|
-
if (key != null) {
|
|
170
|
+
const key = e.data;
|
|
171
|
+
if (key !== undefined && key !== null) {
|
|
169
172
|
state.onInput(key);
|
|
170
173
|
}
|
|
171
174
|
},
|
|
172
175
|
onPaste(e) {
|
|
176
|
+
e.preventDefault();
|
|
173
177
|
if (state.readOnly) {
|
|
174
|
-
e.preventDefault();
|
|
175
178
|
return;
|
|
176
179
|
}
|
|
177
180
|
const pastedValue = e.clipboardData.getData('text');
|
|
178
|
-
if (state.
|
|
179
|
-
e.preventDefault();
|
|
180
|
-
}
|
|
181
|
-
else if (state.selectedSectionIndexes &&
|
|
181
|
+
if (state.selectedSectionIndexes &&
|
|
182
182
|
state.selectedSectionIndexes.startIndex ===
|
|
183
183
|
state.selectedSectionIndexes.endIndex) {
|
|
184
184
|
const activeSection = state.sections[state.selectedSectionIndexes.startIndex];
|
|
185
|
+
const digitsOnly = /^\d+$/.test(pastedValue);
|
|
186
|
+
const lettersOnly = /^[a-zA-Z]+$/.test(pastedValue);
|
|
185
187
|
const isValidValue = Boolean(activeSection) &&
|
|
186
|
-
((activeSection.contentType === 'digit' &&
|
|
187
|
-
activeSection.contentType === 'letter');
|
|
188
|
-
if (
|
|
189
|
-
|
|
188
|
+
((activeSection.contentType === 'digit' && digitsOnly) ||
|
|
189
|
+
(activeSection.contentType === 'letter' && lettersOnly));
|
|
190
|
+
if (isValidValue) {
|
|
191
|
+
state.onInput(pastedValue);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (digitsOnly || lettersOnly) {
|
|
195
|
+
return;
|
|
190
196
|
}
|
|
191
197
|
}
|
|
198
|
+
state.setValueFromString(pastedValue);
|
|
192
199
|
},
|
|
193
200
|
},
|
|
194
201
|
},
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { dateTime, isValid } from '@gravity-ui/date-utils';
|
|
3
3
|
import { useControlledState } from '../../hooks/useControlledState';
|
|
4
4
|
import { createPlaceholderValue, isInvalid, mergeDateTime } from '../../utils/dates';
|
|
5
|
-
import { splitFormatIntoSections } from '../utils';
|
|
5
|
+
import { addSegment, getDurationUnitFromSectionType, getSectionLimits, getSectionValue, setSegment, splitFormatIntoSections, } from '../utils';
|
|
6
6
|
const EDITABLE_SEGMENTS = {
|
|
7
7
|
year: true,
|
|
8
8
|
month: true,
|
|
@@ -13,11 +13,6 @@ const EDITABLE_SEGMENTS = {
|
|
|
13
13
|
dayPeriod: true,
|
|
14
14
|
weekday: true,
|
|
15
15
|
};
|
|
16
|
-
const TYPE_MAPPING = {
|
|
17
|
-
weekday: 'day',
|
|
18
|
-
day: 'date',
|
|
19
|
-
dayPeriod: 'hour',
|
|
20
|
-
};
|
|
21
16
|
const PAGE_STEP = {
|
|
22
17
|
year: 5,
|
|
23
18
|
month: 2,
|
|
@@ -63,10 +58,7 @@ export function useDateFieldState(props) {
|
|
|
63
58
|
? value
|
|
64
59
|
: placeholderDate;
|
|
65
60
|
const sectionsState = useSectionsState(sections, displayValue, validSegments);
|
|
66
|
-
const [selectedSections, setSelectedSections] = React.useState(
|
|
67
|
-
var _a, _b;
|
|
68
|
-
return (_b = (_a = sectionsState.editableSections[0]) === null || _a === void 0 ? void 0 : _a.previousEditableSection) !== null && _b !== void 0 ? _b : -1;
|
|
69
|
-
});
|
|
61
|
+
const [selectedSections, setSelectedSections] = React.useState(-1);
|
|
70
62
|
const selectedSectionIndexes = React.useMemo(() => {
|
|
71
63
|
if (selectedSections === -1) {
|
|
72
64
|
return null;
|
|
@@ -91,9 +83,14 @@ export function useDateFieldState(props) {
|
|
|
91
83
|
return;
|
|
92
84
|
}
|
|
93
85
|
if (Object.keys(validSegments).length >= Object.keys(allSegments).length) {
|
|
94
|
-
|
|
86
|
+
if (!value || !newValue.isSame(value)) {
|
|
87
|
+
setDate(newValue);
|
|
88
|
+
}
|
|
95
89
|
}
|
|
96
90
|
else {
|
|
91
|
+
if (value) {
|
|
92
|
+
setDate(null);
|
|
93
|
+
}
|
|
97
94
|
setPlaceholderDate(newValue);
|
|
98
95
|
}
|
|
99
96
|
}
|
|
@@ -282,9 +279,6 @@ export function useDateFieldState(props) {
|
|
|
282
279
|
const type = getDurationUnitFromSectionType(section.type);
|
|
283
280
|
currentValue = displayValue.set(type, placeholder[type]());
|
|
284
281
|
}
|
|
285
|
-
if (value) {
|
|
286
|
-
setDate(null);
|
|
287
|
-
}
|
|
288
282
|
setValue(currentValue);
|
|
289
283
|
},
|
|
290
284
|
clearAll() {
|
|
@@ -411,6 +405,7 @@ export function useDateFieldState(props) {
|
|
|
411
405
|
}
|
|
412
406
|
},
|
|
413
407
|
setValueFromString(str) {
|
|
408
|
+
enteredKeys.current = '';
|
|
414
409
|
let date = parseDate({ input: str, format, timeZone: props.timeZone });
|
|
415
410
|
if (isValid(date)) {
|
|
416
411
|
if (props.timeZone && !isDateStringWithTimeZone(str)) {
|
|
@@ -434,64 +429,6 @@ function parseDate(options) {
|
|
|
434
429
|
function isDateStringWithTimeZone(str) {
|
|
435
430
|
return /z$/i.test(str) || /[+-]\d\d:\d\d$/.test(str);
|
|
436
431
|
}
|
|
437
|
-
function addSegment(section, date, amount) {
|
|
438
|
-
var _a;
|
|
439
|
-
let val = (_a = section.value) !== null && _a !== void 0 ? _a : 0;
|
|
440
|
-
if (section.type === 'dayPeriod') {
|
|
441
|
-
val = date.hour() + (date.hour() > 12 ? -12 : 12);
|
|
442
|
-
}
|
|
443
|
-
else {
|
|
444
|
-
val = val + amount;
|
|
445
|
-
const min = section.minValue;
|
|
446
|
-
const max = section.maxValue;
|
|
447
|
-
if (typeof min === 'number' && typeof max === 'number') {
|
|
448
|
-
const length = max - min + 1;
|
|
449
|
-
val = ((val - min + length) % length) + min;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
const type = getDurationUnitFromSectionType(section.type);
|
|
453
|
-
return date.set(type, val);
|
|
454
|
-
}
|
|
455
|
-
function setSegment(section, date, amount) {
|
|
456
|
-
const type = section.type;
|
|
457
|
-
switch (type) {
|
|
458
|
-
case 'day':
|
|
459
|
-
case 'weekday':
|
|
460
|
-
case 'month':
|
|
461
|
-
case 'year': {
|
|
462
|
-
return date.set(getDurationUnitFromSectionType(type), amount);
|
|
463
|
-
}
|
|
464
|
-
case 'dayPeriod': {
|
|
465
|
-
const hours = date.hour();
|
|
466
|
-
const wasPM = hours >= 12;
|
|
467
|
-
const isPM = amount >= 12;
|
|
468
|
-
if (isPM === wasPM) {
|
|
469
|
-
return date;
|
|
470
|
-
}
|
|
471
|
-
return date.set('hour', wasPM ? hours - 12 : hours + 12);
|
|
472
|
-
}
|
|
473
|
-
case 'hour': {
|
|
474
|
-
// In 12 hour time, ensure that AM/PM does not change
|
|
475
|
-
let sectionAmount = amount;
|
|
476
|
-
if (section.minValue === 12 || section.maxValue === 11) {
|
|
477
|
-
const hours = date.hour();
|
|
478
|
-
const wasPM = hours >= 12;
|
|
479
|
-
if (!wasPM && sectionAmount === 12) {
|
|
480
|
-
sectionAmount = 0;
|
|
481
|
-
}
|
|
482
|
-
if (wasPM && sectionAmount < 12) {
|
|
483
|
-
sectionAmount += 12;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
return date.set('hour', sectionAmount);
|
|
487
|
-
}
|
|
488
|
-
case 'minute':
|
|
489
|
-
case 'second': {
|
|
490
|
-
return date.set(type, amount);
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
return date;
|
|
494
|
-
}
|
|
495
432
|
function getCurrentEditableSectionIndex(sections, selectedSections) {
|
|
496
433
|
const currentIndex = selectedSections === 'all' || selectedSections === -1 ? 0 : selectedSections;
|
|
497
434
|
const section = sections[currentIndex];
|
|
@@ -541,9 +478,13 @@ function getEditableSections(sections, value, validSegments) {
|
|
|
541
478
|
let renderedValue = section.placeholder;
|
|
542
479
|
if ((isEditable && validSegments[section.type]) || section.type === 'timeZoneName') {
|
|
543
480
|
renderedValue = value.format(section.format);
|
|
481
|
+
if (section.contentType === 'digit' &&
|
|
482
|
+
renderedValue.length < section.placeholder.length) {
|
|
483
|
+
renderedValue = renderedValue.padStart(section.placeholder.length, '0');
|
|
484
|
+
}
|
|
544
485
|
}
|
|
545
486
|
const sectionLength = renderedValue.length;
|
|
546
|
-
const newSection = Object.assign(Object.assign(Object.assign({}, section), { value:
|
|
487
|
+
const newSection = Object.assign(Object.assign(Object.assign({}, section), { value: getSectionValue(section, value), textValue: renderedValue, start: position, end: position + sectionLength, modified: false, previousEditableSection, nextEditableSection: previousEditableSection }), getSectionLimits(section, value));
|
|
547
488
|
newSections.push(newSection);
|
|
548
489
|
if (isEditable) {
|
|
549
490
|
for (let j = Math.max(0, previousEditableSection); j <= i; j++) {
|
|
@@ -561,88 +502,3 @@ function getEditableSections(sections, value, validSegments) {
|
|
|
561
502
|
}
|
|
562
503
|
return newSections;
|
|
563
504
|
}
|
|
564
|
-
function getValue(date, type) {
|
|
565
|
-
switch (type) {
|
|
566
|
-
case 'year':
|
|
567
|
-
case 'month':
|
|
568
|
-
case 'hour':
|
|
569
|
-
case 'minute':
|
|
570
|
-
case 'second': {
|
|
571
|
-
return date[type]();
|
|
572
|
-
}
|
|
573
|
-
case 'day': {
|
|
574
|
-
return date.date();
|
|
575
|
-
}
|
|
576
|
-
case 'weekday': {
|
|
577
|
-
return date.day();
|
|
578
|
-
}
|
|
579
|
-
case 'dayPeriod': {
|
|
580
|
-
return date.hour() >= 12 ? 12 : 0;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
return undefined;
|
|
584
|
-
}
|
|
585
|
-
function isHour12(section) {
|
|
586
|
-
if (section.type === 'hour') {
|
|
587
|
-
return dateTime().set('hour', 15).format(section.format) !== '15';
|
|
588
|
-
}
|
|
589
|
-
return false;
|
|
590
|
-
}
|
|
591
|
-
function getSectionLimits(date, type, options) {
|
|
592
|
-
switch (type) {
|
|
593
|
-
case 'year': {
|
|
594
|
-
return {
|
|
595
|
-
minValue: 1,
|
|
596
|
-
maxValue: 9999,
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
|
-
case 'month': {
|
|
600
|
-
return {
|
|
601
|
-
minValue: 0,
|
|
602
|
-
maxValue: 11,
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
case 'weekday': {
|
|
606
|
-
return {
|
|
607
|
-
minValue: 0,
|
|
608
|
-
maxValue: 6,
|
|
609
|
-
};
|
|
610
|
-
}
|
|
611
|
-
case 'day': {
|
|
612
|
-
return {
|
|
613
|
-
minValue: 1,
|
|
614
|
-
maxValue: date ? date.daysInMonth() : 31,
|
|
615
|
-
};
|
|
616
|
-
}
|
|
617
|
-
case 'hour': {
|
|
618
|
-
if (options.hour12) {
|
|
619
|
-
const isPM = date.hour() >= 12;
|
|
620
|
-
return {
|
|
621
|
-
minValue: isPM ? 12 : 0,
|
|
622
|
-
maxValue: isPM ? 23 : 11,
|
|
623
|
-
};
|
|
624
|
-
}
|
|
625
|
-
return {
|
|
626
|
-
minValue: 0,
|
|
627
|
-
maxValue: 23,
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
case 'minute':
|
|
631
|
-
case 'second': {
|
|
632
|
-
return {
|
|
633
|
-
minValue: 0,
|
|
634
|
-
maxValue: 59,
|
|
635
|
-
};
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
return {};
|
|
639
|
-
}
|
|
640
|
-
function getDurationUnitFromSectionType(type) {
|
|
641
|
-
if (type === 'literal' || type === 'timeZoneName' || type === 'unknown') {
|
|
642
|
-
throw new Error(`${type} section does not have duration unit.`);
|
|
643
|
-
}
|
|
644
|
-
if (type in TYPE_MAPPING) {
|
|
645
|
-
return TYPE_MAPPING[type];
|
|
646
|
-
}
|
|
647
|
-
return type;
|
|
648
|
-
}
|