@gravity-ui/dashkit 8.5.0-beta.2 → 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,15 +23,24 @@ 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
- caclculateEmptyHorizontalSpace(layout: any, cols: any): any;
30
- getElementLayout(layout: any, properties: any): any;
31
44
  renderGroup(group: any, renderLayout: any, renderItems: any, offset: number | undefined, groupGridProperties: any): JSX.Element;
32
45
  render(): any;
33
46
  }
@@ -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,20 +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
- let maxW = gridProperties.cols;
55
- const maxH = gridProperties.maxRows;
56
- if (gridProperties.compactType === constants_1.COMPACT_TYPE_HORIZONTAL_NOWRAP) {
57
- maxW = this.caclculateEmptyHorizontalSpace(layout, gridProperties.cols);
58
- }
59
- if (maxW === 0 || maxH === 0) {
60
- return false;
61
- }
62
- return onDropDragOver(e, { maxH, maxW });
117
+ const { properties, layout } = this.getLayoutAndPropsByGroup(group);
118
+ return this.context.onDropDragOver(e, properties, layout);
63
119
  };
64
120
  this._onDrop = (group, newLayout, item, e) => {
65
121
  if (!item) {
@@ -92,6 +148,10 @@ class GridLayout extends react_1.default.PureComponent {
92
148
  componentWillUnmount() {
93
149
  clearTimeout(this._timeout);
94
150
  document.removeEventListener('visibilitychange', this.onVisibilityChange);
151
+ this._memoCallbacksForGroups = {};
152
+ this._memoGroupsProps = {};
153
+ this._memoGroupsLayouts = {};
154
+ this._memoForwardedPluginRef = [];
95
155
  }
96
156
  getActiveLayout() {
97
157
  const { layout, temporaryLayout } = this.context;
@@ -139,17 +199,6 @@ class GridLayout extends react_1.default.PureComponent {
139
199
  const { type } = temporaryLayout.dragProps;
140
200
  return (react_1.default.createElement(GridItem_1.default, { key: id, id: id, item: { id, type, data: {} }, layout: temporaryLayout.data, adjustWidgetLayout: this.adjustWidgetLayout, isDragging: this.state.isDragging, isPlaceholder: true, noOverlay: noOverlay, withCustomHandle: Boolean(draggableHandleClassName), overlayControls: this.props.overlayControls }));
141
201
  }
142
- caclculateEmptyHorizontalSpace(layout, cols) {
143
- return layout.reduce((memo, item) => memo - item.w, cols);
144
- }
145
- getElementLayout(layout, properties) {
146
- let maxH;
147
- if (properties.maxRows) {
148
- maxH = properties.maxRows;
149
- }
150
- const leftSpace = this.caclculateEmptyHorizontalSpace(layout, properties.cols);
151
- return layout.map((item) => (Object.assign(Object.assign({}, item), { maxW: item.w + leftSpace, maxH })));
152
- }
153
202
  renderGroup(group, renderLayout, renderItems, offset = 0, groupGridProperties) {
154
203
  const { registerManager, editMode, noOverlay, focusable, draggableHandleClassName, outerDnDEnable, } = this.context;
155
204
  const properties = groupGridProperties
@@ -157,22 +206,20 @@ class GridLayout extends react_1.default.PureComponent {
157
206
  : registerManager.gridLayout;
158
207
  let { compactType } = properties;
159
208
  if (compactType === constants_1.COMPACT_TYPE_HORIZONTAL_NOWRAP) {
160
- renderLayout = this.getElementLayout(renderLayout, properties);
161
209
  compactType = 'horizontal';
162
210
  }
163
- 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
164
213
  ? { draggableHandle: `.${draggableHandleClassName}` }
165
214
  : null), (outerDnDEnable
166
215
  ? {
167
216
  isDroppable: true,
168
- onDropDragOver: (e) => this._onDropDragOver(e, properties, renderLayout),
169
- onDrop: (...args) => this._onDrop(group, ...args),
217
+ onDropDragOver: callbacks.onDropDragOver,
218
+ onDrop: callbacks.onDrop,
170
219
  }
171
220
  : null), { draggableCancel: `.${constants_1.OVERLAY_CONTROLS_CLASS_NAME}` }),
172
221
  renderItems.map((item, i) => {
173
- return (react_1.default.createElement(GridItem_1.default, { forwardedPluginRef: (pluginRef) => {
174
- this.pluginsRefs[offset + i] = pluginRef;
175
- }, 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 }));
176
223
  }),
177
224
  this.renderTemporaryPlaceholder()));
178
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,8 +7,10 @@ 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");
13
+ const ITEM_PROPS = ['i', 'h', 'w', 'x', 'y', 'parent'];
12
14
  function useMemoStateContext(props) {
13
15
  // так как мы не хотим хранить параметры виджета с активированной автовысотой в сторе и на сервере, актуальный
14
16
  // (видимый юзером в конкретный момент времени) лэйаут (массив объектов с данными о ширине, высоте,
@@ -17,6 +19,7 @@ function useMemoStateContext(props) {
17
19
  // (высота) из конфига будет запоминаться в originalLayouts, новое значение высоты в adjustedLayouts
18
20
  const originalLayouts = react_1.default.useRef({});
19
21
  const adjustedLayouts = react_1.default.useRef({});
22
+ const nowrapAdjustedLayouts = react_1.default.useRef({});
20
23
  const [temporaryLayout, setTemporaryLayout] = react_1.default.useState(null);
21
24
  const resetTemporaryLayout = react_1.default.useCallback(() => setTemporaryLayout(null), [setTemporaryLayout]);
22
25
  const dndContext = react_1.default.useContext(DashKitContext_1.DashKitDnDContext);
@@ -52,6 +55,11 @@ function useMemoStateContext(props) {
52
55
  }
53
56
  }, [props.config, onChange]);
54
57
  const onItemRemove = react_1.default.useCallback((id) => {
58
+ if (nowrapAdjustedLayouts.current[id]) {
59
+ delete nowrapAdjustedLayouts.current[id];
60
+ delete adjustedLayouts.current[id];
61
+ delete originalLayouts.current[id];
62
+ }
55
63
  if (id === common_1.TEMPORARY_ITEM_ID) {
56
64
  resetTemporaryLayout();
57
65
  }
@@ -106,7 +114,76 @@ function useMemoStateContext(props) {
106
114
  forceUpdateLayout((prev) => prev + 1);
107
115
  }
108
116
  }, []);
109
- const itemsParams = react_1.default.useMemo(() => (0, shared_1.getItemsParams)({
117
+ const horizontalNowrapLayoutAdjust = react_1.default.useCallback((groups, layout) => {
118
+ const defaultProps = props.registerManager._gridLayout || {};
119
+ const nowrapGroups = {};
120
+ let hasNowrapGroups = false;
121
+ if (defaultProps.compactType === common_1.COMPACT_TYPE_HORIZONTAL_NOWRAP) {
122
+ nowrapGroups[common_1.DEFAULT_GROUP] = {
123
+ items: [],
124
+ leftSpace: defaultProps.cols,
125
+ };
126
+ hasNowrapGroups = true;
127
+ }
128
+ if (groups) {
129
+ groups.forEach((group) => {
130
+ var _a;
131
+ const resultProps = ((_a = group.gridProperties) === null || _a === void 0 ? void 0 : _a.call(group, defaultProps)) || {};
132
+ if (resultProps.compactType === common_1.COMPACT_TYPE_HORIZONTAL_NOWRAP) {
133
+ nowrapGroups[group.id] = {
134
+ items: [],
135
+ leftSpace: resultProps.cols,
136
+ };
137
+ hasNowrapGroups = true;
138
+ }
139
+ });
140
+ }
141
+ if (hasNowrapGroups) {
142
+ layout.forEach((item) => {
143
+ const widgetId = item.i;
144
+ const parentId = item.parent || common_1.DEFAULT_GROUP;
145
+ if (nowrapGroups[parentId]) {
146
+ nowrapGroups[parentId].items.push(item);
147
+ nowrapGroups[parentId].leftSpace -= item.w;
148
+ }
149
+ else if (nowrapAdjustedLayouts.current[item.i]) {
150
+ const { originalMaxW } = nowrapAdjustedLayouts.current[widgetId];
151
+ const adjustedItem = adjustedLayouts.current[widgetId];
152
+ if (originalMaxW) {
153
+ adjustedLayouts.current[widgetId] = Object.assign(Object.assign({}, adjustedItem), { maxW: originalMaxW });
154
+ originalLayouts.current[widgetId] = item;
155
+ }
156
+ else {
157
+ adjustedLayouts.current[widgetId] = Object.assign({}, adjustedItem);
158
+ }
159
+ delete nowrapAdjustedLayouts.current[widgetId];
160
+ }
161
+ originalLayouts.current[widgetId] = item;
162
+ });
163
+ Object.entries(nowrapGroups).forEach(([, { items, leftSpace }]) => {
164
+ items.forEach((item) => {
165
+ const maxW = item.w + leftSpace;
166
+ if (!adjustedLayouts[item.i] || adjustedLayouts[item.i].maxW > maxW) {
167
+ originalLayouts.current[item.i] = item;
168
+ adjustedLayouts.current[item.i] = Object.assign(Object.assign({}, item), { maxW });
169
+ nowrapAdjustedLayouts.current[item.i] = item.maxW
170
+ ? { maxW, originalMaxW: item.maxW }
171
+ : { maxW };
172
+ }
173
+ });
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
+ }
184
+ return adjustedLayouts.current;
185
+ }, [props.registerManager]);
186
+ const itemsParams = (0, useDeepEqualMemo_1.useDeepEqualMemo)(() => (0, shared_1.getItemsParams)({
110
187
  defaultGlobalParams: props.defaultGlobalParams,
111
188
  globalParams: props.globalParams,
112
189
  config: props.config,
@@ -119,7 +196,7 @@ function useMemoStateContext(props) {
119
196
  props.itemsStateAndParams,
120
197
  props.registerManager,
121
198
  ]);
122
- const itemsState = react_1.default.useMemo(() => (0, shared_1.getItemsState)({
199
+ const itemsState = (0, useDeepEqualMemo_1.useDeepEqualMemo)(() => (0, shared_1.getItemsState)({
123
200
  config: props.config,
124
201
  itemsStateAndParams: props.itemsStateAndParams,
125
202
  }), [props.config, props.itemsStateAndParams]);
@@ -134,27 +211,24 @@ function useMemoStateContext(props) {
134
211
  .filter(Boolean);
135
212
  }, []);
136
213
  const resultLayout = react_1.default.useMemo(() => {
214
+ const adjusted = horizontalNowrapLayoutAdjust(props.groups, props.layout);
137
215
  return props.layout.map((item) => {
138
- if (item.i in adjustedLayouts.current) {
216
+ if (item.i in adjusted) {
139
217
  // eslint-disable-next-line no-unused-vars
140
- const _a = adjustedLayouts.current[item.i] || {}, { parent } = _a, adjustedItem = tslib_1.__rest(_a, ["parent"]);
218
+ const _a = adjusted[item.i] || {}, { parent } = _a, adjustedItem = tslib_1.__rest(_a, ["parent"]);
141
219
  adjustedItem.w = item.w;
142
220
  adjustedItem.x = item.x;
143
221
  adjustedItem.y = item.y;
144
222
  if (item.parent) {
145
223
  adjustedItem.parent = item.parent;
146
224
  }
147
- // update originalLayouts memoized item
148
- if (originalLayouts.current[item.i]) {
149
- originalLayouts.current[item.i] = item;
150
- }
151
225
  return adjustedItem;
152
226
  }
153
227
  else {
154
- return Object.assign({}, item);
228
+ return item;
155
229
  }
156
230
  });
157
- }, [props.layout, layoutUpdateCounter]);
231
+ }, [props.layout, props.groups, horizontalNowrapLayoutAdjust, layoutUpdateCounter]);
158
232
  const reloadItems = react_1.default.useCallback((pluginsRefs, data) => {
159
233
  pluginsRefs.forEach((ref) => ref && ref.reload && ref.reload(data));
160
234
  }, []);
@@ -173,20 +247,31 @@ function useMemoStateContext(props) {
173
247
  return null;
174
248
  }
175
249
  }, [dragProps, props.registerManager]);
176
- const onDropDragOver = react_1.default.useCallback((_e, gridProps = {}) => {
250
+ const onDropDragOver = react_1.default.useCallback((_e, gridProps, groupLayout) => {
177
251
  if (temporaryLayout) {
178
252
  resetTemporaryLayout();
179
253
  return false;
180
254
  }
181
- if (dragOverPlugin) {
182
- const { defaultLayout } = dragOverPlugin;
183
- const { h = (defaultLayout === null || defaultLayout === void 0 ? void 0 : defaultLayout.h) || common_1.DEFAULT_WIDGET_HEIGHT, w = (defaultLayout === null || defaultLayout === void 0 ? void 0 : defaultLayout.w) || common_1.DEFAULT_WIDGET_WIDTH, } = dragProps.layout || {};
184
- return {
185
- h: gridProps.maxH ? Math.min(h, gridProps.maxH) : h,
186
- w: gridProps.maxW ? Math.min(w, gridProps.maxW) : w,
187
- };
255
+ if (!dragOverPlugin) {
256
+ return false;
257
+ }
258
+ let maxW = gridProps.cols;
259
+ const { defaultLayout } = dragOverPlugin;
260
+ const maxH = Math.min(gridProps.maxRows || Infinity, defaultLayout.maxH || Infinity);
261
+ if (gridProps.compactType === common_1.COMPACT_TYPE_HORIZONTAL_NOWRAP) {
262
+ maxW = groupLayout.reduce((memo, item) => memo - item.w, gridProps.cols);
263
+ }
264
+ if (maxW === 0 ||
265
+ maxH === 0 ||
266
+ maxW < defaultLayout.minW ||
267
+ maxH < defaultLayout.minH) {
268
+ return false;
188
269
  }
189
- return false;
270
+ const { h = (defaultLayout === null || defaultLayout === void 0 ? void 0 : defaultLayout.h) || common_1.DEFAULT_WIDGET_HEIGHT, w = (defaultLayout === null || defaultLayout === void 0 ? void 0 : defaultLayout.w) || common_1.DEFAULT_WIDGET_WIDTH, } = dragProps.layout || {};
271
+ return {
272
+ h: maxH ? Math.min(h, maxH) : h,
273
+ w: maxW ? Math.min(w, maxW) : w,
274
+ };
190
275
  }, [resetTemporaryLayout, temporaryLayout, dragOverPlugin, dragProps]);
191
276
  const onDropProp = props.onDrop;
192
277
  const onDrop = react_1.default.useCallback((newLayout, item) => {
@@ -200,11 +285,11 @@ function useMemoStateContext(props) {
200
285
  onDropProp({
201
286
  newLayout: newLayout.reduce((memo, l) => {
202
287
  if (l.i !== item.i) {
203
- memo.push((0, pick_1.default)(l, ['i', 'h', 'w', 'x', 'y', 'parent']));
288
+ memo.push((0, pick_1.default)(l, ITEM_PROPS));
204
289
  }
205
290
  return memo;
206
291
  }, []),
207
- itemLayout: (0, pick_1.default)(item, ['i', 'h', 'w', 'x', 'y', 'parent']),
292
+ itemLayout: (0, pick_1.default)(item, ITEM_PROPS),
208
293
  commit: resetTemporaryLayout,
209
294
  dragProps,
210
295
  });
@@ -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,15 +23,24 @@ 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
- caclculateEmptyHorizontalSpace(layout: any, cols: any): any;
30
- getElementLayout(layout: any, properties: any): any;
31
44
  renderGroup(group: any, renderLayout: any, renderItems: any, offset: number | undefined, groupGridProperties: any): JSX.Element;
32
45
  render(): any;
33
46
  }
@@ -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,20 +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
- let maxW = gridProperties.cols;
52
- const maxH = gridProperties.maxRows;
53
- if (gridProperties.compactType === COMPACT_TYPE_HORIZONTAL_NOWRAP) {
54
- maxW = this.caclculateEmptyHorizontalSpace(layout, gridProperties.cols);
55
- }
56
- if (maxW === 0 || maxH === 0) {
57
- return false;
58
- }
59
- return onDropDragOver(e, { maxH, maxW });
114
+ const { properties, layout } = this.getLayoutAndPropsByGroup(group);
115
+ return this.context.onDropDragOver(e, properties, layout);
60
116
  };
61
117
  this._onDrop = (group, newLayout, item, e) => {
62
118
  if (!item) {
@@ -89,6 +145,10 @@ class GridLayout extends React.PureComponent {
89
145
  componentWillUnmount() {
90
146
  clearTimeout(this._timeout);
91
147
  document.removeEventListener('visibilitychange', this.onVisibilityChange);
148
+ this._memoCallbacksForGroups = {};
149
+ this._memoGroupsProps = {};
150
+ this._memoGroupsLayouts = {};
151
+ this._memoForwardedPluginRef = [];
92
152
  }
93
153
  getActiveLayout() {
94
154
  const { layout, temporaryLayout } = this.context;
@@ -136,17 +196,6 @@ class GridLayout extends React.PureComponent {
136
196
  const { type } = temporaryLayout.dragProps;
137
197
  return (React.createElement(GridItem, { key: id, id: id, item: { id, type, data: {} }, layout: temporaryLayout.data, adjustWidgetLayout: this.adjustWidgetLayout, isDragging: this.state.isDragging, isPlaceholder: true, noOverlay: noOverlay, withCustomHandle: Boolean(draggableHandleClassName), overlayControls: this.props.overlayControls }));
138
198
  }
139
- caclculateEmptyHorizontalSpace(layout, cols) {
140
- return layout.reduce((memo, item) => memo - item.w, cols);
141
- }
142
- getElementLayout(layout, properties) {
143
- let maxH;
144
- if (properties.maxRows) {
145
- maxH = properties.maxRows;
146
- }
147
- const leftSpace = this.caclculateEmptyHorizontalSpace(layout, properties.cols);
148
- return layout.map((item) => (Object.assign(Object.assign({}, item), { maxW: item.w + leftSpace, maxH })));
149
- }
150
199
  renderGroup(group, renderLayout, renderItems, offset = 0, groupGridProperties) {
151
200
  const { registerManager, editMode, noOverlay, focusable, draggableHandleClassName, outerDnDEnable, } = this.context;
152
201
  const properties = groupGridProperties
@@ -154,22 +203,20 @@ class GridLayout extends React.PureComponent {
154
203
  : registerManager.gridLayout;
155
204
  let { compactType } = properties;
156
205
  if (compactType === COMPACT_TYPE_HORIZONTAL_NOWRAP) {
157
- renderLayout = this.getElementLayout(renderLayout, properties);
158
206
  compactType = 'horizontal';
159
207
  }
160
- 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
161
210
  ? { draggableHandle: `.${draggableHandleClassName}` }
162
211
  : null), (outerDnDEnable
163
212
  ? {
164
213
  isDroppable: true,
165
- onDropDragOver: (e) => this._onDropDragOver(e, properties, renderLayout),
166
- onDrop: (...args) => this._onDrop(group, ...args),
214
+ onDropDragOver: callbacks.onDropDragOver,
215
+ onDrop: callbacks.onDrop,
167
216
  }
168
217
  : null), { draggableCancel: `.${OVERLAY_CONTROLS_CLASS_NAME}` }),
169
218
  renderItems.map((item, i) => {
170
- return (React.createElement(GridItem, { forwardedPluginRef: (pluginRef) => {
171
- this.pluginsRefs[offset + i] = pluginRef;
172
- }, 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 }));
173
220
  }),
174
221
  this.renderTemporaryPlaceholder()));
175
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 = {
@@ -2,10 +2,12 @@ import { __rest } from "tslib";
2
2
  import React from 'react';
3
3
  import isEqual from 'lodash/isEqual';
4
4
  import pick from 'lodash/pick';
5
- import { DEFAULT_WIDGET_HEIGHT, DEFAULT_WIDGET_WIDTH, TEMPORARY_ITEM_ID } from '../constants/common';
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';
10
+ const ITEM_PROPS = ['i', 'h', 'w', 'x', 'y', 'parent'];
9
11
  function useMemoStateContext(props) {
10
12
  // так как мы не хотим хранить параметры виджета с активированной автовысотой в сторе и на сервере, актуальный
11
13
  // (видимый юзером в конкретный момент времени) лэйаут (массив объектов с данными о ширине, высоте,
@@ -14,6 +16,7 @@ function useMemoStateContext(props) {
14
16
  // (высота) из конфига будет запоминаться в originalLayouts, новое значение высоты в adjustedLayouts
15
17
  const originalLayouts = React.useRef({});
16
18
  const adjustedLayouts = React.useRef({});
19
+ const nowrapAdjustedLayouts = React.useRef({});
17
20
  const [temporaryLayout, setTemporaryLayout] = React.useState(null);
18
21
  const resetTemporaryLayout = React.useCallback(() => setTemporaryLayout(null), [setTemporaryLayout]);
19
22
  const dndContext = React.useContext(DashKitDnDContext);
@@ -49,6 +52,11 @@ function useMemoStateContext(props) {
49
52
  }
50
53
  }, [props.config, onChange]);
51
54
  const onItemRemove = React.useCallback((id) => {
55
+ if (nowrapAdjustedLayouts.current[id]) {
56
+ delete nowrapAdjustedLayouts.current[id];
57
+ delete adjustedLayouts.current[id];
58
+ delete originalLayouts.current[id];
59
+ }
52
60
  if (id === TEMPORARY_ITEM_ID) {
53
61
  resetTemporaryLayout();
54
62
  }
@@ -103,7 +111,76 @@ function useMemoStateContext(props) {
103
111
  forceUpdateLayout((prev) => prev + 1);
104
112
  }
105
113
  }, []);
106
- const itemsParams = React.useMemo(() => getItemsParams({
114
+ const horizontalNowrapLayoutAdjust = React.useCallback((groups, layout) => {
115
+ const defaultProps = props.registerManager._gridLayout || {};
116
+ const nowrapGroups = {};
117
+ let hasNowrapGroups = false;
118
+ if (defaultProps.compactType === COMPACT_TYPE_HORIZONTAL_NOWRAP) {
119
+ nowrapGroups[DEFAULT_GROUP] = {
120
+ items: [],
121
+ leftSpace: defaultProps.cols,
122
+ };
123
+ hasNowrapGroups = true;
124
+ }
125
+ if (groups) {
126
+ groups.forEach((group) => {
127
+ var _a;
128
+ const resultProps = ((_a = group.gridProperties) === null || _a === void 0 ? void 0 : _a.call(group, defaultProps)) || {};
129
+ if (resultProps.compactType === COMPACT_TYPE_HORIZONTAL_NOWRAP) {
130
+ nowrapGroups[group.id] = {
131
+ items: [],
132
+ leftSpace: resultProps.cols,
133
+ };
134
+ hasNowrapGroups = true;
135
+ }
136
+ });
137
+ }
138
+ if (hasNowrapGroups) {
139
+ layout.forEach((item) => {
140
+ const widgetId = item.i;
141
+ const parentId = item.parent || DEFAULT_GROUP;
142
+ if (nowrapGroups[parentId]) {
143
+ nowrapGroups[parentId].items.push(item);
144
+ nowrapGroups[parentId].leftSpace -= item.w;
145
+ }
146
+ else if (nowrapAdjustedLayouts.current[item.i]) {
147
+ const { originalMaxW } = nowrapAdjustedLayouts.current[widgetId];
148
+ const adjustedItem = adjustedLayouts.current[widgetId];
149
+ if (originalMaxW) {
150
+ adjustedLayouts.current[widgetId] = Object.assign(Object.assign({}, adjustedItem), { maxW: originalMaxW });
151
+ originalLayouts.current[widgetId] = item;
152
+ }
153
+ else {
154
+ adjustedLayouts.current[widgetId] = Object.assign({}, adjustedItem);
155
+ }
156
+ delete nowrapAdjustedLayouts.current[widgetId];
157
+ }
158
+ originalLayouts.current[widgetId] = item;
159
+ });
160
+ Object.entries(nowrapGroups).forEach(([, { items, leftSpace }]) => {
161
+ items.forEach((item) => {
162
+ const maxW = item.w + leftSpace;
163
+ if (!adjustedLayouts[item.i] || adjustedLayouts[item.i].maxW > maxW) {
164
+ originalLayouts.current[item.i] = item;
165
+ adjustedLayouts.current[item.i] = Object.assign(Object.assign({}, item), { maxW });
166
+ nowrapAdjustedLayouts.current[item.i] = item.maxW
167
+ ? { maxW, originalMaxW: item.maxW }
168
+ : { maxW };
169
+ }
170
+ });
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
+ }
181
+ return adjustedLayouts.current;
182
+ }, [props.registerManager]);
183
+ const itemsParams = useDeepEqualMemo(() => getItemsParams({
107
184
  defaultGlobalParams: props.defaultGlobalParams,
108
185
  globalParams: props.globalParams,
109
186
  config: props.config,
@@ -116,7 +193,7 @@ function useMemoStateContext(props) {
116
193
  props.itemsStateAndParams,
117
194
  props.registerManager,
118
195
  ]);
119
- const itemsState = React.useMemo(() => getItemsState({
196
+ const itemsState = useDeepEqualMemo(() => getItemsState({
120
197
  config: props.config,
121
198
  itemsStateAndParams: props.itemsStateAndParams,
122
199
  }), [props.config, props.itemsStateAndParams]);
@@ -131,27 +208,24 @@ function useMemoStateContext(props) {
131
208
  .filter(Boolean);
132
209
  }, []);
133
210
  const resultLayout = React.useMemo(() => {
211
+ const adjusted = horizontalNowrapLayoutAdjust(props.groups, props.layout);
134
212
  return props.layout.map((item) => {
135
- if (item.i in adjustedLayouts.current) {
213
+ if (item.i in adjusted) {
136
214
  // eslint-disable-next-line no-unused-vars
137
- const _a = adjustedLayouts.current[item.i] || {}, { parent } = _a, adjustedItem = __rest(_a, ["parent"]);
215
+ const _a = adjusted[item.i] || {}, { parent } = _a, adjustedItem = __rest(_a, ["parent"]);
138
216
  adjustedItem.w = item.w;
139
217
  adjustedItem.x = item.x;
140
218
  adjustedItem.y = item.y;
141
219
  if (item.parent) {
142
220
  adjustedItem.parent = item.parent;
143
221
  }
144
- // update originalLayouts memoized item
145
- if (originalLayouts.current[item.i]) {
146
- originalLayouts.current[item.i] = item;
147
- }
148
222
  return adjustedItem;
149
223
  }
150
224
  else {
151
- return Object.assign({}, item);
225
+ return item;
152
226
  }
153
227
  });
154
- }, [props.layout, layoutUpdateCounter]);
228
+ }, [props.layout, props.groups, horizontalNowrapLayoutAdjust, layoutUpdateCounter]);
155
229
  const reloadItems = React.useCallback((pluginsRefs, data) => {
156
230
  pluginsRefs.forEach((ref) => ref && ref.reload && ref.reload(data));
157
231
  }, []);
@@ -170,20 +244,31 @@ function useMemoStateContext(props) {
170
244
  return null;
171
245
  }
172
246
  }, [dragProps, props.registerManager]);
173
- const onDropDragOver = React.useCallback((_e, gridProps = {}) => {
247
+ const onDropDragOver = React.useCallback((_e, gridProps, groupLayout) => {
174
248
  if (temporaryLayout) {
175
249
  resetTemporaryLayout();
176
250
  return false;
177
251
  }
178
- if (dragOverPlugin) {
179
- const { defaultLayout } = dragOverPlugin;
180
- const { h = (defaultLayout === null || defaultLayout === void 0 ? void 0 : defaultLayout.h) || DEFAULT_WIDGET_HEIGHT, w = (defaultLayout === null || defaultLayout === void 0 ? void 0 : defaultLayout.w) || DEFAULT_WIDGET_WIDTH, } = dragProps.layout || {};
181
- return {
182
- h: gridProps.maxH ? Math.min(h, gridProps.maxH) : h,
183
- w: gridProps.maxW ? Math.min(w, gridProps.maxW) : w,
184
- };
252
+ if (!dragOverPlugin) {
253
+ return false;
254
+ }
255
+ let maxW = gridProps.cols;
256
+ const { defaultLayout } = dragOverPlugin;
257
+ const maxH = Math.min(gridProps.maxRows || Infinity, defaultLayout.maxH || Infinity);
258
+ if (gridProps.compactType === COMPACT_TYPE_HORIZONTAL_NOWRAP) {
259
+ maxW = groupLayout.reduce((memo, item) => memo - item.w, gridProps.cols);
260
+ }
261
+ if (maxW === 0 ||
262
+ maxH === 0 ||
263
+ maxW < defaultLayout.minW ||
264
+ maxH < defaultLayout.minH) {
265
+ return false;
185
266
  }
186
- return false;
267
+ const { h = (defaultLayout === null || defaultLayout === void 0 ? void 0 : defaultLayout.h) || DEFAULT_WIDGET_HEIGHT, w = (defaultLayout === null || defaultLayout === void 0 ? void 0 : defaultLayout.w) || DEFAULT_WIDGET_WIDTH, } = dragProps.layout || {};
268
+ return {
269
+ h: maxH ? Math.min(h, maxH) : h,
270
+ w: maxW ? Math.min(w, maxW) : w,
271
+ };
187
272
  }, [resetTemporaryLayout, temporaryLayout, dragOverPlugin, dragProps]);
188
273
  const onDropProp = props.onDrop;
189
274
  const onDrop = React.useCallback((newLayout, item) => {
@@ -197,11 +282,11 @@ function useMemoStateContext(props) {
197
282
  onDropProp({
198
283
  newLayout: newLayout.reduce((memo, l) => {
199
284
  if (l.i !== item.i) {
200
- memo.push(pick(l, ['i', 'h', 'w', 'x', 'y', 'parent']));
285
+ memo.push(pick(l, ITEM_PROPS));
201
286
  }
202
287
  return memo;
203
288
  }, []),
204
- itemLayout: pick(item, ['i', 'h', 'w', 'x', 'y', 'parent']),
289
+ itemLayout: pick(item, ITEM_PROPS),
205
290
  commit: resetTemporaryLayout,
206
291
  dragProps,
207
292
  });
@@ -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.2",
3
+ "version": "8.5.0-beta.4",
4
4
  "description": "Library for rendering dashboard grid layout",
5
5
  "license": "MIT",
6
6
  "repository": {