@gravity-ui/dashkit 8.5.0-beta.3 → 8.5.0-beta.4

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.
@@ -9,6 +9,10 @@ export default class GridLayout extends React.PureComponent<any, any, any> {
9
9
  componentDidMount(): void;
10
10
  componentDidUpdate(): void;
11
11
  componentWillUnmount(): void;
12
+ _memoCallbacksForGroups: {};
13
+ _memoGroupsProps: {};
14
+ _memoGroupsLayouts: {};
15
+ _memoForwardedPluginRef: any[];
12
16
  _timeout: any;
13
17
  _lastReloadAt: any;
14
18
  onVisibilityChange: () => void;
@@ -19,11 +23,22 @@ export default class GridLayout extends React.PureComponent<any, any, any> {
19
23
  }) => void;
20
24
  getItemsMeta: () => any;
21
25
  getActiveLayout(): any;
26
+ getMemoGroupLayout: (group: any, layout: any) => any;
27
+ getMemoGroupCallbacks: (group: any) => any;
28
+ getMemoGroupProps: (group: any, renderLayout: any, properties: any) => {
29
+ layout: any;
30
+ callbacks: any;
31
+ };
32
+ getLayoutAndPropsByGroup: (group: any) => {
33
+ properties: any;
34
+ layout: any;
35
+ };
36
+ getMemoForwardRefCallback: (refIndex: any) => any;
22
37
  mergeGroupsLayout(group: any, newLayout: any, temporaryItem: any): any;
23
38
  reloadItems(): void;
24
39
  _onStart: () => void;
25
40
  _onStop: (group: any, newLayout: any) => void;
26
- _onDropDragOver: (e: any, gridProperties: any, layout: any) => any;
41
+ _onDropDragOver: (group: any, e: any) => any;
27
42
  _onDrop: (group: any, newLayout: any, item: any, e: any) => false | undefined;
28
43
  renderTemporaryPlaceholder(): JSX.Element | null;
29
44
  renderGroup(group: any, renderLayout: any, renderItems: any, offset: number | undefined, groupGridProperties: any): JSX.Element;
