@gravity-ui/date-components 2.7.0 → 2.8.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 (19) hide show
  1. package/dist/cjs/components/DatePicker/hooks/useDatePickerProps.js +1 -1
  2. package/dist/cjs/components/RelativeDatePicker/RelativeDatePicker.d.ts +2 -0
  3. package/dist/cjs/components/RelativeDatePicker/RelativeDatePicker.js +2 -1
  4. package/dist/cjs/components/RelativeDatePicker/hooks/useRelativeDatePickerProps.d.ts +3 -1
  5. package/dist/cjs/components/RelativeDatePicker/hooks/useRelativeDatePickerProps.js +10 -3
  6. package/dist/cjs/components/RelativeRangeDatePicker/RelativeRangeDatePicker.d.ts +2 -0
  7. package/dist/cjs/components/RelativeRangeDatePicker/RelativeRangeDatePicker.js +7 -2
  8. package/dist/cjs/components/RelativeRangeDatePicker/components/PickerDialog/PickerDialog.d.ts +2 -1
  9. package/dist/cjs/components/RelativeRangeDatePicker/components/PickerDialog/PickerDialog.js +5 -2
  10. package/dist/esm/components/DatePicker/hooks/useDatePickerProps.js +1 -1
  11. package/dist/esm/components/RelativeDatePicker/RelativeDatePicker.d.ts +2 -0
  12. package/dist/esm/components/RelativeDatePicker/RelativeDatePicker.js +3 -2
  13. package/dist/esm/components/RelativeDatePicker/hooks/useRelativeDatePickerProps.d.ts +3 -1
  14. package/dist/esm/components/RelativeDatePicker/hooks/useRelativeDatePickerProps.js +11 -4
  15. package/dist/esm/components/RelativeRangeDatePicker/RelativeRangeDatePicker.d.ts +2 -0
  16. package/dist/esm/components/RelativeRangeDatePicker/RelativeRangeDatePicker.js +8 -3
  17. package/dist/esm/components/RelativeRangeDatePicker/components/PickerDialog/PickerDialog.d.ts +2 -1
  18. package/dist/esm/components/RelativeRangeDatePicker/components/PickerDialog/PickerDialog.js +5 -2
  19. package/package.json +1 -1
