@gravity-ui/date-components 2.5.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/cjs/components/DatePicker/DatePicker.d.ts +5 -2
  2. package/dist/cjs/components/DatePicker/hooks/datePickerStateFactory.d.ts +7 -3
  3. package/dist/cjs/components/DatePicker/hooks/datePickerStateFactory.js +26 -9
  4. package/dist/cjs/components/DatePicker/hooks/useDatePickerProps.js +33 -6
  5. package/dist/cjs/components/RelativeDatePicker/hooks/useRelativeDatePickerProps.js +16 -12
  6. package/dist/cjs/components/RelativeRangeDatePicker/RelativeRangeDatePicker.css +3 -0
  7. package/dist/cjs/components/RelativeRangeDatePicker/RelativeRangeDatePicker.js +11 -5
  8. package/dist/cjs/components/RelativeRangeDatePicker/components/PickerDialog/PickerDialog.css +4 -0
  9. package/dist/cjs/components/RelativeRangeDatePicker/components/PickerDialog/PickerDialog.js +4 -1
  10. package/dist/cjs/components/RelativeRangeDatePicker/components/Presets/Presets.css +4 -0
  11. package/dist/cjs/components/RelativeRangeDatePicker/hooks/useRelativeRangeDatePickerState.d.ts +6 -5
  12. package/dist/cjs/components/RelativeRangeDatePicker/index.d.ts +4 -0
  13. package/dist/cjs/components/RelativeRangeDatePicker/index.js +7 -1
  14. package/dist/cjs/components/RelativeRangeDatePicker/utils.js +1 -1
  15. package/dist/cjs/components/types/datePicker.d.ts +8 -0
  16. package/dist/esm/components/DatePicker/DatePicker.d.ts +5 -2
  17. package/dist/esm/components/DatePicker/hooks/datePickerStateFactory.d.ts +7 -3
  18. package/dist/esm/components/DatePicker/hooks/datePickerStateFactory.js +26 -9
  19. package/dist/esm/components/DatePicker/hooks/useDatePickerProps.js +33 -6
  20. package/dist/esm/components/RelativeDatePicker/hooks/useRelativeDatePickerProps.js +16 -12
  21. package/dist/esm/components/RelativeRangeDatePicker/RelativeRangeDatePicker.css +3 -0
  22. package/dist/esm/components/RelativeRangeDatePicker/RelativeRangeDatePicker.js +11 -5
  23. package/dist/esm/components/RelativeRangeDatePicker/components/PickerDialog/PickerDialog.css +4 -0
  24. package/dist/esm/components/RelativeRangeDatePicker/components/PickerDialog/PickerDialog.js +4 -1
  25. package/dist/esm/components/RelativeRangeDatePicker/components/Presets/Presets.css +4 -0
  26. package/dist/esm/components/RelativeRangeDatePicker/hooks/useRelativeRangeDatePickerState.d.ts +6 -5
  27. package/dist/esm/components/RelativeRangeDatePicker/index.d.ts +4 -0
  28. package/dist/esm/components/RelativeRangeDatePicker/index.js +2 -0
  29. package/dist/esm/components/RelativeRangeDatePicker/utils.js +1 -1
  30. package/dist/esm/components/types/datePicker.d.ts +8 -0
  31. package/package.json +13 -11
@@ -1,9 +1,12 @@
1
1
  import React from 'react';
2
2
  import type { DateTime } from '@gravity-ui/date-utils';
3
3
  import type { CalendarProps } from '../Calendar';
4
- import type { AccessibilityProps, DateFieldBase, DomProps, FocusableProps, InputDOMProps, KeyboardEvents, StyleProps, TextInputProps } from '../types';
4
+ import type { AccessibilityProps, DomProps, FocusableProps, InputDOMProps, KeyboardEvents, StyleProps, TextInputProps } from '../types';
5
+ import type { DatePickerStateOptions } from './hooks/datePickerStateFactory';
5
6
  import './DatePicker.css';
