@gravity-ui/dashkit 8.0.2 → 8.1.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.
package/README.md CHANGED
@@ -49,6 +49,7 @@ interface DashKitProps {
49
49
  - **context**: Object that will be propped up on all widgets.
50
50
  - **overlayControls**: Object that overrides widget controls at the time of editing. If not transmitted, basic controls will be displayed.
51
51
  - **noOverlay**: If `true`, overlay and controls are not displayed while editing.
52
+ - **focusable**: If `true`, grid items will be focusable.
52
53
  - **draggableHandleClassName** : СSS class name of the element that makes the widget draggable.
53
54
 
54
55
  ## Usage
@@ -21,6 +21,7 @@ interface DashKitDefaultProps {
21
21
  settings: SettingsProps;
22
22
  context: ContextProps;
23
23
  noOverlay: boolean;
24
+ focusable?: boolean;
24
25
  }
25
26
  export interface DashKitProps extends DashKitGeneralProps, Partial<DashKitDefaultProps> {
26
27
  }
@@ -68,4 +68,5 @@ DashKit.defaultProps = {
68
68
  },
69
69
  context: {},
70
70
  noOverlay: false,
71
+ focusable: false,
71
72
  };
@@ -1,6 +1,9 @@
1
1
  .dashkit-grid-item {
2
2
  position: relative;
3
3
  }
4
+ .dashkit-grid-item:focus {
5
+ outline: none;
6
+ }
4
7
  .dashkit-grid-item__overlay {
5
8
  --_-dashkit-overlay-color: var(--dashkit-overlay-color, var(--g-color-base-generic));
6
9
  --_-dashkit-overlay-border-color: var(--dashkit-overlay-border-color, rgba(0, 0, 0, 0.1));
@@ -56,6 +59,10 @@
56
59
  will-change: transform;
57
60
  }
58
61
 
