@gravity-ui/date-components 2.3.0 → 2.3.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.
@@ -1,11 +1,11 @@
1
1
  import type { DateTime } from '@gravity-ui/date-utils';
2
2
  import type { Value } from '../RelativeDatePicker';
3
- import type { DomProps, InputBase, RangeValue, StyleProps, TextInputProps, Validation } from '../types';
3
+ import type { DomProps, FocusableProps, InputBase, RangeValue, StyleProps, TextInputProps, Validation } from '../types';
4
4
  import type { Preset } from './components/Presets/defaultPresets';
5
5
  import type { PresetTab } from './components/Presets/utils';
6
6
  import type { RelativeRangeDatePickerStateOptions } from './hooks/useRelativeRangeDatePickerState';
7
7
  import './RelativeRangeDatePicker.css';
8
- export interface RelativeRangeDatePickerProps extends RelativeRangeDatePickerStateOptions, DomProps, InputBase, TextInputProps, Validation, StyleProps {
8
+ export interface RelativeRangeDatePickerProps extends RelativeRangeDatePickerStateOptions, DomProps, InputBase, TextInputProps, Validation, FocusableProps, StyleProps {
9
9
  /** Format of the date when rendered in the input. [Available formats](https://day.js.org/docs/en/display/format) */
10
10
  format?: string;
11
11
  /** A placeholder date that controls the default values of each segment when the user first interacts with them. Defaults to today's date at midnight. */
@@ -16,7 +16,7 @@ const utils_1 = require("./utils");
16
16
  require("./RelativeRangeDatePicker.css");
17
17
  const b = (0, cn_1.block)('relative-range-date-picker');
18
18
  function RelativeRangeDatePicker(props) {
19
- var _a, _b;
19
+ var _a;
20
20
  const state = (0, useRelativeRangeDatePickerState_1.useRelativeRangeDatePickerState)(props);
21
21
  const isMobile = (0, uikit_1.useMobile)();
22
22
  const anchorRef = react_1.default.useRef(null);
@@ -24,15 +24,26 @@ function RelativeRangeDatePicker(props) {
24
24
  const [isActive, setIsActive] = react_1.default.useState(false);
25
25
  const [open, setOpen] = react_1.default.useState(false);
26
26
  const { focusWithinProps } = (0, uikit_1.useFocusWithin)({
27
- isDisabled: props.disabled || isMobile,
28
- onFocusWithinChange: (isFocusedWithin) => {
29
- if (!isFocusedWithin) {
27
+ isDisabled: props.disabled,
28
+ onFocusWithin: (e) => {
29
+ var _a;
30
+ if (!isActive) {
31
+ (_a = props.onFocus) === null || _a === void 0 ? void 0 : _a.call(props, e);
32
+ }
33
+ },
34
+ onBlurWithin: (e) => {
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) {
30
40
  setIsActive(false);
41
+ (_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, e);
31
42
  }
32
43
  },
33
44
  });
34
45
  const { alwaysShowAsAbsolute, presetTabs, getRangeTitle } = props;
35
- const format = (_a = props.format) !== null && _a !== void 0 ? _a : 'L';
46
+ const format = props.format || 'L';
36
47
  const text = react_1.default.useMemo(() => typeof getRangeTitle === 'function'
37
48
  ? getRangeTitle(state.value, state.timeZone)
38
49
  : (0, utils_1.getDefaultTitle)({
@@ -43,8 +54,8 @@ function RelativeRangeDatePicker(props) {
43
54
  presets: presetTabs === null || presetTabs === void 0 ? void 0 : presetTabs.flatMap(({ presets }) => presets),
44
55
  }), [alwaysShowAsAbsolute, format, getRangeTitle, presetTabs, state.timeZone, state.value]);
45
56
  const validationState = props.validationState || (state.isInvalid ? 'invalid' : undefined);
46
- const errorMessage = (_b = props.errorMessage) !== null && _b !== void 0 ? _b : state.errors.join('\n');
47
- 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.Popover, { className: b('value-label'), tooltipContentClassName: b('value-label-tooltip'), disabled: isMobile || open || !state.value, delayOpening: 500, placement: ['right', 'right-start', 'right-end', 'auto'], hasArrow: false, content: (0, jsx_runtime_1.jsx)(ValueLabel, { value: state.value, format: format, timeZone: state.timeZone }), children: (0, jsx_runtime_1.jsx)(uikit_1.TextInput, { controlRef: inputRef, value: text, placeholder: props.placeholder, onUpdate: (v) => {
57
+ 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.Popover, { className: b('value-label'), tooltipContentClassName: b('value-label-tooltip'), disabled: isMobile || open || !state.value, delayOpening: 500, placement: ['right', 'right-start', 'right-end', 'auto'], hasArrow: false, content: (0, jsx_runtime_1.jsx)(ValueLabel, { value: state.value, format: format, timeZone: state.timeZone }), children: (0, jsx_runtime_1.jsx)(uikit_1.TextInput, { autoFocus: props.autoFocus, controlRef: inputRef, value: text, placeholder: props.placeholder, onUpdate: (v) => {
48
59
  if (!props.readOnly && !v) {
49
60
  state.setValue(null, 'default');
50
61
  }
@@ -16,7 +16,7 @@ function PickerDialog({ props, state, open, onClose, isMobile, anchorRef, classN
16
16
  if (isMobile) {
17
17
  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 })) }));
18
18
  }
19
- return ((0, jsx_runtime_1.jsx)(uikit_1.Popup, { open: open, onClose: onClose, role: "dialog", anchorRef: anchorRef, contentClassName: b('content', { size: props.size }, className), focusTrap: true, restoreFocus: true, children: (0, jsx_runtime_1.jsx)(DialogContent, Object.assign({}, props, { state: state, onApply: onClose })) }));
19
+ 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
20
  }
21
21
  exports.PickerDialog = PickerDialog;
22
22
  function DialogContent(props) {
@@ -1,11 +1,11 @@
1
1
  import type { DateTime } from '@gravity-ui/date-utils';
2
2
  import type { Value } from '../RelativeDatePicker';
3
- import type { DomProps, InputBase, RangeValue, StyleProps, TextInputProps, Validation } from '../types';
3
+ import type { DomProps, FocusableProps, InputBase, RangeValue, StyleProps, TextInputProps, Validation } from '../types';
4
4
  import type { Preset } from './components/Presets/defaultPresets';
5
5
  import type { PresetTab } from './components/Presets/utils';
6
6
  import type { RelativeRangeDatePickerStateOptions } from './hooks/useRelativeRangeDatePickerState';
7
7
  import './RelativeRangeDatePicker.css';
8
- export interface RelativeRangeDatePickerProps extends RelativeRangeDatePickerStateOptions, DomProps, InputBase, TextInputProps, Validation, StyleProps {
8
+ export interface RelativeRangeDatePickerProps extends RelativeRangeDatePickerStateOptions, DomProps, InputBase, TextInputProps, Validation, FocusableProps, StyleProps {
9
9
  /** Format of the date when rendered in the input. [Available formats](https://day.js.org/docs/en/display/format) */
10
10
  format?: string;
11
11
  /** A placeholder date that controls the default values of each segment when the user first interacts with them. Defaults to today's date at midnight. */
@@ -12,7 +12,7 @@ import { getDefaultTitle } from './utils';
12
12
  import './RelativeRangeDatePicker.css';
13
13
  const b = block('relative-range-date-picker');
14
14
  export function RelativeRangeDatePicker(props) {
15
- var _a, _b;
15
+ var _a;
16
16
  const state = useRelativeRangeDatePickerState(props);
17
17
  const isMobile = useMobile();
18
18
  const anchorRef = React.useRef(null);
@@ -20,15 +20,26 @@ export function RelativeRangeDatePicker(props) {
20
20
  const [isActive, setIsActive] = React.useState(false);
21
21
  const [open, setOpen] = React.useState(false);
22
22
  const { focusWithinProps } = useFocusWithin({
23
- isDisabled: props.disabled || isMobile,
24
- onFocusWithinChange: (isFocusedWithin) => {
25
- if (!isFocusedWithin) {
23
+ isDisabled: props.disabled,
24
+ onFocusWithin: (e) => {
25
+ var _a;
26
+ if (!isActive) {
27
+ (_a = props.onFocus) === null || _a === void 0 ? void 0 : _a.call(props, e);
28
+ }
29
+ },
30
+ onBlurWithin: (e) => {
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) {
26
36
  setIsActive(false);
37
+ (_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, e);
27
38
  }
28
39
  },
29
40
  });
30
41
  const { alwaysShowAsAbsolute, presetTabs, getRangeTitle } = props;
31
- const format = (_a = props.format) !== null && _a !== void 0 ? _a : 'L';
42
+ const format = props.format || 'L';
32
43
  const text = React.useMemo(() => typeof getRangeTitle === 'function'
33
44
  ? getRangeTitle(state.value, state.timeZone)
34
45
  : getDefaultTitle({
@@ -39,8 +50,8 @@ export function RelativeRangeDatePicker(props) {
39
50
  presets: presetTabs === null || presetTabs === void 0 ? void 0 : presetTabs.flatMap(({ presets }) => presets),
40
51
  }), [alwaysShowAsAbsolute, format, getRangeTitle, presetTabs, state.timeZone, state.value]);
41
52
  const validationState = props.validationState || (state.isInvalid ? 'invalid' : undefined);
42
- const errorMessage = (_b = props.errorMessage) !== null && _b !== void 0 ? _b : state.errors.join('\n');
43
- return (_jsxs("div", Object.assign({ ref: anchorRef }, focusWithinProps, { className: b(null, props.className), style: props.style, children: [_jsx(Popover, { className: b('value-label'), tooltipContentClassName: b('value-label-tooltip'), disabled: isMobile || open || !state.value, delayOpening: 500, placement: ['right', 'right-start', 'right-end', 'auto'], hasArrow: false, content: _jsx(ValueLabel, { value: state.value, format: format, timeZone: state.timeZone }), children: _jsx(TextInput, { controlRef: inputRef, value: text, placeholder: props.placeholder, onUpdate: (v) => {
53
+ 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(Popover, { className: b('value-label'), tooltipContentClassName: b('value-label-tooltip'), disabled: isMobile || open || !state.value, delayOpening: 500, placement: ['right', 'right-start', 'right-end', 'auto'], hasArrow: false, content: _jsx(ValueLabel, { value: state.value, format: format, timeZone: state.timeZone }), children: _jsx(TextInput, { autoFocus: props.autoFocus, controlRef: inputRef, value: text, placeholder: props.placeholder, onUpdate: (v) => {
44
55
  if (!props.readOnly && !v) {
45
56
  state.setValue(null, 'default');
46
57
  }
@@ -13,7 +13,7 @@ export function PickerDialog({ props, state, open, onClose, isMobile, anchorRef,
13
13
  if (isMobile) {
14
14
  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 })) }));
15
15
  }
16
- return (_jsx(Popup, { open: open, onClose: onClose, role: "dialog", anchorRef: anchorRef, contentClassName: b('content', { size: props.size }, className), focusTrap: true, restoreFocus: true, children: _jsx(DialogContent, Object.assign({}, props, { state: state, onApply: onClose })) }));
16
+ 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
17
  }
18
18
  function DialogContent(props) {
19
19
  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.3.0",
3
+ "version": "2.3.1",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "main": "dist/cjs/index.js",
@@ -51,36 +51,36 @@
51
51
  "tslib": "^2.6.2"
52
52
  },
53
53
  "devDependencies": {
54
- "@babel/preset-env": "^7.22.9",
55
- "@babel/preset-react": "^7.22.5",
56
- "@babel/preset-typescript": "^7.22.5",
57
- "@commitlint/cli": "^18.4.3",
58
- "@commitlint/config-conventional": "^18.4.3",
59
- "@gravity-ui/eslint-config": "^3.1.1",
54
+ "@babel/preset-env": "^7.24.5",
55
+ "@babel/preset-react": "^7.24.1",
56
+ "@babel/preset-typescript": "^7.24.1",
57
+ "@commitlint/cli": "^19.3.0",
58
+ "@commitlint/config-conventional": "^19.2.2",
59
+ "@gravity-ui/eslint-config": "^3.2.0",
60
60
  "@gravity-ui/prettier-config": "^1.1.0",
61
61
  "@gravity-ui/stylelint-config": "^4.0.1",
62
62
  "@gravity-ui/tsconfig": "^1.0.0",
63
63
  "@gravity-ui/uikit": "^6.1.1",
64
- "@storybook/addon-a11y": "^7.6.15",
65
- "@storybook/addon-essentials": "^7.6.15",
66
- "@storybook/addon-viewport": "^7.6.15",
67
- "@storybook/addons": "^7.6.15",
68
- "@storybook/api": "^7.6.15",
69
- "@storybook/blocks": "^7.6.15",
64
+ "@storybook/addon-a11y": "^8.0.10",
65
+ "@storybook/addon-essentials": "^8.0.10",
66
+ "@storybook/addon-viewport": "^8.0.10",
67
+ "@storybook/addon-webpack5-compiler-babel": "^3.0.3",
68
+ "@storybook/blocks": "^8.0.10",
69
+ "@storybook/manager-api": "^8.0.10",
70
70
  "@storybook/preset-scss": "^1.0.3",
71
- "@storybook/react": "^7.6.15",
72
- "@storybook/react-webpack5": "^7.6.15",
73
- "@storybook/theming": "^7.6.15",
74
- "@testing-library/jest-dom": "^6.4.2",
75
- "@testing-library/react": "^14.2.1",
71
+ "@storybook/react": "^8.0.10",
72
+ "@storybook/react-webpack5": "^8.0.10",
73
+ "@storybook/theming": "^8.0.10",
74
+ "@testing-library/jest-dom": "^6.4.5",
75
+ "@testing-library/react": "^15.0.7",
76
76
  "@testing-library/user-event": "^14.5.2",
77
77
  "@types/jest": "^29.5.12",
78
- "@types/react": "^18.2.55",
79
- "@types/react-dom": "^18.2.19",
80
- "eslint": "^8.56.0",
81
- "eslint-plugin-testing-library": "^6.2.0",
82
- "gulp": "^4.0.2",
83
- "gulp-cli": "^2.3.0",
78
+ "@types/react": "^18.3.2",
79
+ "@types/react-dom": "^18.3.0",
80
+ "eslint": "^8.57.0",
81
+ "eslint-plugin-testing-library": "^6.2.2",
82
+ "gulp": "^5.0.0",
83
+ "gulp-cli": "^3.0.0",
84
84
  "gulp-dart-sass": "^1.1.0",
85
85
  "gulp-replace": "^1.1.4",
86
86
  "gulp-typescript": "^5.0.1",
@@ -91,18 +91,16 @@
91
91
  "nano-staged": "^0.8.0",
92
92
  "npm-run-all": "^4.1.5",
93
93
  "prettier": "^3.2.5",
94
- "react": "^18.2.0",
95
- "react-dom": "^18.2.0",
96
- "rimraf": "5.0.5",
97
- "sass": "^1.70.0",
98
- "sass-loader": "^13.3.3",
99
- "storybook": "^7.6.15",
100
- "style-loader": "^3.3.4",
94
+ "react": "^18.3.1",
95
+ "react-dom": "^18.3.1",
96
+ "rimraf": "^5.0.7",
97
+ "sass": "^1.77.1",
98
+ "storybook": "^8.0.10",
101
99
  "stylelint": "^15.11.0",
102
- "stylelint-use-logical": "^2.1.0",
100
+ "stylelint-use-logical": "^2.1.2",
103
101
  "ts-jest": "^29.1.2",
104
102
  "ts-node": "^10.9.2",
105
- "typescript": "^5.3.3"
103
+ "typescript": "^5.4.5"
106
104
  },
107
105
  "peerDependencies": {
108
106
  "@gravity-ui/uikit": "^6.0.0",