@@ -50,7 +50,7 @@ function useDatePickerProps(state, _a) {
50
50
  function focusInput() {
51
51
  setTimeout(() => {
52
52
  var _a;
53
- (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
53
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus({ preventScroll: true });
54
54
  });
55
55
  }
56
56
  const onlyTime = state.hasTime && !state.hasDate;
@@ -5,5 +5,7 @@ import type { RelativeDatePickerStateOptions } from './hooks/useRelativeDatePick
5
5
  import './RelativeDatePicker.css';
6
6
  export interface RelativeDatePickerProps extends RelativeDatePickerStateOptions, TextInputProps, FocusableProps, KeyboardEvents, DomProps, InputDOMProps, StyleProps, AccessibilityProps {
7
7
  children?: (props: CalendarProps) => React.ReactNode;
8
+ /** Handler that is called when the popup's open state changes. */
9
+ onOpenChange?: (open: boolean) => void;
8
10
  }
9
11
  export declare function RelativeDatePicker(props: RelativeDatePickerProps): import("react/jsx-runtime").JSX.Element;
@@ -22,9 +22,10 @@ function RelativeDatePicker(props) {
22
22
  const state = (0, useRelativeDatePickerState_1.useRelativeDatePickerState)(props);
23
23
  const { groupProps, fieldProps, modeSwitcherProps, calendarButtonProps, popupProps, calendarProps, timeInputProps, } = (0, useRelativeDatePickerProps_1.useRelativeDatePickerProps)(state, props);
24
24
  const anchorRef = react_1.default.useRef(null);
25
+ const handleRef = (0, uikit_1.useForkRef)(anchorRef, groupProps.ref);
25
26
  const isMobile = (0, uikit_1.useMobile)();
26
27
  const isOnlyTime = state.datePickerState.hasTime && !state.datePickerState.hasDate;
27
- return ((0, jsx_runtime_1.jsxs)("div", Object.assign({ ref: anchorRef, className: b(null, props.className) }, groupProps, { children: [isMobile && state.mode === 'absolute' && ((0, jsx_runtime_1.jsx)(MobileCalendar_1.MobileCalendar, { state: state.datePickerState, props: {
28
+ return ((0, jsx_runtime_1.jsxs)("div", Object.assign({}, groupProps, { ref: handleRef, className: b(null, props.className), children: [isMobile && state.mode === 'absolute' && ((0, jsx_runtime_1.jsx)(MobileCalendar_1.MobileCalendar, { state: state.datePickerState, props: {
28
29
  id: props.id,
29
30
  disabled: props.disabled,
30
31
  readOnly: props.readOnly,
@@ -5,7 +5,9 @@ import type { DateFieldProps } from '../../DateField';
5
5
  import type { RelativeDatePickerProps } from '../RelativeDatePicker';
6
6
  import type { RelativeDatePickerState } from './useRelativeDatePickerState';
7
7
  interface InnerRelativeDatePickerProps {
8
- groupProps: React.HTMLAttributes<unknown>;
8
+ groupProps: React.HTMLAttributes<unknown> & {
9
+ ref: React.Ref<HTMLElement>;
10
+ };
9
11
  fieldProps: TextInputProps;
10
12
  modeSwitcherProps: ButtonProps;
11
13
  calendarButtonProps: ButtonProps;
@@ -34,7 +34,7 @@ function useRelativeDatePickerProps(state, _a) {
34
34
  }
35
35
  },
36
36
  });
37
- const [isOpen, setOpen] = react_1.default.useState(false);
37
+ const [isOpen, setOpen] = (0, uikit_1.useControlledState)(undefined, false, props.onOpenChange);
38
38
  if (!state.isActive && isOpen) {
39
39
  setOpen(false);
40
40
  }
@@ -80,11 +80,12 @@ function useRelativeDatePickerProps(state, _a) {
80
80
  function focusInput() {
81
81
  setTimeout(() => {
82
82
  var _a;
83
- (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
83
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus({ preventScroll: true });
84
84
  });
85
85
  }
86
+ const groupRef = react_1.default.useRef(null);
86
87
  return {
87
- groupProps: Object.assign(Object.assign({ tabIndex: -1, role: 'group' }, focusWithinProps), { onKeyDown: (e) => {
88
+ groupProps: Object.assign(Object.assign({ ref: groupRef, tabIndex: -1, role: 'group' }, focusWithinProps), { onKeyDown: (e) => {
88
89
  if (e.altKey && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {
89
90
  e.preventDefault();
90
91
  e.stopPropagation();
@@ -144,6 +145,12 @@ function useRelativeDatePickerProps(state, _a) {
144
145
  setOpen(false);
145
146
  focusInput();
146
147
  },
148
+ onOutsideClick: (e) => {
149
+ var _a;
150
+ if (e.target && !((_a = groupRef.current) === null || _a === void 0 ? void 0 : _a.contains(e.target))) {
151
+ setOpen(false);
152
+ }
153
+ },
147
154
  onTransitionExited: () => {
148
155
  setFocusedDate(mode === 'relative'
149
156
  ? relativeDateState.lastCorrectDate
@@ -26,5 +26,7 @@ export interface RelativeRangeDatePickerProps extends RelativeRangeDatePickerSta
26
26
  getRangeTitle?: (value: RangeValue<Value | null> | null, timeZone: string) => string;
27
27
  /** Sets the CSS className for the popup element. */
28
28
  popupClassName?: string;
29
+ /** Handler that is called when the popup's open state changes. */
30
+ onOpenChange?: (open: boolean) => void;
29
31
  }
30
32
  export declare function RelativeRangeDatePicker(props: RelativeRangeDatePickerProps): import("react/jsx-runtime").JSX.Element;
@@ -22,7 +22,7 @@ function RelativeRangeDatePicker(props) {
22
22
  const anchorRef = react_1.default.useRef(null);
23
23
  const inputRef = react_1.default.useRef(null);
24
24
  const [isActive, setIsActive] = react_1.default.useState(false);
25
- const [open, setOpen] = react_1.default.useState(false);
25
+ const [open, setOpen] = (0, uikit_1.useControlledState)(undefined, false, props.onOpenChange);
26
26
  const { focusWithinProps } = (0, uikit_1.useFocusWithin)({
27
27
  isDisabled: props.disabled,
28
28
  onFocusWithin: (e) => {
@@ -100,7 +100,12 @@ function RelativeRangeDatePicker(props) {
100
100
  state.setValue(v.value, v.timeZone);
101
101
  }, value: { value: state.value, timeZone: state.timeZone }, toStringValue: (v) => v.timeZone, disabled: props.disabled }), (0, jsx_runtime_1.jsx)(PickerDialog_1.PickerDialog, { state: state, props: props, open: open, onClose: () => {
102
102
  setOpen(false);
103
- }, anchorRef: anchorRef, isMobile: isMobile, className: props.className })] })));
103
+ }, focusInput: () => {
104
+ setTimeout(() => {
105
+ var _a;
106
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus({ preventScroll: true });
107
+ });
108
+ }, anchorRef: anchorRef, isMobile: isMobile, className: props.popupClassName })] })));
104
109
  }
105
110
  exports.RelativeRangeDatePicker = RelativeRangeDatePicker;
106
111
  function getNativeValue(value) {
@@ -10,5 +10,6 @@ export interface PickerDialogProps {
10
10
  isMobile?: boolean;
11
11
  anchorRef?: React.RefObject<HTMLElement>;
12
12
  onClose: () => void;
13
+ focusInput: () => void;
13
14
  }
14
- export declare function PickerDialog({ props, state, open, onClose, isMobile, anchorRef, className, }: PickerDialogProps): import("react/jsx-runtime").JSX.Element;
15
+ export declare function PickerDialog({ props, state, open, onClose, focusInput, isMobile, anchorRef, className, }: PickerDialogProps): import("react/jsx-runtime").JSX.Element;
@@ -13,11 +13,14 @@ const i18n_1 = require("./i18n");
13
13
  const useRelativeRangeDatePickerDialogState_1 = require("./useRelativeRangeDatePickerDialogState");
14
14
  require("./PickerDialog.css");
15
15
  const b = (0, cn_1.block)('relative-range-date-picker-dialog');
16
- function PickerDialog({ props, state, open, onClose, isMobile, anchorRef, className, }) {
16
+ function PickerDialog({ props, state, open, onClose, focusInput, isMobile, anchorRef, className, }) {
17
17
  if (isMobile) {
18
18
  return ((0, jsx_runtime_1.jsx)(uikit_1.Sheet, { visible: open, onClose: onClose, contentClassName: b('content', { mobile: true, size: 'xl' }, className), children: (0, jsx_runtime_1.jsx)(DialogContent, Object.assign({}, props, { size: "xl", state: state, onApply: onClose })) }));
19
19
  }
20
- return ((0, jsx_runtime_1.jsx)(uikit_1.Popup, { open: open, onClose: onClose, role: "dialog", anchorRef: anchorRef, contentClassName: b('content', { size: props.size }, className), autoFocus: true, focusTrap: true, restoreFocus: true, children: (0, jsx_runtime_1.jsx)(DialogContent, Object.assign({}, props, { state: state, onApply: onClose })) }));
20
+ return ((0, jsx_runtime_1.jsx)(uikit_1.Popup, { open: open, onEscapeKeyDown: () => {
21
+ onClose();
22
+ focusInput();
23
+ }, onClose: onClose, role: "dialog", anchorRef: anchorRef, contentClassName: b('content', { size: props.size }, className), autoFocus: true, focusTrap: true, children: (0, jsx_runtime_1.jsx)(DialogContent, Object.assign({}, props, { state: state, onApply: onClose })) }));
21
24
  }
22
25
  exports.PickerDialog = PickerDialog;
23
26
  function DialogContent(props) {
@@ -47,7 +47,7 @@ export function useDatePickerProps(state, _a) {
47
47
  function focusInput() {
48
48
  setTimeout(() => {
49
49
  var _a;
50
- (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
50
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus({ preventScroll: true });
51
51
  });
52
52
  }
53
53
  const onlyTime = state.hasTime && !state.hasDate;
@@ -5,5 +5,7 @@ import type { RelativeDatePickerStateOptions } from './hooks/useRelativeDatePick
5
5
  import './RelativeDatePicker.css';
6
6
  export interface RelativeDatePickerProps extends RelativeDatePickerStateOptions, TextInputProps, FocusableProps, KeyboardEvents, DomProps, InputDOMProps, StyleProps, AccessibilityProps {
7
7
  children?: (props: CalendarProps) => React.ReactNode;
8
+ /** Handler that is called when the popup's open state changes. */
9
+ onOpenChange?: (open: boolean) => void;
8
10
  }
9
11
  export declare function RelativeDatePicker(props: RelativeDatePickerProps): import("react/jsx-runtime").JSX.Element;
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import React from 'react';
4
4
  import { Calendar as CalendarIcon, Clock as ClockIcon, Function as FunctionIcon, } from '@gravity-ui/icons';
5
- import { Button, Icon, Popup, TextInput, useMobile } from '@gravity-ui/uikit';
5
+ import { Button, Icon, Popup, TextInput, useForkRef, useMobile } from '@gravity-ui/uikit';
6
6
  import { block } from '../../utils/cn';
7
7
  import { Calendar } from '../Calendar';
8
8
  import { DateField } from '../DateField';
@@ -18,9 +18,10 @@ export function RelativeDatePicker(props) {
18
18
  const state = useRelativeDatePickerState(props);
19
19
  const { groupProps, fieldProps, modeSwitcherProps, calendarButtonProps, popupProps, calendarProps, timeInputProps, } = useRelativeDatePickerProps(state, props);
20
20
  const anchorRef = React.useRef(null);
21
+ const handleRef = useForkRef(anchorRef, groupProps.ref);
21
22
  const isMobile = useMobile();
22
23
  const isOnlyTime = state.datePickerState.hasTime && !state.datePickerState.hasDate;
23
- return (_jsxs("div", Object.assign({ ref: anchorRef, className: b(null, props.className) }, groupProps, { children: [isMobile && state.mode === 'absolute' && (_jsx(MobileCalendar, { state: state.datePickerState, props: {
24
+ return (_jsxs("div", Object.assign({}, groupProps, { ref: handleRef, className: b(null, props.className), children: [isMobile && state.mode === 'absolute' && (_jsx(MobileCalendar, { state: state.datePickerState, props: {
24
25
  id: props.id,
25
26
  disabled: props.disabled,
26
27
  readOnly: props.readOnly,
@@ -5,7 +5,9 @@ import type { DateFieldProps } from '../../DateField';
5
5
  import type { RelativeDatePickerProps } from '../RelativeDatePicker';
6
6
  import type { RelativeDatePickerState } from './useRelativeDatePickerState';
7
7
  interface InnerRelativeDatePickerProps {
8
- groupProps: React.HTMLAttributes<unknown>;
8
+ groupProps: React.HTMLAttributes<unknown> & {
9
+ ref: React.Ref<HTMLElement>;
10
+ };
9
11
  fieldProps: TextInputProps;
10
12
  modeSwitcherProps: ButtonProps;
11
13
  calendarButtonProps: ButtonProps;
@@ -1,6 +1,6 @@
1
1
  import { __rest } from "tslib";
2
2
  import React from 'react';
3
- import { useFocusWithin, useForkRef } from '@gravity-ui/uikit';
3
+ import { useControlledState, useFocusWithin, useForkRef } from '@gravity-ui/uikit';
4
4
  import { useDateFieldProps } from '../../DateField';
5
5
  import { useRelativeDateFieldProps } from '../../RelativeDateField';
6
6
  import { getButtonSizeForInput } from '../../utils/getButtonSizeForInput';
@@ -31,7 +31,7 @@ export function useRelativeDatePickerProps(state, _a) {
31
31
  }
32
32
  },
33
33
  });
34
- const [isOpen, setOpen] = React.useState(false);
34
+ const [isOpen, setOpen] = useControlledState(undefined, false, props.onOpenChange);
35
35
  if (!state.isActive && isOpen) {
36
36
  setOpen(false);
37
37
  }
@@ -77,11 +77,12 @@ export function useRelativeDatePickerProps(state, _a) {
77
77
  function focusInput() {
78
78
  setTimeout(() => {
79
79
  var _a;
80
- (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
80
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus({ preventScroll: true });
81
81
  });
82
82
  }
83
+ const groupRef = React.useRef(null);
83
84
  return {
84
- groupProps: Object.assign(Object.assign({ tabIndex: -1, role: 'group' }, focusWithinProps), { onKeyDown: (e) => {
85
+ groupProps: Object.assign(Object.assign({ ref: groupRef, tabIndex: -1, role: 'group' }, focusWithinProps), { onKeyDown: (e) => {
85
86
  if (e.altKey && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {
86
87
  e.preventDefault();
87
88
  e.stopPropagation();
@@ -141,6 +142,12 @@ export function useRelativeDatePickerProps(state, _a) {
141
142
  setOpen(false);
142
143
  focusInput();
143
144
  },
145
+ onOutsideClick: (e) => {
146
+ var _a;
147
+ if (e.target && !((_a = groupRef.current) === null || _a === void 0 ? void 0 : _a.contains(e.target))) {
148
+ setOpen(false);
149
+ }
150
+ },
144
151
  onTransitionExited: () => {
145
152
  setFocusedDate(mode === 'relative'
146
153
  ? relativeDateState.lastCorrectDate
@@ -26,5 +26,7 @@ export interface RelativeRangeDatePickerProps extends RelativeRangeDatePickerSta
26
26
  getRangeTitle?: (value: RangeValue<Value | null> | null, timeZone: string) => string;
27
27
  /** Sets the CSS className for the popup element. */
28
28
  popupClassName?: string;
29
+ /** Handler that is called when the popup's open state changes. */
30
+ onOpenChange?: (open: boolean) => void;
29
31
  }
30
32
  export declare function RelativeRangeDatePicker(props: RelativeRangeDatePickerProps): import("react/jsx-runtime").JSX.Element;
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import React from 'react';
4
4
  import { Calendar as CalendarIcon } from '@gravity-ui/icons';
5
- import { Button, Icon, TextInput, useFocusWithin, useMobile } from '@gravity-ui/uikit';
5
+ import { Button, Icon, TextInput, useControlledState, useFocusWithin, useMobile, } from '@gravity-ui/uikit';
6
6
  import { block } from '../../utils/cn';
7
7
  import { HiddenInput } from '../HiddenInput/HiddenInput';
8
8
  import { getButtonSizeForInput } from '../utils/getButtonSizeForInput';
@@ -18,7 +18,7 @@ export function RelativeRangeDatePicker(props) {
18
18
  const anchorRef = React.useRef(null);
19
19
  const inputRef = React.useRef(null);
20
20
  const [isActive, setIsActive] = React.useState(false);
21
- const [open, setOpen] = React.useState(false);
21
+ const [open, setOpen] = useControlledState(undefined, false, props.onOpenChange);
22
22
  const { focusWithinProps } = useFocusWithin({
23
23
  isDisabled: props.disabled,
24
24
  onFocusWithin: (e) => {
@@ -96,7 +96,12 @@ export function RelativeRangeDatePicker(props) {
96
96
  state.setValue(v.value, v.timeZone);
97
97
  }, value: { value: state.value, timeZone: state.timeZone }, toStringValue: (v) => v.timeZone, disabled: props.disabled }), _jsx(PickerDialog, { state: state, props: props, open: open, onClose: () => {
98
98
  setOpen(false);
99
- }, anchorRef: anchorRef, isMobile: isMobile, className: props.className })] })));
99
+ }, focusInput: () => {
100
+ setTimeout(() => {
101
+ var _a;
102
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus({ preventScroll: true });
103
+ });
104
+ }, anchorRef: anchorRef, isMobile: isMobile, className: props.popupClassName })] })));
100
105
  }
101
106
  function getNativeValue(value) {
102
107
  if (!value) {
@@ -10,5 +10,6 @@ export interface PickerDialogProps {
10
10
  isMobile?: boolean;
11
11
  anchorRef?: React.RefObject<HTMLElement>;
12
12
  onClose: () => void;
13
+ focusInput: () => void;
13
14
  }
14
- export declare function PickerDialog({ props, state, open, onClose, isMobile, anchorRef, className, }: PickerDialogProps): import("react/jsx-runtime").JSX.Element;
15
+ export declare function PickerDialog({ props, state, open, onClose, focusInput, isMobile, anchorRef, className, }: PickerDialogProps): import("react/jsx-runtime").JSX.Element;
@@ -10,11 +10,14 @@ import { i18n } from './i18n';
10
10
  import { useRelativeRangeDatePickerDialogState } from './useRelativeRangeDatePickerDialogState';
11
11
  import './PickerDialog.css';
12
12
  const b = block('relative-range-date-picker-dialog');
13
- export function PickerDialog({ props, state, open, onClose, isMobile, anchorRef, className, }) {
13
+ export function PickerDialog({ props, state, open, onClose, focusInput, isMobile, anchorRef, className, }) {
14
14
  if (isMobile) {
15
15
  return (_jsx(Sheet, { visible: open, onClose: onClose, contentClassName: b('content', { mobile: true, size: 'xl' }, className), children: _jsx(DialogContent, Object.assign({}, props, { size: "xl", state: state, onApply: onClose })) }));
16
16
  }
17
- return (_jsx(Popup, { open: open, onClose: onClose, role: "dialog", anchorRef: anchorRef, contentClassName: b('content', { size: props.size }, className), autoFocus: true, focusTrap: true, restoreFocus: true, children: _jsx(DialogContent, Object.assign({}, props, { state: state, onApply: onClose })) }));
17
+ return (_jsx(Popup, { open: open, onEscapeKeyDown: () => {
18
+ onClose();
19
+ focusInput();
20
+ }, onClose: onClose, role: "dialog", anchorRef: anchorRef, contentClassName: b('content', { size: props.size }, className), autoFocus: true, focusTrap: true, children: _jsx(DialogContent, Object.assign({}, props, { state: state, onApply: onClose })) }));
18
21
  }
19
22
  function DialogContent(props) {
20
23
  var _a, _b, _c, _d, _e, _f, _g;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/date-components",
3
- "version": "2.7.0",
3
+ "version": "2.8.0",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "main": "dist/cjs/index.js",