62
+ .react-grid-item.dashkit-grid-item_is-focused {
63
+ z-index: 2;
64
+ }
65
+
59
66
  .react-grid-item.react-grid-placeholder {
60
67
  --_-dashkit-placeholder-color: var(--dashkit-placeholder-color, #fc0);
61
68
  --_-dashkit-placeholder-opacity: var(--dashkit-placeholder-opacity, 0.2);
@@ -8,7 +8,55 @@ const cn_1 = require("../../utils/cn");
8
8
  const Item_1 = tslib_1.__importDefault(require("../Item/Item"));
9
9
  const OverlayControls_1 = tslib_1.__importDefault(require("../OverlayControls/OverlayControls"));
10
10
  const b = (0, cn_1.cn)('dashkit-grid-item');
11
+ class WindowFocusObserver {
12
+ constructor() {
13
+ this.blurHandler = (e) => {
14
+ if (e.target === window) {
15
+ this.isFocused = false;
16
+ }
17
+ };
18
+ this.focusHandler = (e) => {
19
+ if (e.target === window) {
20
+ this.isFocused = true;
21
+ }
22
+ };
23
+ this.subscribers = 0;
24
+ this.isFocused = !document.hidden;
25
+ window.addEventListener('blur', this.blurHandler, true);
26
+ window.addEventListener('focus', this.focusHandler, true);
27
+ }
28
+ // Method to get state after all blur\focus events in document are triggered
29
+ async getFocuseState() {
30
+ return new Promise((resolve) => {
31
+ requestAnimationFrame(() => {
32
+ resolve(this.isFocused);
33
+ });
34
+ });
35
+ }
36
+ }
37
+ const windowFocusObserver = new WindowFocusObserver();
11
38
  class GridItem extends react_1.default.PureComponent {
39
+ constructor() {
40
+ super(...arguments);
41
+ this.state = {
42
+ isFocused: false,
43
+ };
44
+ this.onFocusHandler = () => {
45
+ this.setState({ isFocused: true });
46
+ if (this.controller) {
47
+ this.controller.abort();
48
+ }
49
+ };
50
+ this.onBlurHandler = () => {
51
+ this.controller = new AbortController();
52
+ windowFocusObserver.getFocuseState().then((isWindowFocused) => {
53
+ if (!this.controller.signal.aborted && isWindowFocused) {
54
+ this.setState({ isFocused: false });
55
+ }
56
+ this.controller = null;
57
+ });
58
+ };
59
+ }
12
60
  renderOverlay() {
13
61
  const { overlayControls } = this.props;
14
62
  const { editMode } = this.context;
@@ -24,7 +72,7 @@ class GridItem extends react_1.default.PureComponent {
24
72
  render() {
25
73
  // из-за бага, что Grid Items unmounts при изменении static, isDraggable, isResaizable
26
74
  // https://github.com/STRML/react-grid-layout/issues/721
27
- const { style, onMouseDown, onMouseUp, onTouchEnd, onTouchStart, children, className, isDragging, noOverlay, withCustomHandle, } = this.props;
75
+ const { style, onMouseDown, onMouseUp, onTouchEnd, onTouchStart, children, className, isDragging, noOverlay, focusable, withCustomHandle, } = this.props;
28
76
  const { editMode } = this.context;
29
77
  const width = Number.parseInt(style.width, 10);
30
78
  const height = Number.parseInt(style.height, 10);
@@ -39,8 +87,15 @@ class GridItem extends react_1.default.PureComponent {
39
87
  const { _editActive } = this.props.item.data;
40
88
  return (react_1.default.createElement("div", Object.assign({ className: b({
41
89
  'is-dragging': isDragging,
90
+ 'is-focused': this.state.isFocused,
42
91
  'with-custom-handle': withCustomHandle,
43
- }, preparedClassName), "data-qa": "dashkit-grid-item", style: style, ref: this.props.forwardedRef }, reactGridLayoutProps),
92
+ }, preparedClassName), "data-qa": "dashkit-grid-item", style: style, ref: this.props.forwardedRef }, reactGridLayoutProps, (focusable
93
+ ? {
94
+ onFocus: this.onFocusHandler,
95
+ onBlur: this.onBlurHandler,
96
+ tabIndex: -1,
97
+ }
98
+ : {})),
44
99
  react_1.default.createElement("div", { className: b('item', { editMode: editMode && !_editActive && !noOverlay }) },
45
100
  react_1.default.createElement(Item_1.default, { id: this.props.id, item: this.props.item, shouldItemUpdate: !isDragging, width: width, height: height, transform: transform, adjustWidgetLayout: this.props.adjustWidgetLayout, layout: this.props.layout, forwardedPluginRef: this.props.forwardedPluginRef })),
46
101
  !noOverlay && this.renderOverlay(),
@@ -61,6 +116,7 @@ GridItem.propTypes = {
61
116
  className: prop_types_1.default.string,
62
117
  style: prop_types_1.default.object,
63
118
  noOverlay: prop_types_1.default.bool,
119
+ focusable: prop_types_1.default.bool,
64
120
  withCustomHandle: prop_types_1.default.bool,
65
121
  onMouseDown: prop_types_1.default.func,
66
122
  onMouseUp: prop_types_1.default.func,
@@ -72,14 +72,14 @@ class GridLayout extends react_1.default.PureComponent {
72
72
  }
73
73
  }
74
74
  render() {
75
- const { layout, config, registerManager, editMode, noOverlay, draggableHandleClassName } = this.context;
75
+ const { layout, config, registerManager, editMode, noOverlay, focusable, draggableHandleClassName, } = this.context;
76
76
  this.pluginsRefs.length = config.items.length;
77
77
  return (react_1.default.createElement(Layout, Object.assign({}, registerManager.gridLayout, { layout: layout, isDraggable: editMode, isResizable: editMode, onDragStart: this._onStart, onDragStop: this._onStop, onResizeStart: this._onStart, onResizeStop: this._onStop }, (draggableHandleClassName
78
78
  ? { draggableHandle: `.${draggableHandleClassName}` }
79
79
  : null), { draggableCancel: `.${constants_1.OVERLAY_CONTROLS_CLASS_NAME}` }), config.items.map((item, i) => {
80
80
  return (react_1.default.createElement(GridItem_1.default, { forwardedPluginRef: (pluginRef) => {
81
81
  this.pluginsRefs[i] = pluginRef;
82
- }, key: item.id, id: item.id, item: item, layout: layout, adjustWidgetLayout: this.adjustWidgetLayout, isDragging: this.state.isDragging, noOverlay: noOverlay, withCustomHandle: Boolean(draggableHandleClassName), overlayControls: this.props.overlayControls }));
82
+ }, key: item.id, id: item.id, item: item, layout: layout, adjustWidgetLayout: this.adjustWidgetLayout, isDragging: this.state.isDragging, noOverlay: noOverlay, focusable: focusable, withCustomHandle: Boolean(draggableHandleClassName), overlayControls: this.props.overlayControls }));
83
83
  })));
84
84
  }
85
85
  }