@@ -9,6 +9,10 @@ const ReactGridLayout_1 = require("./ReactGridLayout");
9
9
  class GridLayout extends react_1.default.PureComponent {
10
10
  constructor(props, context) {
11
11
  super(props, context);
12
+ this._memoForwardedPluginRef = [];
13
+ this._memoGroupsProps = {};
14
+ this._memoGroupsLayouts = {};
15
+ this._memoCallbacksForGroups = {};
12
16
  this.onVisibilityChange = () => {
13
17
  this.setState({
14
18
  isPageHidden: document.hidden,
@@ -30,6 +34,65 @@ class GridLayout extends react_1.default.PureComponent {
30
34
  const { getItemsMeta } = this.context;
31
35
  return getItemsMeta(this.pluginsRefs);
32
36
  };
37
+ this.getMemoGroupLayout = (group, layout) => {
38
+ // fastest possible way to match json
39
+ const key = JSON.stringify(layout);
40
+ if (this._memoGroupsLayouts[group]) {
41
+ if (this._memoGroupsLayouts[group].key === key) {
42
+ return this._memoGroupsLayouts[group];
43
+ }
44
+ else {
45
+ this._memoGroupsLayouts[group].key = key;
46
+ this._memoGroupsLayouts[group].layout = layout;
47
+ }
48
+ }
49
+ else {
50
+ this._memoGroupsLayouts[group] = {
51
+ key,
52
+ layout,
53
+ };
54
+ }
55
+ return this._memoGroupsLayouts[group];
56
+ };
57
+ this.getMemoGroupCallbacks = (group) => {
58
+ if (!this._memoCallbacksForGroups[group]) {
59
+ const onStart = this._onStart;
60
+ const onStop = this._onStop.bind(this, group);
61
+ const onDrop = this._onDrop.bind(this, group);
62
+ const onDropDragOver = this._onDropDragOver.bind(this, group);
63
+ this._memoCallbacksForGroups[group] = {
64
+ onDragStart: onStart,
65
+ onResizeStart: onStart,
66
+ onDragStop: onStop,
67
+ onResizeStop: onStop,
68
+ onDrop: onDrop,
69
+ onDropDragOver,
70
+ };
71
+ }
72
+ return this._memoCallbacksForGroups[group];
73
+ };
74
+ this.getMemoGroupProps = (group, renderLayout, properties) => {
75
+ // Needed for _onDropDragOver
76
+ this._memoGroupsProps[group] = properties;
77
+ return {
78
+ layout: this.getMemoGroupLayout(group, renderLayout).layout,
79
+ callbacks: this.getMemoGroupCallbacks(group),
80
+ };
81
+ };
82
+ this.getLayoutAndPropsByGroup = (group) => {
83
+ return {
84
+ properties: this._memoGroupsProps[group],
85
+ layout: this._memoGroupsLayouts[group].layout,
86
+ };
87
+ };
88
+ this.getMemoForwardRefCallback = (refIndex) => {
89
+ if (!this._memoForwardedPluginRef[refIndex]) {
90
+ this._memoForwardedPluginRef[refIndex] = (pluginRef) => {
91
+ this.pluginsRefs[refIndex] = pluginRef;
92
+ };
93
+ }
94
+ return this._memoForwardedPluginRef[refIndex];
95
+ };
33
96
  this._onStart = () => {
34
97
  if (this.temporaryLayout)
35
98
  return;
@@ -46,12 +109,13 @@ class GridLayout extends react_1.default.PureComponent {
46
109
  }
47
110
  this.setState({ isDragging: false });
48
111
  };
49
- this._onDropDragOver = (e, gridProperties, layout) => {
50
- const { editMode, dragOverPlugin, onDropDragOver } = this.context;
112
+ this._onDropDragOver = (group, e) => {
113
+ const { editMode, dragOverPlugin } = this.context;
51
114
  if (!editMode || !dragOverPlugin) {
52
115
  return false;
53
116
  }
54
- return onDropDragOver(e, gridProperties, layout);
117
+ const { properties, layout } = this.getLayoutAndPropsByGroup(group);
118
+ return this.context.onDropDragOver(e, properties, layout);
55
119
  };
56
120
  this._onDrop = (group, newLayout, item, e) => {
57
121
  if (!item) {
@@ -84,6 +148,10 @@ class GridLayout extends react_1.default.PureComponent {
84
148
  componentWillUnmount() {
85
149
  clearTimeout(this._timeout);
86
150
  document.removeEventListener('visibilitychange', this.onVisibilityChange);
151
+ this._memoCallbacksForGroups = {};
152
+ this._memoGroupsProps = {};
153
+ this._memoGroupsLayouts = {};
154
+ this._memoForwardedPluginRef = [];
87
155
  }
88
156
  getActiveLayout() {
89
157
  const { layout, temporaryLayout } = this.context;
@@ -140,19 +208,18 @@ class GridLayout extends react_1.default.PureComponent {
140
208
  if (compactType === constants_1.COMPACT_TYPE_HORIZONTAL_NOWRAP) {
141
209
  compactType = 'horizontal';
142
210
  }
143
- return (react_1.default.createElement(ReactGridLayout_1.Layout, Object.assign({}, properties, { compactType: compactType, layout: renderLayout, key: `group_${group}`, isDraggable: editMode, isResizable: editMode, onDragStart: this._onStart, onDragStop: (...args) => this._onStop(group, ...args), onResizeStart: this._onStart, onResizeStop: (...args) => this._onStop(group, ...args) }, (draggableHandleClassName
211
+ const { callbacks, layout } = this.getMemoGroupProps(group, renderLayout, properties);
212
+ return (react_1.default.createElement(ReactGridLayout_1.Layout, Object.assign({}, properties, { compactType: compactType, layout: layout, key: `group_${group}`, isDraggable: editMode, isResizable: editMode, onDragStart: callbacks.onDragStart, onDragStop: callbacks.onDragStop, onResizeStart: callbacks.onResizeStart, onResizeStop: callbacks.onResizeStop }, (draggableHandleClassName
144
213
  ? { draggableHandle: `.${draggableHandleClassName}` }
145
214
  : null), (outerDnDEnable
146
215
  ? {
147
216
  isDroppable: true,
148
- onDropDragOver: (e) => this._onDropDragOver(e, properties, renderLayout),
149
- onDrop: (...args) => this._onDrop(group, ...args),
217
+ onDropDragOver: callbacks.onDropDragOver,
218
+ onDrop: callbacks.onDrop,
150
219
  }
151
220
  : null), { draggableCancel: `.${constants_1.OVERLAY_CONTROLS_CLASS_NAME}` }),
152
221
  renderItems.map((item, i) => {
153
- return (react_1.default.createElement(GridItem_1.default, { forwardedPluginRef: (pluginRef) => {
154
- this.pluginsRefs[offset + i] = pluginRef;
155
- }, key: item.id, id: item.id, item: item, layout: renderLayout, adjustWidgetLayout: this.adjustWidgetLayout, isDragging: this.state.isDragging, noOverlay: noOverlay, focusable: focusable, withCustomHandle: Boolean(draggableHandleClassName), overlayControls: this.props.overlayControls }));
222
+ return (react_1.default.createElement(GridItem_1.default, { forwardedPluginRef: this.getMemoForwardRefCallback(offset + i), 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 }));
156
223
  }),
157
224
  this.renderTemporaryPlaceholder()));
158
225
  }
@@ -2,6 +2,8 @@ declare const _default: {
2
2
  new (props: any): {
3
3
  shouldComponentUpdate(nextProps: any): boolean;
4
4
  _onStateAndParamsChange: (stateAndParams: any, options: any) => void;
5
+ _currentRenderProps: {};
6
+ getRenderProps: () => {};
5
7
  render(): JSX.Element;
6
8
  context: unknown;
7
9
  setState<K extends string | number | symbol>(state: any, callback?: (() => void) | undefined): void;
@@ -26,6 +28,8 @@ declare const _default: {
26
28
  new (props: any, context: any): {
27
29
  shouldComponentUpdate(nextProps: any): boolean;
28
30
  _onStateAndParamsChange: (stateAndParams: any, options: any) => void;
31
+ _currentRenderProps: {};
32
+ getRenderProps: () => {};
29
33
  render(): JSX.Element;
30
34
  context: unknown;
31
35
  setState<K extends string | number | symbol>(state: any, callback?: (() => void) | undefined): void;
@@ -2,6 +2,8 @@ export function prepareItem(Component: any): {
2
2
  new (props: any): {
3
3
  shouldComponentUpdate(nextProps: any): boolean;
4
4
  _onStateAndParamsChange: (stateAndParams: any, options: any) => void;
5
+ _currentRenderProps: {};
6
+ getRenderProps: () => {};
5
7
  render(): JSX.Element;
6
8
  context: unknown;
7
9
  setState<K extends string | number | symbol>(state: any, callback?: (() => void) | undefined): void;
@@ -26,6 +28,8 @@ export function prepareItem(Component: any): {
26
28
  new (props: any, context: any): {
27
29
  shouldComponentUpdate(nextProps: any): boolean;
28
30
  _onStateAndParamsChange: (stateAndParams: any, options: any) => void;
31
+ _currentRenderProps: {};
32
+ getRenderProps: () => {};
29
33
  render(): JSX.Element;
30
34
  context: unknown;
31
35
  setState<K extends string | number | symbol>(state: any, callback?: (() => void) | undefined): void;
@@ -13,6 +13,35 @@ function prepareItem(Component) {
13
13
  this._onStateAndParamsChange = (stateAndParams, options) => {
14
14
  this.context.onItemStateAndParamsChange(this.props.id, stateAndParams, options);
15
15
  };
16
+ this._currentRenderProps = {};
17
+ this.getRenderProps = () => {
18
+ const { id, width, height, item, adjustWidgetLayout, layout, isPlaceholder } = this.props;
19
+ const { itemsState, itemsParams, registerManager, settings, context, editMode } = this.context;
20
+ const { data, defaults, namespace } = item;
21
+ const rendererProps = {
22
+ data,
23
+ editMode,
24
+ params: itemsParams[id],
25
+ state: itemsState[id],
26
+ onStateAndParamsChange: this._onStateAndParamsChange,
27
+ width,
28
+ height,
29
+ id,
30
+ defaults,
31
+ namespace,
32
+ settings,
33
+ context,
34
+ layout,
35
+ gridLayout: registerManager.gridLayout,
36
+ adjustWidgetLayout,
37
+ isPlaceholder,
38
+ };
39
+ const changedProp = Object.entries(rendererProps).find(([key, value]) => this._currentRenderProps[key] !== value);
40
+ if (changedProp) {
41
+ this._currentRenderProps = rendererProps;
42
+ }
43
+ return this._currentRenderProps;
44
+ };
16
45
  }
17
46
  shouldComponentUpdate(nextProps) {
18
47
  const { width, height, transform } = this.props;
@@ -26,28 +55,10 @@ function prepareItem(Component) {
26
55
  return true;
27
56
  }
28
57
  render() {
29
- const { id, width, height, item, adjustWidgetLayout, layout, isPlaceholder } = this.props;
30
- const { itemsState, itemsParams, registerManager, settings, context, editMode } = this.context;
31
- const { type, data, defaults, namespace } = item;
32
- const rendererProps = {
33
- data,
34
- editMode,
35
- params: itemsParams[id],
36
- state: itemsState[id],
37
- onStateAndParamsChange: this._onStateAndParamsChange,
38
- width,
39
- height,
40
- id,
41
- defaults,
42
- namespace,
43
- settings,
44
- context,
45
- layout,
46
- gridLayout: registerManager.gridLayout,
47
- adjustWidgetLayout,
48
- isPlaceholder,
49
- };
50
- return (react_1.default.createElement(Component, { forwardedPluginRef: this.props.forwardedPluginRef, rendererProps: rendererProps, registerManager: registerManager, type: type, isPlaceholder: isPlaceholder }));
58
+ const { item, isPlaceholder } = this.props;
59
+ const { registerManager } = this.context;
60
+ const { type } = item;
61
+ return (react_1.default.createElement(Component, { forwardedPluginRef: this.props.forwardedPluginRef, rendererProps: this.getRenderProps(), registerManager: registerManager, type: type, isPlaceholder: isPlaceholder }));
51
62
  }
52
63
  },
53
64
  _a.propTypes = {
@@ -7,6 +7,7 @@ const isEqual_1 = tslib_1.__importDefault(require("lodash/isEqual"));
7
7
  const pick_1 = tslib_1.__importDefault(require("lodash/pick"));
8
8
  const common_1 = require("../constants/common");
9
9
  const DashKitContext_1 = require("../context/DashKitContext");
10
+ const useDeepEqualMemo_1 = require("../hooks/useDeepEqualMemo");
10
11
  const shared_1 = require("../shared");
11
12
  const utils_1 = require("../utils");
12
13
  const ITEM_PROPS = ['i', 'h', 'w', 'x', 'y', 'parent'];
@@ -154,10 +155,10 @@ function useMemoStateContext(props) {
154
155
  }
155
156
  else {
156
157
  adjustedLayouts.current[widgetId] = Object.assign({}, adjustedItem);
157
- originalLayouts.current[widgetId] = item;
158
158
  }
159
159
  delete nowrapAdjustedLayouts.current[widgetId];
160
160
  }
161
+ originalLayouts.current[widgetId] = item;
161
162
  });
162
163
  Object.entries(nowrapGroups).forEach(([, { items, leftSpace }]) => {
163
164
  items.forEach((item) => {
@@ -172,9 +173,17 @@ function useMemoStateContext(props) {
172
173
  });
173
174
  });
174
175
  }
176
+ else if (groups) {
177
+ layout.forEach((item) => {
178
+ const widgetId = item.i;
179
+ if (adjustedLayouts.current[widgetId]) {
180
+ originalLayouts.current[widgetId] = item;
181
+ }
182
+ });
183
+ }
175
184
  return adjustedLayouts.current;
176
185
  }, [props.registerManager]);
177
- const itemsParams = react_1.default.useMemo(() => (0, shared_1.getItemsParams)({
186
+ const itemsParams = (0, useDeepEqualMemo_1.useDeepEqualMemo)(() => (0, shared_1.getItemsParams)({
178
187
  defaultGlobalParams: props.defaultGlobalParams,
179
188
  globalParams: props.globalParams,
180
189
  config: props.config,
@@ -187,7 +196,7 @@ function useMemoStateContext(props) {
187
196
  props.itemsStateAndParams,
188
197
  props.registerManager,
189
198
  ]);
190
- const itemsState = react_1.default.useMemo(() => (0, shared_1.getItemsState)({
199
+ const itemsState = (0, useDeepEqualMemo_1.useDeepEqualMemo)(() => (0, shared_1.getItemsState)({
191
200
  config: props.config,
192
201
  itemsStateAndParams: props.itemsStateAndParams,
193
202
  }), [props.config, props.itemsStateAndParams]);
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const useDeepEqualMemo: <T extends unknown>(predicate: () => T, deps: React.DependencyList) => T;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useDeepEqualMemo = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importDefault(require("react"));
6
+ const isEqual_1 = tslib_1.__importDefault(require("lodash/isEqual"));
7
+ const useDeepEqualMemo = (predicate, deps) => {
8
+ const previousValueRef = react_1.default.useRef({});
9
+ const callbackRef = react_1.default.useRef(predicate);
10
+ callbackRef.current = predicate;
11
+ return react_1.default.useMemo(() => {
12
+ const value = predicate();
13
+ if (!(0, isEqual_1.default)(previousValueRef.current, value)) {
14
+ previousValueRef.current = value;
15
+ }
16
+ return previousValueRef.current;
17
+ }, [callbackRef, previousValueRef, ...deps]);
18
+ };
19
+ exports.useDeepEqualMemo = useDeepEqualMemo;
@@ -9,6 +9,10 @@ export default class GridLayout extends React.PureComponent<any, any, any> {
9
9
  componentDidMount(): void;
10
10
  componentDidUpdate(): void;
11
11
  componentWillUnmount(): void;
12
+ _memoCallbacksForGroups: {};
13
+ _memoGroupsProps: {};
14
+ _memoGroupsLayouts: {};
15
+ _memoForwardedPluginRef: any[];
12
16
  _timeout: any;
13
17
  _lastReloadAt: any;
14
18
  onVisibilityChange: () => void;
@@ -19,11 +23,22 @@ export default class GridLayout extends React.PureComponent<any, any, any> {
19
23
  }) => void;
20
24
  getItemsMeta: () => any;
21
25
  getActiveLayout(): any;
26
+ getMemoGroupLayout: (group: any, layout: any) => any;
27
+ getMemoGroupCallbacks: (group: any) => any;
28
+ getMemoGroupProps: (group: any, renderLayout: any, properties: any) => {
29
+ layout: any;
30
+ callbacks: any;
31
+ };
32
+ getLayoutAndPropsByGroup: (group: any) => {
33
+ properties: any;
34
+ layout: any;
35
+ };
36
+ getMemoForwardRefCallback: (refIndex: any) => any;
22
37
  mergeGroupsLayout(group: any, newLayout: any, temporaryItem: any): any;
23
38
  reloadItems(): void;
24
39
  _onStart: () => void;
25
40
  _onStop: (group: any, newLayout: any) => void;
26
- _onDropDragOver: (e: any, gridProperties: any, layout: any) => any;
41
+ _onDropDragOver: (group: any, e: any) => any;
27
42
  _onDrop: (group: any, newLayout: any, item: any, e: any) => false | undefined;
28
43
  renderTemporaryPlaceholder(): JSX.Element | null;
29
44
  renderGroup(group: any, renderLayout: any, renderItems: any, offset: number | undefined, groupGridProperties: any): JSX.Element;
@@ -6,6 +6,10 @@ import { Layout } from './ReactGridLayout';
6
6
  class GridLayout extends React.PureComponent {
7
7
  constructor(props, context) {
8
8
  super(props, context);
9
+ this._memoForwardedPluginRef = [];
10
+ this._memoGroupsProps = {};
11
+ this._memoGroupsLayouts = {};
12
+ this._memoCallbacksForGroups = {};
9
13
  this.onVisibilityChange = () => {
10
14
  this.setState({
11
15
  isPageHidden: document.hidden,
@@ -27,6 +31,65 @@ class GridLayout extends React.PureComponent {
27
31
  const { getItemsMeta } = this.context;
28
32
  return getItemsMeta(this.pluginsRefs);
29
33
  };
34
+ this.getMemoGroupLayout = (group, layout) => {
35
+ // fastest possible way to match json
36
+ const key = JSON.stringify(layout);
37
+ if (this._memoGroupsLayouts[group]) {
38
+ if (this._memoGroupsLayouts[group].key === key) {
39
+ return this._memoGroupsLayouts[group];
40
+ }
41
+ else {
42
+ this._memoGroupsLayouts[group].key = key;
43
+ this._memoGroupsLayouts[group].layout = layout;
44
+ }
45
+ }
46
+ else {
47
+ this._memoGroupsLayouts[group] = {
48
+ key,
49
+ layout,
50
+ };
51
+ }
52
+ return this._memoGroupsLayouts[group];
53
+ };
54
+ this.getMemoGroupCallbacks = (group) => {
55
+ if (!this._memoCallbacksForGroups[group]) {
56
+ const onStart = this._onStart;
57
+ const onStop = this._onStop.bind(this, group);
58
+ const onDrop = this._onDrop.bind(this, group);
59
+ const onDropDragOver = this._onDropDragOver.bind(this, group);
60
+ this._memoCallbacksForGroups[group] = {
61
+ onDragStart: onStart,
62
+ onResizeStart: onStart,
63
+ onDragStop: onStop,
64
+ onResizeStop: onStop,
65
+ onDrop: onDrop,
66
+ onDropDragOver,
67
+ };
68
+ }
69
+ return this._memoCallbacksForGroups[group];
70
+ };
71
+ this.getMemoGroupProps = (group, renderLayout, properties) => {
72
+ // Needed for _onDropDragOver
73
+ this._memoGroupsProps[group] = properties;
74
+ return {
75
+ layout: this.getMemoGroupLayout(group, renderLayout).layout,
76
+ callbacks: this.getMemoGroupCallbacks(group),
77
+ };
78
+ };
79
+ this.getLayoutAndPropsByGroup = (group) => {
80
+ return {
81
+ properties: this._memoGroupsProps[group],
82
+ layout: this._memoGroupsLayouts[group].layout,
83
+ };
84
+ };
85
+ this.getMemoForwardRefCallback = (refIndex) => {
86
+ if (!this._memoForwardedPluginRef[refIndex]) {
87
+ this._memoForwardedPluginRef[refIndex] = (pluginRef) => {
88
+ this.pluginsRefs[refIndex] = pluginRef;
89
+ };
90
+ }
91
+ return this._memoForwardedPluginRef[refIndex];
92
+ };
30
93
  this._onStart = () => {
31
94
  if (this.temporaryLayout)
32
95
  return;
@@ -43,12 +106,13 @@ class GridLayout extends React.PureComponent {
43
106
  }
44
107
  this.setState({ isDragging: false });
45
108
  };
46
- this._onDropDragOver = (e, gridProperties, layout) => {
47
- const { editMode, dragOverPlugin, onDropDragOver } = this.context;
109
+ this._onDropDragOver = (group, e) => {
110
+ const { editMode, dragOverPlugin } = this.context;
48
111
  if (!editMode || !dragOverPlugin) {
49
112
  return false;
50
113
  }
51
- return onDropDragOver(e, gridProperties, layout);
114
+ const { properties, layout } = this.getLayoutAndPropsByGroup(group);
115
+ return this.context.onDropDragOver(e, properties, layout);
52
116
  };
53
117
  this._onDrop = (group, newLayout, item, e) => {
54
118
  if (!item) {
@@ -81,6 +145,10 @@ class GridLayout extends React.PureComponent {
81
145
  componentWillUnmount() {
82
146
  clearTimeout(this._timeout);
83
147
  document.removeEventListener('visibilitychange', this.onVisibilityChange);
148
+ this._memoCallbacksForGroups = {};
149
+ this._memoGroupsProps = {};
150
+ this._memoGroupsLayouts = {};
151
+ this._memoForwardedPluginRef = [];
84
152
  }
85
153
  getActiveLayout() {
86
154
  const { layout, temporaryLayout } = this.context;
@@ -137,19 +205,18 @@ class GridLayout extends React.PureComponent {
137
205
  if (compactType === COMPACT_TYPE_HORIZONTAL_NOWRAP) {
138
206
  compactType = 'horizontal';
139
207
  }
140
- return (React.createElement(Layout, Object.assign({}, properties, { compactType: compactType, layout: renderLayout, key: `group_${group}`, isDraggable: editMode, isResizable: editMode, onDragStart: this._onStart, onDragStop: (...args) => this._onStop(group, ...args), onResizeStart: this._onStart, onResizeStop: (...args) => this._onStop(group, ...args) }, (draggableHandleClassName
208
+ const { callbacks, layout } = this.getMemoGroupProps(group, renderLayout, properties);
209
+ return (React.createElement(Layout, Object.assign({}, properties, { compactType: compactType, layout: layout, key: `group_${group}`, isDraggable: editMode, isResizable: editMode, onDragStart: callbacks.onDragStart, onDragStop: callbacks.onDragStop, onResizeStart: callbacks.onResizeStart, onResizeStop: callbacks.onResizeStop }, (draggableHandleClassName
141
210
  ? { draggableHandle: `.${draggableHandleClassName}` }
142
211
  : null), (outerDnDEnable
143
212
  ? {
144
213
  isDroppable: true,
145
- onDropDragOver: (e) => this._onDropDragOver(e, properties, renderLayout),
146
- onDrop: (...args) => this._onDrop(group, ...args),
214
+ onDropDragOver: callbacks.onDropDragOver,
215
+ onDrop: callbacks.onDrop,
147
216
  }
148
217
  : null), { draggableCancel: `.${OVERLAY_CONTROLS_CLASS_NAME}` }),
149
218
  renderItems.map((item, i) => {
150
- return (React.createElement(GridItem, { forwardedPluginRef: (pluginRef) => {
151
- this.pluginsRefs[offset + i] = pluginRef;
152
- }, key: item.id, id: item.id, item: item, layout: renderLayout, adjustWidgetLayout: this.adjustWidgetLayout, isDragging: this.state.isDragging, noOverlay: noOverlay, focusable: focusable, withCustomHandle: Boolean(draggableHandleClassName), overlayControls: this.props.overlayControls }));
219
+ return (React.createElement(GridItem, { forwardedPluginRef: this.getMemoForwardRefCallback(offset + i), 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 }));
153
220
  }),
154
221
  this.renderTemporaryPlaceholder()));
155
222
  }
@@ -2,6 +2,8 @@ declare const _default: {
2
2
  new (props: any): {
3
3
  shouldComponentUpdate(nextProps: any): boolean;
4
4
  _onStateAndParamsChange: (stateAndParams: any, options: any) => void;
5
+ _currentRenderProps: {};
6
+ getRenderProps: () => {};
5
7
  render(): JSX.Element;
6
8
  context: unknown;
7
9
  setState<K extends string | number | symbol>(state: any, callback?: (() => void) | undefined): void;
@@ -26,6 +28,8 @@ declare const _default: {
26
28
  new (props: any, context: any): {
27
29
  shouldComponentUpdate(nextProps: any): boolean;
28
30
  _onStateAndParamsChange: (stateAndParams: any, options: any) => void;
31
+ _currentRenderProps: {};
32
+ getRenderProps: () => {};
29
33
  render(): JSX.Element;
30
34
  context: unknown;
31
35
  setState<K extends string | number | symbol>(state: any, callback?: (() => void) | undefined): void;
@@ -2,6 +2,8 @@ export function prepareItem(Component: any): {
2
2
  new (props: any): {
3
3
  shouldComponentUpdate(nextProps: any): boolean;
4
4
  _onStateAndParamsChange: (stateAndParams: any, options: any) => void;
5
+ _currentRenderProps: {};
6
+ getRenderProps: () => {};
5
7
  render(): JSX.Element;
6
8
  context: unknown;
7
9
  setState<K extends string | number | symbol>(state: any, callback?: (() => void) | undefined): void;
@@ -26,6 +28,8 @@ export function prepareItem(Component: any): {
26
28
  new (props: any, context: any): {
27
29
  shouldComponentUpdate(nextProps: any): boolean;
28
30
  _onStateAndParamsChange: (stateAndParams: any, options: any) => void;
31
+ _currentRenderProps: {};
32
+ getRenderProps: () => {};
29
33
  render(): JSX.Element;
30
34
  context: unknown;
31
35
  setState<K extends string | number | symbol>(state: any, callback?: (() => void) | undefined): void;
@@ -9,6 +9,35 @@ export function prepareItem(Component) {
9
9
  this._onStateAndParamsChange = (stateAndParams, options) => {
10
10
  this.context.onItemStateAndParamsChange(this.props.id, stateAndParams, options);
11
11
  };
12
+ this._currentRenderProps = {};
13
+ this.getRenderProps = () => {
14
+ const { id, width, height, item, adjustWidgetLayout, layout, isPlaceholder } = this.props;
15
+ const { itemsState, itemsParams, registerManager, settings, context, editMode } = this.context;
16
+ const { data, defaults, namespace } = item;
17
+ const rendererProps = {
18
+ data,
19
+ editMode,
20
+ params: itemsParams[id],
21
+ state: itemsState[id],
22
+ onStateAndParamsChange: this._onStateAndParamsChange,
23
+ width,
24
+ height,
25
+ id,
26
+ defaults,
27
+ namespace,
28
+ settings,
29
+ context,
30
+ layout,
31
+ gridLayout: registerManager.gridLayout,
32
+ adjustWidgetLayout,
33
+ isPlaceholder,
34
+ };
35
+ const changedProp = Object.entries(rendererProps).find(([key, value]) => this._currentRenderProps[key] !== value);
36
+ if (changedProp) {
37
+ this._currentRenderProps = rendererProps;
38
+ }
39
+ return this._currentRenderProps;
40
+ };
12
41
  }
13
42
  shouldComponentUpdate(nextProps) {
14
43
  const { width, height, transform } = this.props;
@@ -22,28 +51,10 @@ export function prepareItem(Component) {
22
51
  return true;
23
52
  }
24
53
  render() {
25
- const { id, width, height, item, adjustWidgetLayout, layout, isPlaceholder } = this.props;
26
- const { itemsState, itemsParams, registerManager, settings, context, editMode } = this.context;
27
- const { type, data, defaults, namespace } = item;
28
- const rendererProps = {
29
- data,
30
- editMode,
31
- params: itemsParams[id],
32
- state: itemsState[id],
33
- onStateAndParamsChange: this._onStateAndParamsChange,
34
- width,
35
- height,
36
- id,
37
- defaults,
38
- namespace,
39
- settings,
40
- context,
41
- layout,
42
- gridLayout: registerManager.gridLayout,
43
- adjustWidgetLayout,
44
- isPlaceholder,
45
- };
46
- return (React.createElement(Component, { forwardedPluginRef: this.props.forwardedPluginRef, rendererProps: rendererProps, registerManager: registerManager, type: type, isPlaceholder: isPlaceholder }));
54
+ const { item, isPlaceholder } = this.props;
55
+ const { registerManager } = this.context;
56
+ const { type } = item;
57
+ return (React.createElement(Component, { forwardedPluginRef: this.props.forwardedPluginRef, rendererProps: this.getRenderProps(), registerManager: registerManager, type: type, isPlaceholder: isPlaceholder }));
47
58
  }
48
59
  },
49
60
  _a.propTypes = {
@@ -4,6 +4,7 @@ import isEqual from 'lodash/isEqual';
4
4
  import pick from 'lodash/pick';
5
5
  import { COMPACT_TYPE_HORIZONTAL_NOWRAP, DEFAULT_GROUP, DEFAULT_WIDGET_HEIGHT, DEFAULT_WIDGET_WIDTH, TEMPORARY_ITEM_ID, } from '../constants/common';
6
6
  import { DashKitContext, DashKitDnDContext } from '../context/DashKitContext';
7
+ import { useDeepEqualMemo } from '../hooks/useDeepEqualMemo';
7
8
  import { getItemsParams, getItemsState } from '../shared';
8
9
  import { UpdateManager } from '../utils';
9
10
  const ITEM_PROPS = ['i', 'h', 'w', 'x', 'y', 'parent'];
@@ -151,10 +152,10 @@ function useMemoStateContext(props) {
151
152
  }
152
153
  else {
153
154
  adjustedLayouts.current[widgetId] = Object.assign({}, adjustedItem);
154
- originalLayouts.current[widgetId] = item;
155
155
  }
156
156
  delete nowrapAdjustedLayouts.current[widgetId];
157
157
  }
158
+ originalLayouts.current[widgetId] = item;
158
159
  });
159
160
  Object.entries(nowrapGroups).forEach(([, { items, leftSpace }]) => {
160
161
  items.forEach((item) => {
@@ -169,9 +170,17 @@ function useMemoStateContext(props) {
169
170
  });
170
171
  });
171
172
  }
173
+ else if (groups) {
174
+ layout.forEach((item) => {
175
+ const widgetId = item.i;
176
+ if (adjustedLayouts.current[widgetId]) {
177
+ originalLayouts.current[widgetId] = item;
178
+ }
179
+ });
180
+ }
172
181
  return adjustedLayouts.current;
173
182
  }, [props.registerManager]);
174
- const itemsParams = React.useMemo(() => getItemsParams({
183
+ const itemsParams = useDeepEqualMemo(() => getItemsParams({
175
184
  defaultGlobalParams: props.defaultGlobalParams,
176
185
  globalParams: props.globalParams,
177
186
  config: props.config,
@@ -184,7 +193,7 @@ function useMemoStateContext(props) {
184
193
  props.itemsStateAndParams,
185
194
  props.registerManager,
186
195
  ]);
187
- const itemsState = React.useMemo(() => getItemsState({
196
+ const itemsState = useDeepEqualMemo(() => getItemsState({
188
197
  config: props.config,
189
198
  itemsStateAndParams: props.itemsStateAndParams,
190
199
  }), [props.config, props.itemsStateAndParams]);
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const useDeepEqualMemo: <T extends unknown>(predicate: () => T, deps: React.DependencyList) => T;
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import isEqual from 'lodash/isEqual';
3
+ export const useDeepEqualMemo = (predicate, deps) => {
4
+ const previousValueRef = React.useRef({});
5
+ const callbackRef = React.useRef(predicate);
6
+ callbackRef.current = predicate;
7
+ return React.useMemo(() => {
8
+ const value = predicate();
9
+ if (!isEqual(previousValueRef.current, value)) {
10
+ previousValueRef.current = value;
11
+ }
12
+ return previousValueRef.current;
13
+ }, [callbackRef, previousValueRef, ...deps]);
14
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/dashkit",
3
- "version": "8.5.0-beta.3",
3
+ "version": "8.5.0-beta.4",
4
4
  "description": "Library for rendering dashboard grid layout",
5
5
  "license": "MIT",
6
6
  "repository": {