6
- export interface DatePickerProps<T = DateTime> extends DateFieldBase<T>, TextInputProps, FocusableProps, KeyboardEvents, DomProps, InputDOMProps, StyleProps, AccessibilityProps {
7
+ export interface DatePickerProps<T = DateTime> extends DatePickerStateOptions<T>, TextInputProps, FocusableProps, KeyboardEvents, DomProps, InputDOMProps, StyleProps, AccessibilityProps {
7
8
  children?: (props: CalendarProps<T>) => React.ReactNode;
9
+ disablePortal?: boolean;
10
+ disableFocusTrap?: boolean;
8
11
  }
9
12
  export declare function DatePicker({ className, ...props }: DatePickerProps): import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,7 @@
1
1
  import type { DateTime } from '@gravity-ui/date-utils';
2
2
  import type { DateFieldState } from '../../DateField';
3
- import type { DateFieldBase } from '../../types';
3
+ import type { DateFieldBase, PopupTriggerProps } from '../../types';
4
+ type OpenChangeReason = 'EscapeKeyDown' | 'FocusOut' | 'ClickOutside' | 'ValueSelected' | 'TriggerButtonClick' | 'ShortcutKeyDown';
4
5
  export interface DatePickerState<T = DateTime> {
5
6
  /** The currently selected date. */
6
7
  value: T | null;
@@ -36,7 +37,7 @@ export interface DatePickerState<T = DateTime> {
36
37
  /** Whether the calendar popover is currently open. */
37
38
  isOpen: boolean;
38
39
  /** Sets whether the calendar popover is open. */
39
- setOpen: (isOpen: boolean) => void;
40
+ setOpen: (isOpen: boolean, reason: OpenChangeReason) => void;
40
41
  dateFieldState: DateFieldState<T>;
41
42
  }
42
43
  export interface DatePickerStateFactoryOptions<T, O extends DateFieldBase<T>> {
@@ -46,4 +47,7 @@ export interface DatePickerStateFactoryOptions<T, O extends DateFieldBase<T>> {
46
47
  getDateTime: (date: T | null | undefined) => DateTime | undefined;
47
48
  useDateFieldState: (props: O) => DateFieldState<T>;
48
49
  }
49
- export declare function datePickerStateFactory<T, O extends DateFieldBase<T>>({ getPlaceholderTime, mergeDateTime, setTimezone, getDateTime, useDateFieldState, }: DatePickerStateFactoryOptions<T, O>): (props: O) => DatePickerState<T>;
50
+ export interface DatePickerStateOptions<T> extends DateFieldBase<T>, PopupTriggerProps<[OpenChangeReason]> {
51
+ }
52
+ export declare function datePickerStateFactory<T, O extends DatePickerStateOptions<T>>({ getPlaceholderTime, mergeDateTime, setTimezone, getDateTime, useDateFieldState, }: DatePickerStateFactoryOptions<T, O>): (props: O) => DatePickerState<T>;
53
+ export {};
@@ -7,10 +7,11 @@ const uikit_1 = require("@gravity-ui/uikit");
7
7
  const useDefaultTimeZone_1 = require("../../utils/useDefaultTimeZone");
8
8
  function datePickerStateFactory({ getPlaceholderTime, mergeDateTime, setTimezone, getDateTime, useDateFieldState, }) {
9
9
  return function useDatePickerState(props) {
10
- var _a;
10
+ var _a, _b;
11
11
  const { disabled, readOnly } = props;
12
- const [isOpen, setOpen] = react_1.default.useState(false);
13
- const [value, setValue] = (0, uikit_1.useControlledState)(props.value, (_a = props.defaultValue) !== null && _a !== void 0 ? _a : null, props.onUpdate);
12
+ const [isOpen, _setOpen] = (0, uikit_1.useControlledState)(props.open, (_a = props.defaultOpen) !== null && _a !== void 0 ? _a : false, props.onOpenChange);
13
+ const setOpen = _setOpen;
14
+ const [value, setValue] = (0, uikit_1.useControlledState)(props.value, (_b = props.defaultValue) !== null && _b !== void 0 ? _b : null, props.onUpdate);
14
15
  const [selectedDateInner, setSelectedDate] = react_1.default.useState(null);
15
16
  const [selectedTimeInner, setSelectedTime] = react_1.default.useState(null);
16
17
  const inputTimeZone = (0, useDefaultTimeZone_1.useDefaultTimeZone)(getDateTime(props.value) || getDateTime(props.defaultValue) || props.placeholderValue);
@@ -38,9 +39,25 @@ function datePickerStateFactory({ getPlaceholderTime, mergeDateTime, setTimezone
38
39
  disabled,
39
40
  readOnly, validationState: props.validationState, minValue: props.minValue, maxValue: props.maxValue, isDateUnavailable: props.isDateUnavailable, format, placeholderValue: props.placeholderValue, timeZone }));
40
41
  const timeFormat = react_1.default.useMemo(() => {
41
- const hasSeconds = dateFieldState.sections.some((s) => s.type === 'second');
42
- return hasSeconds ? 'LTS' : 'LT';
43
- }, [dateFieldState.sections]);
42
+ if (!dateFieldState.hasTime) {
43
+ return undefined;
44
+ }
45
+ const timeFormatParts = [];
46
+ const hours = dateFieldState.sections.find((s) => s.type === 'hour');
47
+ if (hours) {
48
+ timeFormatParts.push(hours.format);
49
+ }
50
+ const minutes = dateFieldState.sections.find((s) => s.type === 'minute');
51
+ if (minutes) {
52
+ timeFormatParts.push(minutes.format);
53
+ }
54
+ const seconds = dateFieldState.sections.find((s) => s.type === 'second');
55
+ if (seconds) {
56
+ timeFormatParts.push(seconds.format);
57
+ }
58
+ const dayPeriod = dateFieldState.sections.find((s) => s.type === 'dayPeriod');
59
+ return timeFormatParts.join(':') + (dayPeriod ? ` ${dayPeriod.format}` : '');
60
+ }, [dateFieldState.hasTime, dateFieldState.sections]);
44
61
  if (value) {
45
62
  selectedDate = setTimezone(value, timeZone);
46
63
  if (dateFieldState.hasTime) {
@@ -65,7 +82,7 @@ function datePickerStateFactory({ getPlaceholderTime, mergeDateTime, setTimezone
65
82
  commitValue(newValue, newValue);
66
83
  }
67
84
  if (shouldClose) {
68
- setOpen(false);
85
+ setOpen(false, 'ValueSelected');
69
86
  }
70
87
  };
71
88
  const selectTime = (newValue) => {
@@ -108,11 +125,11 @@ function datePickerStateFactory({ getPlaceholderTime, mergeDateTime, setTimezone
108
125
  timeFormat,
109
126
  timeZone,
110
127
  isOpen,
111
- setOpen(newIsOpen) {
128
+ setOpen(newIsOpen, reason) {
112
129
  if (!newIsOpen && !value && selectedDate && dateFieldState.hasTime) {
113
130
  commitValue(selectedDate, selectedTime || getPlaceholderTime(props.placeholderValue, props.timeZone));
114
131
  }
115
- setOpen(newIsOpen);
132
+ setOpen(newIsOpen, reason);
116
133
  },
117
134
  dateFieldState,
118
135
  };
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useDatePickerProps = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const react_1 = tslib_1.__importDefault(require("react"));
6
+ const date_utils_1 = require("@gravity-ui/date-utils");
6
7
  const uikit_1 = require("@gravity-ui/uikit");
7
8
  const DateField_1 = require("../../DateField");
8
9
  const getButtonSizeForInput_1 = require("../../utils/getButtonSizeForInput");
@@ -12,13 +13,31 @@ const utils_1 = require("../utils");
12
13
  function useDatePickerProps(state, _a) {
13
14
  var { onFocus, onBlur } = _a, props = tslib_1.__rest(_a, ["onFocus", "onBlur"]);
14
15
  const [isActive, setActive] = react_1.default.useState(false);
16
+ const [focusedDate, setFocusedDate] = react_1.default.useState((0, date_utils_1.isDateTime)(state.dateFieldState.displayValue)
17
+ ? state.dateFieldState.displayValue
18
+ : state.dateFieldState.displayValue.start);
19
+ const [prevDateValue, setPrevDateValue] = react_1.default.useState(state.dateFieldState.displayValue);
20
+ if ((0, date_utils_1.isDateTime)(state.dateFieldState.displayValue)) {
21
+ if (!state.dateFieldState.displayValue.isSame(prevDateValue, 'day')) {
22
+ setPrevDateValue(state.dateFieldState.displayValue);
23
+ setFocusedDate(state.dateFieldState.displayValue);
24
+ }
25
+ }
26
+ else if (!state.dateFieldState.displayValue.start.isSame(prevDateValue.start, 'day')) {
27
+ setPrevDateValue(state.dateFieldState.displayValue);
28
+ setFocusedDate(state.dateFieldState.displayValue.start);
29
+ }
30
+ else if (!state.dateFieldState.displayValue.end.isSame(prevDateValue.end, 'day')) {
31
+ setPrevDateValue(state.dateFieldState.displayValue);
32
+ setFocusedDate(state.dateFieldState.displayValue.end);
33
+ }
15
34
  const { focusWithinProps } = (0, uikit_1.useFocusWithin)({
16
35
  onFocusWithin: onFocus,
17
36
  onBlurWithin: onBlur,
18
37
  onFocusWithinChange(isFocusWithin) {
19
38
  setActive(isFocusWithin);
20
39
  if (!isFocusWithin) {
21
- state.setOpen(false);
40
+ state.setOpen(false, 'FocusOut');
22
41
  }
23
42
  },
24
43
  });
@@ -40,7 +59,7 @@ function useDatePickerProps(state, _a) {
40
59
  if (!onlyTime && e.altKey && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {
41
60
  e.preventDefault();
42
61
  e.stopPropagation();
43
- state.setOpen(true);
62
+ state.setOpen(true, 'ShortcutKeyDown');
44
63
  }
45
64
  } }),
46
65
  fieldProps: (0, mergeProps_1.mergeProps)(inputProps, state.dateFieldState.isEmpty && !isActive && props.placeholder
@@ -58,25 +77,31 @@ function useDatePickerProps(state, _a) {
58
77
  view: 'flat-secondary',
59
78
  onClick: () => {
60
79
  setActive(true);
61
- state.setOpen(!state.isOpen);
80
+ state.setOpen(!state.isOpen, 'TriggerButtonClick');
62
81
  },
63
82
  },
64
83
  popupProps: {
65
84
  open: state.isOpen,
66
85
  onEscapeKeyDown: () => {
67
- state.setOpen(false);
86
+ state.setOpen(false, 'EscapeKeyDown');
68
87
  focusInput();
69
88
  },
70
89
  onOutsideClick: (e) => {
71
90
  var _a;
72
91
  if (e.target !== calendarButtonRef.current) {
73
- state.setOpen(false);
92
+ state.setOpen(false, 'ClickOutside');
74
93
  }
75
94
  if (e.target && ((_a = groupRef.current) === null || _a === void 0 ? void 0 : _a.contains(e.target))) {
76
95
  focusInput();
77
96
  }
78
97
  },
79
- focusTrap: true,
98
+ onTransitionExited: () => {
99
+ setFocusedDate((0, date_utils_1.isDateTime)(state.dateFieldState.displayValue)
100
+ ? state.dateFieldState.displayValue
101
+ : state.dateFieldState.displayValue.start);
102
+ },
103
+ focusTrap: !props.disableFocusTrap,
104
+ disablePortal: props.disablePortal,
80
105
  },
81
106
  calendarProps: {
82
107
  ref: calendarRef,
@@ -95,6 +120,8 @@ function useDatePickerProps(state, _a) {
95
120
  maxValue: props.maxValue,
96
121
  isDateUnavailable: props.isDateUnavailable,
97
122
  timeZone: state.timeZone,
123
+ focusedValue: focusedDate,
124
+ onFocusUpdate: setFocusedDate,
98
125
  },
99
126
  timeInputProps: {
100
127
  value: state.timeValue,
@@ -38,18 +38,6 @@ function useRelativeDatePickerProps(state, _a) {
38
38
  if (!state.isActive && isOpen) {
39
39
  setOpen(false);
40
40
  }
41
- const [prevOpen, setPrevOpen] = react_1.default.useState(isOpen);
42
- if (isOpen !== prevOpen) {
43
- setPrevOpen(isOpen);
44
- if (!isOpen) {
45
- // FIXME: use popup afterOpenChange instead.
46
- setTimeout(() => {
47
- setFocusedDate(mode === 'relative'
48
- ? relativeDateState.lastCorrectDate
49
- : datePickerState.dateFieldState.displayValue);
50
- }, 200);
51
- }
52
- }
53
41
  const commonInputProps = {
54
42
  onFocus: () => {
55
43
  if (!state.isActive) {
@@ -59,6 +47,17 @@ function useRelativeDatePickerProps(state, _a) {
59
47
  },
60
48
  errorMessage: props.errorMessage,
61
49
  errorPlacement: props.errorPlacement,
50
+ controlProps: {
51
+ onClick: () => {
52
+ if (state.disabled) {
53
+ return;
54
+ }
55
+ if (!isOpen) {
56
+ state.setActive(true);
57
+ setOpen(true);
58
+ }
59
+ },
60
+ },
62
61
  };
63
62
  const { inputProps } = (0, DateField_1.useDateFieldProps)(datePickerState.dateFieldState, Object.assign(Object.assign({}, props), { value: undefined, defaultValue: undefined, onUpdate: undefined }));
64
63
  const { inputProps: relativeDateProps } = (0, RelativeDateField_1.useRelativeDateFieldProps)(relativeDateState, Object.assign(Object.assign({}, props), { value: undefined, defaultValue: undefined, onUpdate: undefined }));
@@ -145,6 +144,11 @@ function useRelativeDatePickerProps(state, _a) {
145
144
  setOpen(false);
146
145
  focusInput();
147
146
  },
147
+ onTransitionExited: () => {
148
+ setFocusedDate(mode === 'relative'
149
+ ? relativeDateState.lastCorrectDate
150
+ : datePickerState.dateFieldState.displayValue);
151
+ },
148
152
  },
149
153
  calendarProps: {
150
154
  ref: calendarRef,
@@ -2,6 +2,9 @@
2
2
  position: relative;
3
3
  display: inline-flex;
4
4
  }
5
+ .g-date-relative-range-date-picker__input {
6
+ caret-color: transparent;
7
+ }
5
8
  .g-date-relative-range-date-picker__input_mobile {
6
9
  pointer-events: none;
7
10
  }
@@ -33,10 +33,7 @@ function RelativeRangeDatePicker(props) {
33
33
  },
34
34
  onBlurWithin: (e) => {
35
35
  var _a;
36
- // when the popup is open and an user clicks outside, focus will be returned to the input
37
- const seemsIsClickOutsideInEmptySpace = open &&
38
- (document.activeElement === null || document.activeElement === document.body);
39
- if (!seemsIsClickOutsideInEmptySpace) {
36
+ if (!open) {
40
37
  setIsActive(false);
41
38
  (_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, e);
42
39
  }
@@ -55,7 +52,7 @@ function RelativeRangeDatePicker(props) {
55
52
  }), [alwaysShowAsAbsolute, format, getRangeTitle, presetTabs, state.timeZone, state.value]);
56
53
  const validationState = props.validationState || (state.isInvalid ? 'invalid' : undefined);
57
54
  const errorMessage = (_a = props.errorMessage) !== null && _a !== void 0 ? _a : state.errors.join('\n');
58
- return ((0, jsx_runtime_1.jsxs)("div", Object.assign({ ref: anchorRef }, focusWithinProps, { className: b(null, props.className), style: props.style, children: [(0, jsx_runtime_1.jsx)(uikit_1.TextInput, { autoFocus: props.autoFocus, controlRef: inputRef, value: text, placeholder: props.placeholder, onUpdate: (v) => {
55
+ return ((0, jsx_runtime_1.jsxs)("div", Object.assign({ ref: anchorRef }, focusWithinProps, { className: b(null, props.className), style: props.style, children: [(0, jsx_runtime_1.jsx)(uikit_1.TextInput, { id: props.id, autoFocus: props.autoFocus, controlRef: inputRef, value: text, placeholder: props.placeholder, onUpdate: (v) => {
59
56
  if (!props.readOnly && !v) {
60
57
  state.setValue(null, 'default');
61
58
  }
@@ -64,6 +61,15 @@ function RelativeRangeDatePicker(props) {
64
61
  'aria-expanded': open,
65
62
  disabled: isMobile,
66
63
  className: b('input', { mobile: isMobile }),
64
+ onClick: () => {
65
+ if (props.disabled) {
66
+ return;
67
+ }
68
+ if (!open) {
69
+ setIsActive(true);
70
+ setOpen(true);
71
+ }
72
+ },
67
73
  }, onKeyDown: (e) => {
68
74
  if (props.disabled) {
69
75
  return;
@@ -1,5 +1,6 @@
1
1
  .g-date-relative-range-date-picker-dialog__content {
2
2
  --_--popup-content-padding: var(--g-spacing-2);
3
+ --_--g-date-picker-presets-padding: var(--_--popup-content-padding);
3
4
  padding: var(--_--popup-content-padding);
4
5
  }
5
6
  .g-date-relative-range-date-picker-dialog__content_mobile {
@@ -29,4 +30,7 @@
29
30
  margin-block: var(--g-spacing-2) calc(-1 * var(--_--popup-content-padding));
30
31
  margin-inline: calc(-1 * var(--_--popup-content-padding));
31
32
  border-block-start: 1px solid var(--g-color-line-generic);
33
+ }
34
+ .g-date-relative-range-date-picker-dialog__presets {
35
+ margin-inline: calc(-1 * var(--_--popup-content-padding));
32
36
  }
@@ -40,5 +40,8 @@ function DialogContent(props) {
40
40
  props.onApply();
41
41
  }, className: b('apply'), width: "max", children: (0, i18n_1.i18n)('Apply') })) : null, props.withPresets && !props.readOnly ? ((0, jsx_runtime_1.jsx)(Presets_1.Presets, { size: props.size, presetTabs: props.presetTabs, onChoosePreset: (start, end) => {
42
42
  state.setRange({ type: 'relative', value: start }, { type: 'relative', value: end });
43
- }, minValue: props.minValue, docs: props.docs })) : null, props.withZonesList ? ((0, jsx_runtime_1.jsx)("div", { className: b('zone'), children: (0, jsx_runtime_1.jsx)(Zones_1.Zones, { value: state.timeZone, onUpdate: state.setTimeZone, disabled: props.readOnly, size: props.size }) })) : null] }));
43
+ if (!props.withApplyButton) {
44
+ props.onApply();
45
+ }
46
+ }, minValue: props.minValue, docs: props.docs, className: b('presets') })) : null, props.withZonesList ? ((0, jsx_runtime_1.jsx)("div", { className: b('zone'), children: (0, jsx_runtime_1.jsx)(Zones_1.Zones, { value: state.timeZone, onUpdate: state.setTimeZone, disabled: props.readOnly, size: props.size }) })) : null] }));
44
47
  }
@@ -1,8 +1,12 @@
1
+ .g-date-relative-range-date-picker-presets {
2
+ --g-list-item-padding: 0 var(--_--g-date-picker-presets-padding, 0);
3
+ }
1
4
  .g-date-relative-range-date-picker-presets__tabs {
2
5
  --g-tabs-border-width: 0;
3
6
  display: flex;
4
7
  align-items: center;
5
8
  gap: var(--g-spacing-2);
9
+ padding-inline: var(--_--g-date-picker-presets-padding, 0);
6
10
  box-shadow: inset 0 -1px var(--g-color-line-generic);
7
11
  }
8
12
  .g-date-relative-range-date-picker-presets__list-container {
@@ -1,17 +1,18 @@
1
1
  import type { DateTime } from '@gravity-ui/date-utils';
2
2
  import type { Value } from '../../RelativeDatePicker';
3
3
  import type { RangeValue } from '../../types';
4
+ export type RelativeRangeDatePickerValue = RangeValue<Value | null>;
4
5
  export interface RelativeRangeDatePickerState {
5
- value: RangeValue<Value | null> | null;
6
+ value: RelativeRangeDatePickerValue | null;
6
7
  timeZone: string;
7
- setValue: (value: RangeValue<Value | null> | null, timeZone: string) => void;
8
+ setValue: (value: RelativeRangeDatePickerValue | null, timeZone: string) => void;
8
9
  isInvalid: boolean;
9
10
  errors: string[];
10
11
  }
11
12
  export interface RelativeRangeDatePickerStateOptions {
12
- value?: RangeValue<Value | null> | null;
13
- defaultValue?: RangeValue<Value | null>;
14
- onUpdate?: (value: RangeValue<Value | null> | null, timeZone: string) => void;
13
+ value?: RelativeRangeDatePickerValue | null;
14
+ defaultValue?: RelativeRangeDatePickerValue;
15
+ onUpdate?: (value: RelativeRangeDatePickerValue | null, timeZone: string) => void;
15
16
  timeZone?: string;
16
17
  defaultTimeZone?: string;
17
18
  onUpdateTimeZone?: (timeZone: string) => void;
@@ -2,3 +2,7 @@ export * from './RelativeRangeDatePicker';
2
2
  export { PickerDialog as RelativeRangeDatePickerDialog } from './components/PickerDialog/PickerDialog';
3
3
  export type { PickerDialogProps as RelativeRangeDatePickerDialogProps } from './components/PickerDialog/PickerDialog';
4
4
  export * from './hooks/useRelativeRangeDatePickerState';
5
+ export { DEFAULT_DATE_PRESETS, DEFAULT_TIME_PRESETS, DEFAULT_OTHERS_PRESETS, } from './components/Presets/defaultPresets';
6
+ export { getDefaultPresetTabs } from './components/Presets/utils';
7
+ export type { Preset as RelativeRangeDatePickerPreset } from './components/Presets/defaultPresets';
8
+ export type { PresetTab as RelativeRangeDatePickerPresetTab } from './components/Presets/utils';
@@ -1,8 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RelativeRangeDatePickerDialog = void 0;
3
+ exports.getDefaultPresetTabs = exports.DEFAULT_OTHERS_PRESETS = exports.DEFAULT_TIME_PRESETS = exports.DEFAULT_DATE_PRESETS = exports.RelativeRangeDatePickerDialog = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  tslib_1.__exportStar(require("./RelativeRangeDatePicker"), exports);
6
6
  var PickerDialog_1 = require("./components/PickerDialog/PickerDialog");
7
7
  Object.defineProperty(exports, "RelativeRangeDatePickerDialog", { enumerable: true, get: function () { return PickerDialog_1.PickerDialog; } });
8
8
  tslib_1.__exportStar(require("./hooks/useRelativeRangeDatePickerState"), exports);
9
+ var defaultPresets_1 = require("./components/Presets/defaultPresets");
10
+ Object.defineProperty(exports, "DEFAULT_DATE_PRESETS", { enumerable: true, get: function () { return defaultPresets_1.DEFAULT_DATE_PRESETS; } });
11
+ Object.defineProperty(exports, "DEFAULT_TIME_PRESETS", { enumerable: true, get: function () { return defaultPresets_1.DEFAULT_TIME_PRESETS; } });
12
+ Object.defineProperty(exports, "DEFAULT_OTHERS_PRESETS", { enumerable: true, get: function () { return defaultPresets_1.DEFAULT_OTHERS_PRESETS; } });
13
+ var utils_1 = require("./components/Presets/utils");
14
+ Object.defineProperty(exports, "getDefaultPresetTabs", { enumerable: true, get: function () { return utils_1.getDefaultPresetTabs; } });
@@ -27,7 +27,7 @@ function getDefaultTitle({ value, timeZone, alwaysShowAsAbsolute, format = 'L',
27
27
  if (!value) {
28
28
  return '';
29
29
  }
30
- const tz = timeZone === 'system' ? '' : ` (${getTimeZoneOffset(timeZone)})`;
30
+ const tz = timeZone === 'default' ? '' : ` (${getTimeZoneOffset(timeZone)})`;
31
31
  let from = '';
32
32
  if (value.start) {
33
33
  from =
@@ -17,3 +17,11 @@ export interface DateFieldBase<T = DateTime> extends ValueBase<T | null>, InputB
17
17
  */
18
18
  timeZone?: string;
19
19
  }
20
+ export interface PopupTriggerProps<Args extends unknown[] = []> {
21
+ /** Whether the popup is open (controlled). */
22
+ open?: boolean;
23
+ /** Whether the popup is open by default (uncontrolled). */
24
+ defaultOpen?: boolean;
25
+ /** Handler that is called when the popup's open state changes. */
26
+ onOpenChange?: (open: boolean, ...args: Args) => void;
27
+ }
@@ -1,9 +1,12 @@
1
1
  import React from 'react';
2
2
  import type { DateTime } from '@gravity-ui/date-utils';
3
3
  import type { CalendarProps } from '../Calendar';
4
- import type { AccessibilityProps, DateFieldBase, DomProps, FocusableProps, InputDOMProps, KeyboardEvents, StyleProps, TextInputProps } from '../types';
4
+ import type { AccessibilityProps, DomProps, FocusableProps, InputDOMProps, KeyboardEvents, StyleProps, TextInputProps } from '../types';
5
+ import type { DatePickerStateOptions } from './hooks/datePickerStateFactory';
5
6
  import './DatePicker.css';
6
- export interface DatePickerProps<T = DateTime> extends DateFieldBase<T>, TextInputProps, FocusableProps, KeyboardEvents, DomProps, InputDOMProps, StyleProps, AccessibilityProps {
7
+ export interface DatePickerProps<T = DateTime> extends DatePickerStateOptions<T>, TextInputProps, FocusableProps, KeyboardEvents, DomProps, InputDOMProps, StyleProps, AccessibilityProps {
7
8
  children?: (props: CalendarProps<T>) => React.ReactNode;
9
+ disablePortal?: boolean;
10
+ disableFocusTrap?: boolean;
8
11
  }
9
12
  export declare function DatePicker({ className, ...props }: DatePickerProps): import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,7 @@
1
1
  import type { DateTime } from '@gravity-ui/date-utils';
2
2
  import type { DateFieldState } from '../../DateField';
3
- import type { DateFieldBase } from '../../types';
3
+ import type { DateFieldBase, PopupTriggerProps } from '../../types';
4
+ type OpenChangeReason = 'EscapeKeyDown' | 'FocusOut' | 'ClickOutside' | 'ValueSelected' | 'TriggerButtonClick' | 'ShortcutKeyDown';
4
5
  export interface DatePickerState<T = DateTime> {
5
6
  /** The currently selected date. */
6
7
  value: T | null;
@@ -36,7 +37,7 @@ export interface DatePickerState<T = DateTime> {
36
37
  /** Whether the calendar popover is currently open. */
37
38
  isOpen: boolean;
38
39
  /** Sets whether the calendar popover is open. */
39
- setOpen: (isOpen: boolean) => void;
40
+ setOpen: (isOpen: boolean, reason: OpenChangeReason) => void;
40
41
  dateFieldState: DateFieldState<T>;
41
42
  }
42
43
  export interface DatePickerStateFactoryOptions<T, O extends DateFieldBase<T>> {
@@ -46,4 +47,7 @@ export interface DatePickerStateFactoryOptions<T, O extends DateFieldBase<T>> {
46
47
  getDateTime: (date: T | null | undefined) => DateTime | undefined;
47
48
  useDateFieldState: (props: O) => DateFieldState<T>;
48
49
  }
49
- export declare function datePickerStateFactory<T, O extends DateFieldBase<T>>({ getPlaceholderTime, mergeDateTime, setTimezone, getDateTime, useDateFieldState, }: DatePickerStateFactoryOptions<T, O>): (props: O) => DatePickerState<T>;
50
+ export interface DatePickerStateOptions<T> extends DateFieldBase<T>, PopupTriggerProps<[OpenChangeReason]> {
51
+ }
52
+ export declare function datePickerStateFactory<T, O extends DatePickerStateOptions<T>>({ getPlaceholderTime, mergeDateTime, setTimezone, getDateTime, useDateFieldState, }: DatePickerStateFactoryOptions<T, O>): (props: O) => DatePickerState<T>;
53
+ export {};
@@ -3,10 +3,11 @@ import { useControlledState } from '@gravity-ui/uikit';
3
3
  import { useDefaultTimeZone } from '../../utils/useDefaultTimeZone';
4
4
  export function datePickerStateFactory({ getPlaceholderTime, mergeDateTime, setTimezone, getDateTime, useDateFieldState, }) {
5
5
  return function useDatePickerState(props) {
6
- var _a;
6
+ var _a, _b;
7
7
  const { disabled, readOnly } = props;
8
- const [isOpen, setOpen] = React.useState(false);
9
- const [value, setValue] = useControlledState(props.value, (_a = props.defaultValue) !== null && _a !== void 0 ? _a : null, props.onUpdate);
8
+ const [isOpen, _setOpen] = useControlledState(props.open, (_a = props.defaultOpen) !== null && _a !== void 0 ? _a : false, props.onOpenChange);
9
+ const setOpen = _setOpen;
10
+ const [value, setValue] = useControlledState(props.value, (_b = props.defaultValue) !== null && _b !== void 0 ? _b : null, props.onUpdate);
10
11
  const [selectedDateInner, setSelectedDate] = React.useState(null);
11
12
  const [selectedTimeInner, setSelectedTime] = React.useState(null);
12
13
  const inputTimeZone = useDefaultTimeZone(getDateTime(props.value) || getDateTime(props.defaultValue) || props.placeholderValue);
@@ -34,9 +35,25 @@ export function datePickerStateFactory({ getPlaceholderTime, mergeDateTime, setT
34
35
  disabled,
35
36
  readOnly, validationState: props.validationState, minValue: props.minValue, maxValue: props.maxValue, isDateUnavailable: props.isDateUnavailable, format, placeholderValue: props.placeholderValue, timeZone }));
36
37
  const timeFormat = React.useMemo(() => {
37
- const hasSeconds = dateFieldState.sections.some((s) => s.type === 'second');
38
- return hasSeconds ? 'LTS' : 'LT';
39
- }, [dateFieldState.sections]);
38
+ if (!dateFieldState.hasTime) {
39
+ return undefined;
40
+ }
41
+ const timeFormatParts = [];
42
+ const hours = dateFieldState.sections.find((s) => s.type === 'hour');
43
+ if (hours) {
44
+ timeFormatParts.push(hours.format);
45
+ }
46
+ const minutes = dateFieldState.sections.find((s) => s.type === 'minute');
47
+ if (minutes) {
48
+ timeFormatParts.push(minutes.format);
49
+ }
50
+ const seconds = dateFieldState.sections.find((s) => s.type === 'second');
51
+ if (seconds) {
52
+ timeFormatParts.push(seconds.format);
53
+ }
54
+ const dayPeriod = dateFieldState.sections.find((s) => s.type === 'dayPeriod');
55
+ return timeFormatParts.join(':') + (dayPeriod ? ` ${dayPeriod.format}` : '');
56
+ }, [dateFieldState.hasTime, dateFieldState.sections]);
40
57
  if (value) {
41
58
  selectedDate = setTimezone(value, timeZone);
42
59
  if (dateFieldState.hasTime) {
@@ -61,7 +78,7 @@ export function datePickerStateFactory({ getPlaceholderTime, mergeDateTime, setT
61
78
  commitValue(newValue, newValue);
62
79
  }
63
80
  if (shouldClose) {
64
- setOpen(false);
81
+ setOpen(false, 'ValueSelected');
65
82
  }
66
83
  };
67
84
  const selectTime = (newValue) => {
@@ -104,11 +121,11 @@ export function datePickerStateFactory({ getPlaceholderTime, mergeDateTime, setT
104
121
  timeFormat,
105
122
  timeZone,
106
123
  isOpen,
107
- setOpen(newIsOpen) {
124
+ setOpen(newIsOpen, reason) {
108
125
  if (!newIsOpen && !value && selectedDate && dateFieldState.hasTime) {
109
126
  commitValue(selectedDate, selectedTime || getPlaceholderTime(props.placeholderValue, props.timeZone));
110
127
  }
111
- setOpen(newIsOpen);
128
+ setOpen(newIsOpen, reason);
112
129
  },
113
130
  dateFieldState,
114
131
  };
@@ -1,5 +1,6 @@
1
1
  import { __rest } from "tslib";
2
2
  import React from 'react';
3
+ import { isDateTime } from '@gravity-ui/date-utils';
3
4
  import { useFocusWithin, useForkRef } from '@gravity-ui/uikit';
4
5
  import { useDateFieldProps } from '../../DateField';
5
6
  import { getButtonSizeForInput } from '../../utils/getButtonSizeForInput';
@@ -9,13 +10,31 @@ import { getDateTimeValue } from '../utils';
9
10
  export function useDatePickerProps(state, _a) {
10
11
  var { onFocus, onBlur } = _a, props = __rest(_a, ["onFocus", "onBlur"]);
11
12
  const [isActive, setActive] = React.useState(false);
13
+ const [focusedDate, setFocusedDate] = React.useState(isDateTime(state.dateFieldState.displayValue)
14
+ ? state.dateFieldState.displayValue
15
+ : state.dateFieldState.displayValue.start);
16
+ const [prevDateValue, setPrevDateValue] = React.useState(state.dateFieldState.displayValue);
17
+ if (isDateTime(state.dateFieldState.displayValue)) {
18
+ if (!state.dateFieldState.displayValue.isSame(prevDateValue, 'day')) {
19
+ setPrevDateValue(state.dateFieldState.displayValue);
20
+ setFocusedDate(state.dateFieldState.displayValue);
21
+ }
22
+ }
23
+ else if (!state.dateFieldState.displayValue.start.isSame(prevDateValue.start, 'day')) {
24
+ setPrevDateValue(state.dateFieldState.displayValue);
25
+ setFocusedDate(state.dateFieldState.displayValue.start);
26
+ }
27
+ else if (!state.dateFieldState.displayValue.end.isSame(prevDateValue.end, 'day')) {
28
+ setPrevDateValue(state.dateFieldState.displayValue);
29
+ setFocusedDate(state.dateFieldState.displayValue.end);
30
+ }
12
31
  const { focusWithinProps } = useFocusWithin({
13
32
  onFocusWithin: onFocus,
14
33
  onBlurWithin: onBlur,
15
34
  onFocusWithinChange(isFocusWithin) {
16
35
  setActive(isFocusWithin);
17
36
  if (!isFocusWithin) {
18
- state.setOpen(false);
37
+ state.setOpen(false, 'FocusOut');
19
38
  }
20
39
  },
21
40
  });
@@ -37,7 +56,7 @@ export function useDatePickerProps(state, _a) {
37
56
  if (!onlyTime && e.altKey && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {
38
57
  e.preventDefault();
39
58
  e.stopPropagation();
40
- state.setOpen(true);
59
+ state.setOpen(true, 'ShortcutKeyDown');
41
60
  }
42
61
  } }),
43
62
  fieldProps: mergeProps(inputProps, state.dateFieldState.isEmpty && !isActive && props.placeholder
@@ -55,25 +74,31 @@ export function useDatePickerProps(state, _a) {
55
74
  view: 'flat-secondary',
56
75
  onClick: () => {
57
76
  setActive(true);
58
- state.setOpen(!state.isOpen);
77
+ state.setOpen(!state.isOpen, 'TriggerButtonClick');
59
78
  },
60
79
  },
61
80
  popupProps: {
62
81
  open: state.isOpen,
63
82
  onEscapeKeyDown: () => {
64
- state.setOpen(false);
83
+ state.setOpen(false, 'EscapeKeyDown');
65
84
  focusInput();
66
85
  },
67
86
  onOutsideClick: (e) => {
68
87
  var _a;
69
88
  if (e.target !== calendarButtonRef.current) {
70
- state.setOpen(false);
89
+ state.setOpen(false, 'ClickOutside');
71
90
  }
72
91
  if (e.target && ((_a = groupRef.current) === null || _a === void 0 ? void 0 : _a.contains(e.target))) {
73
92
  focusInput();
74
93
  }
75
94
  },
76
- focusTrap: true,
95
+ onTransitionExited: () => {
96
+ setFocusedDate(isDateTime(state.dateFieldState.displayValue)
97
+ ? state.dateFieldState.displayValue
98
+ : state.dateFieldState.displayValue.start);
99
+ },
100
+ focusTrap: !props.disableFocusTrap,
101
+ disablePortal: props.disablePortal,
77
102
  },
78
103
  calendarProps: {
79
104
  ref: calendarRef,
@@ -92,6 +117,8 @@ export function useDatePickerProps(state, _a) {
92
117
  maxValue: props.maxValue,
93
118
  isDateUnavailable: props.isDateUnavailable,
94
119
  timeZone: state.timeZone,
120
+ focusedValue: focusedDate,
121
+ onFocusUpdate: setFocusedDate,
95
122
  },
96
123
  timeInputProps: {
97
124
  value: state.timeValue,
@@ -35,18 +35,6 @@ export function useRelativeDatePickerProps(state, _a) {
35
35
  if (!state.isActive && isOpen) {
36
36
  setOpen(false);
37
37
  }
38
- const [prevOpen, setPrevOpen] = React.useState(isOpen);
39
- if (isOpen !== prevOpen) {
40
- setPrevOpen(isOpen);
41
- if (!isOpen) {
42
- // FIXME: use popup afterOpenChange instead.
43
- setTimeout(() => {
44
- setFocusedDate(mode === 'relative'
45
- ? relativeDateState.lastCorrectDate
46
- : datePickerState.dateFieldState.displayValue);
47
- }, 200);
48
- }
49
- }
50
38
  const commonInputProps = {
51
39
  onFocus: () => {
52
40
  if (!state.isActive) {
@@ -56,6 +44,17 @@ export function useRelativeDatePickerProps(state, _a) {
56
44
  },
57
45
  errorMessage: props.errorMessage,
58
46
  errorPlacement: props.errorPlacement,
47
+ controlProps: {
48
+ onClick: () => {
49
+ if (state.disabled) {
50
+ return;
51
+ }
52
+ if (!isOpen) {
53
+ state.setActive(true);
54
+ setOpen(true);
55
+ }
56
+ },
57
+ },
59
58
  };
60
59
  const { inputProps } = useDateFieldProps(datePickerState.dateFieldState, Object.assign(Object.assign({}, props), { value: undefined, defaultValue: undefined, onUpdate: undefined }));
61
60
  const { inputProps: relativeDateProps } = useRelativeDateFieldProps(relativeDateState, Object.assign(Object.assign({}, props), { value: undefined, defaultValue: undefined, onUpdate: undefined }));
@@ -142,6 +141,11 @@ export function useRelativeDatePickerProps(state, _a) {
142
141
  setOpen(false);
143
142
  focusInput();
144
143
  },
144
+ onTransitionExited: () => {
145
+ setFocusedDate(mode === 'relative'
146
+ ? relativeDateState.lastCorrectDate
147
+ : datePickerState.dateFieldState.displayValue);
148
+ },
145
149
  },
146
150
  calendarProps: {
147
151
  ref: calendarRef,
@@ -2,6 +2,9 @@
2
2
  position: relative;
3
3
  display: inline-flex;
4
4
  }
5
+ .g-date-relative-range-date-picker__input {
6
+ caret-color: transparent;
7
+ }
5
8
  .g-date-relative-range-date-picker__input_mobile {
6
9
  pointer-events: none;
7
10
  }
@@ -29,10 +29,7 @@ export function RelativeRangeDatePicker(props) {
29
29
  },
30
30
  onBlurWithin: (e) => {
31
31
  var _a;
32
- // when the popup is open and an user clicks outside, focus will be returned to the input
33
- const seemsIsClickOutsideInEmptySpace = open &&
34
- (document.activeElement === null || document.activeElement === document.body);
35
- if (!seemsIsClickOutsideInEmptySpace) {
32
+ if (!open) {
36
33
  setIsActive(false);
37
34
  (_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, e);
38
35
  }
@@ -51,7 +48,7 @@ export function RelativeRangeDatePicker(props) {
51
48
  }), [alwaysShowAsAbsolute, format, getRangeTitle, presetTabs, state.timeZone, state.value]);
52
49
  const validationState = props.validationState || (state.isInvalid ? 'invalid' : undefined);
53
50
  const errorMessage = (_a = props.errorMessage) !== null && _a !== void 0 ? _a : state.errors.join('\n');
54
- return (_jsxs("div", Object.assign({ ref: anchorRef }, focusWithinProps, { className: b(null, props.className), style: props.style, children: [_jsx(TextInput, { autoFocus: props.autoFocus, controlRef: inputRef, value: text, placeholder: props.placeholder, onUpdate: (v) => {
51
+ return (_jsxs("div", Object.assign({ ref: anchorRef }, focusWithinProps, { className: b(null, props.className), style: props.style, children: [_jsx(TextInput, { id: props.id, autoFocus: props.autoFocus, controlRef: inputRef, value: text, placeholder: props.placeholder, onUpdate: (v) => {
55
52
  if (!props.readOnly && !v) {
56
53
  state.setValue(null, 'default');
57
54
  }
@@ -60,6 +57,15 @@ export function RelativeRangeDatePicker(props) {
60
57
  'aria-expanded': open,
61
58
  disabled: isMobile,
62
59
  className: b('input', { mobile: isMobile }),
60
+ onClick: () => {
61
+ if (props.disabled) {
62
+ return;
63
+ }
64
+ if (!open) {
65
+ setIsActive(true);
66
+ setOpen(true);
67
+ }
68
+ },
63
69
  }, onKeyDown: (e) => {
64
70
  if (props.disabled) {
65
71
  return;
@@ -1,5 +1,6 @@
1
1
  .g-date-relative-range-date-picker-dialog__content {
2
2
  --_--popup-content-padding: var(--g-spacing-2);
3
+ --_--g-date-picker-presets-padding: var(--_--popup-content-padding);
3
4
  padding: var(--_--popup-content-padding);
4
5
  }
5
6
  .g-date-relative-range-date-picker-dialog__content_mobile {
@@ -29,4 +30,7 @@
29
30
  margin-block: var(--g-spacing-2) calc(-1 * var(--_--popup-content-padding));
30
31
  margin-inline: calc(-1 * var(--_--popup-content-padding));
31
32
  border-block-start: 1px solid var(--g-color-line-generic);
33
+ }
34
+ .g-date-relative-range-date-picker-dialog__presets {
35
+ margin-inline: calc(-1 * var(--_--popup-content-padding));
32
36
  }
@@ -36,5 +36,8 @@ function DialogContent(props) {
36
36
  props.onApply();
37
37
  }, className: b('apply'), width: "max", children: i18n('Apply') })) : null, props.withPresets && !props.readOnly ? (_jsx(Presets, { size: props.size, presetTabs: props.presetTabs, onChoosePreset: (start, end) => {
38
38
  state.setRange({ type: 'relative', value: start }, { type: 'relative', value: end });
39
- }, minValue: props.minValue, docs: props.docs })) : null, props.withZonesList ? (_jsx("div", { className: b('zone'), children: _jsx(Zones, { value: state.timeZone, onUpdate: state.setTimeZone, disabled: props.readOnly, size: props.size }) })) : null] }));
39
+ if (!props.withApplyButton) {
40
+ props.onApply();
41
+ }
42
+ }, minValue: props.minValue, docs: props.docs, className: b('presets') })) : null, props.withZonesList ? (_jsx("div", { className: b('zone'), children: _jsx(Zones, { value: state.timeZone, onUpdate: state.setTimeZone, disabled: props.readOnly, size: props.size }) })) : null] }));
40
43
  }
@@ -1,8 +1,12 @@
1
+ .g-date-relative-range-date-picker-presets {
2
+ --g-list-item-padding: 0 var(--_--g-date-picker-presets-padding, 0);
3
+ }
1
4
  .g-date-relative-range-date-picker-presets__tabs {
2
5
  --g-tabs-border-width: 0;
3
6
  display: flex;
4
7
  align-items: center;
5
8
  gap: var(--g-spacing-2);
9
+ padding-inline: var(--_--g-date-picker-presets-padding, 0);
6
10
  box-shadow: inset 0 -1px var(--g-color-line-generic);
7
11
  }
8
12
  .g-date-relative-range-date-picker-presets__list-container {
@@ -1,17 +1,18 @@
1
1
  import type { DateTime } from '@gravity-ui/date-utils';
2
2
  import type { Value } from '../../RelativeDatePicker';
3
3
  import type { RangeValue } from '../../types';
4
+ export type RelativeRangeDatePickerValue = RangeValue<Value | null>;
4
5
  export interface RelativeRangeDatePickerState {
5
- value: RangeValue<Value | null> | null;
6
+ value: RelativeRangeDatePickerValue | null;
6
7
  timeZone: string;
7
- setValue: (value: RangeValue<Value | null> | null, timeZone: string) => void;
8
+ setValue: (value: RelativeRangeDatePickerValue | null, timeZone: string) => void;
8
9
  isInvalid: boolean;
9
10
  errors: string[];
10
11
  }
11
12
  export interface RelativeRangeDatePickerStateOptions {
12
- value?: RangeValue<Value | null> | null;
13
- defaultValue?: RangeValue<Value | null>;
14
- onUpdate?: (value: RangeValue<Value | null> | null, timeZone: string) => void;
13
+ value?: RelativeRangeDatePickerValue | null;
14
+ defaultValue?: RelativeRangeDatePickerValue;
15
+ onUpdate?: (value: RelativeRangeDatePickerValue | null, timeZone: string) => void;
15
16
  timeZone?: string;
16
17
  defaultTimeZone?: string;
17
18
  onUpdateTimeZone?: (timeZone: string) => void;
@@ -2,3 +2,7 @@ export * from './RelativeRangeDatePicker';
2
2
  export { PickerDialog as RelativeRangeDatePickerDialog } from './components/PickerDialog/PickerDialog';
3
3
  export type { PickerDialogProps as RelativeRangeDatePickerDialogProps } from './components/PickerDialog/PickerDialog';
4
4
  export * from './hooks/useRelativeRangeDatePickerState';
5
+ export { DEFAULT_DATE_PRESETS, DEFAULT_TIME_PRESETS, DEFAULT_OTHERS_PRESETS, } from './components/Presets/defaultPresets';
6
+ export { getDefaultPresetTabs } from './components/Presets/utils';
7
+ export type { Preset as RelativeRangeDatePickerPreset } from './components/Presets/defaultPresets';
8
+ export type { PresetTab as RelativeRangeDatePickerPresetTab } from './components/Presets/utils';
@@ -1,3 +1,5 @@
1
1
  export * from './RelativeRangeDatePicker';
2
2
  export { PickerDialog as RelativeRangeDatePickerDialog } from './components/PickerDialog/PickerDialog';
3
3
  export * from './hooks/useRelativeRangeDatePickerState';
4
+ export { DEFAULT_DATE_PRESETS, DEFAULT_TIME_PRESETS, DEFAULT_OTHERS_PRESETS, } from './components/Presets/defaultPresets';
5
+ export { getDefaultPresetTabs } from './components/Presets/utils';
@@ -21,7 +21,7 @@ export function getDefaultTitle({ value, timeZone, alwaysShowAsAbsolute, format
21
21
  if (!value) {
22
22
  return '';
23
23
  }
24
- const tz = timeZone === 'system' ? '' : ` (${getTimeZoneOffset(timeZone)})`;
24
+ const tz = timeZone === 'default' ? '' : ` (${getTimeZoneOffset(timeZone)})`;
25
25
  let from = '';
26
26
  if (value.start) {
27
27
  from =
@@ -17,3 +17,11 @@ export interface DateFieldBase<T = DateTime> extends ValueBase<T | null>, InputB
17
17
  */
18
18
  timeZone?: string;
19
19
  }
20
+ export interface PopupTriggerProps<Args extends unknown[] = []> {
21
+ /** Whether the popup is open (controlled). */
22
+ open?: boolean;
23
+ /** Whether the popup is open by default (uncontrolled). */
24
+ defaultOpen?: boolean;
25
+ /** Handler that is called when the popup's open state changes. */
26
+ onOpenChange?: (open: boolean, ...args: Args) => void;
27
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/date-components",
3
- "version": "2.5.0",
3
+ "version": "2.7.0",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "main": "dist/cjs/index.js",
@@ -61,17 +61,19 @@
61
61
  "@gravity-ui/stylelint-config": "^4.0.1",
62
62
  "@gravity-ui/tsconfig": "^1.0.0",
63
63
  "@gravity-ui/uikit": "^6.15.0",
64
- "@storybook/addon-a11y": "^8.1.3",
65
- "@storybook/addon-essentials": "^8.1.3",
66
- "@storybook/addon-viewport": "^8.1.3",
64
+ "@storybook/addon-a11y": "^8.1.10",
65
+ "@storybook/addon-actions": "^8.1.10",
66
+ "@storybook/addon-essentials": "^8.1.10",
67
+ "@storybook/addon-storysource": "^8.1.10",
68
+ "@storybook/addon-viewport": "^8.1.10",
67
69
  "@storybook/addon-webpack5-compiler-babel": "^3.0.3",
68
- "@storybook/blocks": "^8.1.3",
69
- "@storybook/manager-api": "^8.1.3",
70
+ "@storybook/blocks": "^8.1.10",
71
+ "@storybook/manager-api": "^8.1.10",
70
72
  "@storybook/preset-scss": "^1.0.3",
71
- "@storybook/react": "^8.1.3",
72
- "@storybook/react-webpack5": "^8.1.3",
73
- "@storybook/theming": "^8.1.3",
74
- "@storybook/types": "^8.1.3",
73
+ "@storybook/react": "^8.1.10",
74
+ "@storybook/react-webpack5": "^8.1.10",
75
+ "@storybook/theming": "^8.1.10",
76
+ "@storybook/types": "^8.1.10",
75
77
  "@testing-library/dom": "^10.1.0",
76
78
  "@testing-library/jest-dom": "^6.4.6",
77
79
  "@testing-library/react": "^16.0.0",
@@ -98,7 +100,7 @@
98
100
  "react-dom": "^18.3.1",
99
101
  "rimraf": "^5.0.7",
100
102
  "sass": "^1.77.1",
101
- "storybook": "^8.1.3",
103
+ "storybook": "^8.1.10",
102
104
  "stylelint": "^15.11.0",
103
105
  "stylelint-use-logical": "^2.1.2",
104
106
  "ts-jest": "^29.1.5",