@@ -129,6 +129,7 @@ function useMemoStateContext(props) {
129
129
  config: props.config,
130
130
  context: props.context,
131
131
  noOverlay: props.noOverlay,
132
+ focusable: props.focusable,
132
133
  defaultGlobalParams: props.globalParams,
133
134
  globalParams: props.globalParams,
134
135
  editMode: props.editMode,
@@ -151,6 +152,7 @@ function useMemoStateContext(props) {
151
152
  props.config,
152
153
  props.context,
153
154
  props.noOverlay,
155
+ props.focusable,
154
156
  props.globalParams,
155
157
  props.editMode,
156
158
  props.settings,
@@ -21,6 +21,7 @@ interface DashKitDefaultProps {
21
21
  settings: SettingsProps;
22
22
  context: ContextProps;
23
23
  noOverlay: boolean;
24
+ focusable?: boolean;
24
25
  }
25
26
  export interface DashKitProps extends DashKitGeneralProps, Partial<DashKitDefaultProps> {
26
27
  }
@@ -63,4 +63,5 @@ DashKit.defaultProps = {
63
63
  },
64
64
  context: {},
65
65
  noOverlay: false,
66
+ focusable: false,
66
67
  };
@@ -1,6 +1,9 @@
1
1
  .dashkit-grid-item {
2
2
  position: relative;
3
3
  }
4
+ .dashkit-grid-item:focus {
5
+ outline: none;
6
+ }
4
7
  .dashkit-grid-item__overlay {
5
8
  --_-dashkit-overlay-color: var(--dashkit-overlay-color, var(--g-color-base-generic));
6
9
  --_-dashkit-overlay-border-color: var(--dashkit-overlay-border-color, rgba(0, 0, 0, 0.1));
@@ -56,6 +59,10 @@
56
59
  will-change: transform;
57
60
  }
58
61
 
62
+ .react-grid-item.dashkit-grid-item_is-focused {
63
+ z-index: 2;
64
+ }
65
+
59
66
  .react-grid-item.react-grid-placeholder {
60
67
  --_-dashkit-placeholder-color: var(--dashkit-placeholder-color, #fc0);
61
68
  --_-dashkit-placeholder-opacity: var(--dashkit-placeholder-opacity, 0.2);
@@ -6,7 +6,55 @@ import Item from '../Item/Item';
6
6
  import OverlayControls from '../OverlayControls/OverlayControls';
7
7
  import './GridItem.css';
8
8
  const b = cn('dashkit-grid-item');
9
+ class WindowFocusObserver {
10
+ constructor() {
11
+ this.blurHandler = (e) => {
12
+ if (e.target === window) {
13
+ this.isFocused = false;
14
+ }
15
+ };
16
+ this.focusHandler = (e) => {
17
+ if (e.target === window) {
18
+ this.isFocused = true;
19
+ }
20
+ };
21
+ this.subscribers = 0;
22
+ this.isFocused = !document.hidden;
23
+ window.addEventListener('blur', this.blurHandler, true);
24
+ window.addEventListener('focus', this.focusHandler, true);
25
+ }
26
+ // Method to get state after all blur\focus events in document are triggered
27
+ async getFocuseState() {
28
+ return new Promise((resolve) => {
29
+ requestAnimationFrame(() => {
30
+ resolve(this.isFocused);
31
+ });
32
+ });
33
+ }
34
+ }
35
+ const windowFocusObserver = new WindowFocusObserver();
9
36
  class GridItem extends React.PureComponent {
37
+ constructor() {
38
+ super(...arguments);
39
+ this.state = {
40
+ isFocused: false,
41
+ };
42
+ this.onFocusHandler = () => {
43
+ this.setState({ isFocused: true });
44
+ if (this.controller) {
45
+ this.controller.abort();
46
+ }
47
+ };
48
+ this.onBlurHandler = () => {
49
+ this.controller = new AbortController();
50
+ windowFocusObserver.getFocuseState().then((isWindowFocused) => {
51
+ if (!this.controller.signal.aborted && isWindowFocused) {
52
+ this.setState({ isFocused: false });
53
+ }
54
+ this.controller = null;
55
+ });
56
+ };
57
+ }
10
58
  renderOverlay() {
11
59
  const { overlayControls } = this.props;
12
60
  const { editMode } = this.context;
@@ -22,7 +70,7 @@ class GridItem extends React.PureComponent {
22
70
  render() {
23
71
  // из-за бага, что Grid Items unmounts при изменении static, isDraggable, isResaizable
24
72
  // https://github.com/STRML/react-grid-layout/issues/721
25
- const { style, onMouseDown, onMouseUp, onTouchEnd, onTouchStart, children, className, isDragging, noOverlay, withCustomHandle, } = this.props;
73
+ const { style, onMouseDown, onMouseUp, onTouchEnd, onTouchStart, children, className, isDragging, noOverlay, focusable, withCustomHandle, } = this.props;
26
74
  const { editMode } = this.context;
27
75
  const width = Number.parseInt(style.width, 10);
28
76
  const height = Number.parseInt(style.height, 10);
@@ -37,8 +85,15 @@ class GridItem extends React.PureComponent {
37
85
  const { _editActive } = this.props.item.data;
38
86
  return (React.createElement("div", Object.assign({ className: b({
39
87
  'is-dragging': isDragging,
88
+ 'is-focused': this.state.isFocused,
40
89
  'with-custom-handle': withCustomHandle,
41
- }, preparedClassName), "data-qa": "dashkit-grid-item", style: style, ref: this.props.forwardedRef }, reactGridLayoutProps),
90
+ }, preparedClassName), "data-qa": "dashkit-grid-item", style: style, ref: this.props.forwardedRef }, reactGridLayoutProps, (focusable
91
+ ? {
92
+ onFocus: this.onFocusHandler,
93
+ onBlur: this.onBlurHandler,
94
+ tabIndex: -1,
95
+ }
96
+ : {})),
42
97
  React.createElement("div", { className: b('item', { editMode: editMode && !_editActive && !noOverlay }) },
43
98
  React.createElement(Item, { id: this.props.id, item: this.props.item, shouldItemUpdate: !isDragging, width: width, height: height, transform: transform, adjustWidgetLayout: this.props.adjustWidgetLayout, layout: this.props.layout, forwardedPluginRef: this.props.forwardedPluginRef })),
44
99
  !noOverlay && this.renderOverlay(),
@@ -59,6 +114,7 @@ GridItem.propTypes = {
59
114
  className: PropTypes.string,
60
115
  style: PropTypes.object,
61
116
  noOverlay: PropTypes.bool,
117
+ focusable: PropTypes.bool,
62
118
  withCustomHandle: PropTypes.bool,
63
119
  onMouseDown: PropTypes.func,
64
120
  onMouseUp: PropTypes.func,
@@ -69,14 +69,14 @@ class GridLayout extends React.PureComponent {
69
69
  }
70
70
  }
71
71
  render() {
72
- const { layout, config, registerManager, editMode, noOverlay, draggableHandleClassName } = this.context;
72
+ const { layout, config, registerManager, editMode, noOverlay, focusable, draggableHandleClassName, } = this.context;
73
73
  this.pluginsRefs.length = config.items.length;
74
74
  return (React.createElement(Layout, Object.assign({}, registerManager.gridLayout, { layout: layout, isDraggable: editMode, isResizable: editMode, onDragStart: this._onStart, onDragStop: this._onStop, onResizeStart: this._onStart, onResizeStop: this._onStop }, (draggableHandleClassName
75
75
  ? { draggableHandle: `.${draggableHandleClassName}` }
76
76
  : null), { draggableCancel: `.${OVERLAY_CONTROLS_CLASS_NAME}` }), config.items.map((item, i) => {
77
77
  return (React.createElement(GridItem, { forwardedPluginRef: (pluginRef) => {
78
78
  this.pluginsRefs[i] = pluginRef;
79
- }, key: item.id, id: item.id, item: item, layout: layout, adjustWidgetLayout: this.adjustWidgetLayout, isDragging: this.state.isDragging, noOverlay: noOverlay, withCustomHandle: Boolean(draggableHandleClassName), overlayControls: this.props.overlayControls }));
79
+ }, key: item.id, id: item.id, item: item, layout: layout, adjustWidgetLayout: this.adjustWidgetLayout, isDragging: this.state.isDragging, noOverlay: noOverlay, focusable: focusable, withCustomHandle: Boolean(draggableHandleClassName), overlayControls: this.props.overlayControls }));
80
80
  })));
81
81
  }
82
82
  }
@@ -125,6 +125,7 @@ function useMemoStateContext(props) {
125
125
  config: props.config,
126
126
  context: props.context,
127
127
  noOverlay: props.noOverlay,
128
+ focusable: props.focusable,
128
129
  defaultGlobalParams: props.globalParams,
129
130
  globalParams: props.globalParams,
130
131
  editMode: props.editMode,
@@ -147,6 +148,7 @@ function useMemoStateContext(props) {
147
148
  props.config,
148
149
  props.context,
149
150
  props.noOverlay,
151
+ props.focusable,
150
152
  props.globalParams,
151
153
  props.editMode,
152
154
  props.settings,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/dashkit",
3
- "version": "8.0.2",
3
+ "version": "8.1.0",
4
4
  "description": "Library for rendering dashboard grid layout",
5
5
  "license": "MIT",
6
6
  "repository": {