@gravity-ui/dashkit 8.5.0 → 8.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +11 -4
  2. package/build/cjs/components/DashKit/DashKit.d.ts +3 -2
  3. package/build/cjs/components/DashKitView/DashKitView.js +2 -2
  4. package/build/cjs/components/GridItem/GridItem.js +2 -4
  5. package/build/cjs/components/GridLayout/GridLayout.d.ts +16 -1
  6. package/build/cjs/components/GridLayout/GridLayout.js +81 -10
  7. package/build/cjs/components/Item/Item.d.ts +4 -0
  8. package/build/cjs/components/OverlayControls/OverlayControls.d.ts +8 -6
  9. package/build/cjs/components/OverlayControls/OverlayControls.js +58 -42
  10. package/build/cjs/constants/common.d.ts +1 -0
  11. package/build/cjs/constants/common.js +2 -1
  12. package/build/cjs/context/DashKitContext.d.ts +1 -0
  13. package/build/cjs/context/DashKitContext.js +3 -1
  14. package/build/cjs/hocs/prepareItem.d.ts +4 -0
  15. package/build/cjs/hocs/prepareItem.js +33 -22
  16. package/build/cjs/hocs/withContext.js +129 -21
  17. package/build/cjs/hooks/useDeepEqualMemo.d.ts +2 -0
  18. package/build/cjs/hooks/useDeepEqualMemo.js +17 -0
  19. package/build/cjs/typings/config.d.ts +3 -1
  20. package/build/esm/components/DashKit/DashKit.d.ts +3 -2
  21. package/build/esm/components/DashKitView/DashKitView.js +2 -2
  22. package/build/esm/components/GridItem/GridItem.js +2 -4
  23. package/build/esm/components/GridLayout/GridLayout.d.ts +16 -1
  24. package/build/esm/components/GridLayout/GridLayout.js +82 -11
  25. package/build/esm/components/Item/Item.d.ts +4 -0
  26. package/build/esm/components/OverlayControls/OverlayControls.d.ts +8 -6
  27. package/build/esm/components/OverlayControls/OverlayControls.js +59 -43
  28. package/build/esm/constants/common.d.ts +1 -0
  29. package/build/esm/constants/common.js +1 -0
  30. package/build/esm/context/DashKitContext.d.ts +1 -0
  31. package/build/esm/context/DashKitContext.js +2 -1
  32. package/build/esm/hocs/prepareItem.d.ts +4 -0
  33. package/build/esm/hocs/prepareItem.js +33 -22
  34. package/build/esm/hocs/withContext.js +132 -23
  35. package/build/esm/hooks/useDeepEqualMemo.d.ts +2 -0
  36. package/build/esm/hooks/useDeepEqualMemo.js +12 -0
  37. package/build/esm/typings/config.d.ts +3 -1
  38. package/package.json +1 -1
@@ -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);
@@ -51,7 +54,13 @@ function useMemoStateContext(props) {
51
54
  onChange({ config: newConfig });
52
55
  }
53
56
  }, [props.config, onChange]);
57
+ const getLayoutItem = react_1.default.useCallback((id) => {
58
+ return props.config.layout.find(({ i }) => i === id);
59
+ }, [props.config.layout]);
54
60
  const onItemRemove = react_1.default.useCallback((id) => {
61
+ delete nowrapAdjustedLayouts.current[id];
62
+ delete adjustedLayouts.current[id];
63
+ delete originalLayouts.current[id];
55
64
  if (id === common_1.TEMPORARY_ITEM_ID) {
56
65
  resetTemporaryLayout();
57
66
  }
@@ -106,7 +115,55 @@ function useMemoStateContext(props) {
106
115
  forceUpdateLayout((prev) => prev + 1);
107
116
  }
108
117
  }, []);
109
- const itemsParams = react_1.default.useMemo(() => (0, shared_1.getItemsParams)({
118
+ react_1.default.useMemo(() => {
119
+ const groups = props.groups;
120
+ const layout = props.layout;
121
+ const defaultProps = props.registerManager._gridLayout || {};
122
+ const nowrapGroups = {};
123
+ let hasNowrapGroups = false;
124
+ if (defaultProps.compactType === common_1.COMPACT_TYPE_HORIZONTAL_NOWRAP) {
125
+ nowrapGroups[common_1.DEFAULT_GROUP] = {
126
+ items: [],
127
+ leftSpace: defaultProps.cols,
128
+ };
129
+ hasNowrapGroups = true;
130
+ }
131
+ if (groups) {
132
+ groups.forEach((group) => {
133
+ var _a;
134
+ const resultProps = ((_a = group.gridProperties) === null || _a === void 0 ? void 0 : _a.call(group, defaultProps)) || {};
135
+ if (resultProps.compactType === common_1.COMPACT_TYPE_HORIZONTAL_NOWRAP) {
136
+ nowrapGroups[group.id] = {
137
+ items: [],
138
+ leftSpace: resultProps.cols,
139
+ };
140
+ hasNowrapGroups = true;
141
+ }
142
+ });
143
+ }
144
+ if (hasNowrapGroups) {
145
+ layout.forEach((item) => {
146
+ const widgetId = item.i;
147
+ const parentId = item.parent || common_1.DEFAULT_GROUP;
148
+ if (nowrapGroups[parentId]) {
149
+ // Collecting nowrap elements
150
+ nowrapGroups[parentId].items.push(item);
151
+ nowrapGroups[parentId].leftSpace -= item.w;
152
+ }
153
+ else if (nowrapAdjustedLayouts.current[widgetId]) {
154
+ // If element is not in horizontal-nowrap cleaning up and reverting adjustLayout values
155
+ delete nowrapAdjustedLayouts.current[widgetId];
156
+ }
157
+ });
158
+ Object.entries(nowrapGroups).forEach(([, { items, leftSpace }]) => {
159
+ items.forEach((item) => {
160
+ // setting maxW with adjustLayout fields and saving previous
161
+ nowrapAdjustedLayouts.current[item.i] = item.w + leftSpace;
162
+ });
163
+ });
164
+ }
165
+ }, [props.registerManager, props.groups, props.layout]);
166
+ const itemsParams = (0, useDeepEqualMemo_1.useDeepEqualMemo)(() => (0, shared_1.getItemsParams)({
110
167
  defaultGlobalParams: props.defaultGlobalParams,
111
168
  globalParams: props.globalParams,
112
169
  config: props.config,
@@ -119,7 +176,7 @@ function useMemoStateContext(props) {
119
176
  props.itemsStateAndParams,
120
177
  props.registerManager,
121
178
  ]);
122
- const itemsState = react_1.default.useMemo(() => (0, shared_1.getItemsState)({
179
+ const itemsState = (0, useDeepEqualMemo_1.useDeepEqualMemo)(() => (0, shared_1.getItemsState)({
123
180
  config: props.config,
124
181
  itemsStateAndParams: props.itemsStateAndParams,
125
182
  }), [props.config, props.itemsStateAndParams]);
@@ -134,12 +191,31 @@ function useMemoStateContext(props) {
134
191
  .filter(Boolean);
135
192
  }, []);
136
193
  const resultLayout = react_1.default.useMemo(() => {
194
+ const adjusted = adjustedLayouts.current;
195
+ const original = originalLayouts.current;
196
+ const nowrapAdjust = nowrapAdjustedLayouts.current;
137
197
  return props.layout.map((item) => {
138
- if (item.i in adjustedLayouts.current) {
139
- return Object.assign(Object.assign({}, adjustedLayouts.current[item.i]), { w: item.w, x: item.x, y: item.y });
198
+ const widgetId = item.i;
199
+ if (widgetId in adjusted || widgetId in nowrapAdjust) {
200
+ original[widgetId] = item;
201
+ // eslint-disable-next-line no-unused-vars
202
+ const _a = adjusted[widgetId] || item, { parent } = _a, adjustedItem = tslib_1.__rest(_a, ["parent"]);
203
+ adjustedItem.w = item.w;
204
+ adjustedItem.x = item.x;
205
+ adjustedItem.y = item.y;
206
+ if (item.parent) {
207
+ adjustedItem.parent = item.parent;
208
+ }
209
+ if (widgetId in nowrapAdjust) {
210
+ adjustedItem.maxW = nowrapAdjust[widgetId];
211
+ }
212
+ return adjustedItem;
140
213
  }
141
214
  else {
142
- return Object.assign({}, item);
215
+ if (widgetId in original) {
216
+ delete original[widgetId];
217
+ }
218
+ return item;
143
219
  }
144
220
  });
145
221
  }, [props.layout, layoutUpdateCounter]);
@@ -161,17 +237,31 @@ function useMemoStateContext(props) {
161
237
  return null;
162
238
  }
163
239
  }, [dragProps, props.registerManager]);
164
- const onDropDragOver = react_1.default.useCallback(() => {
240
+ const onDropDragOver = react_1.default.useCallback((_e, gridProps, groupLayout) => {
165
241
  if (temporaryLayout) {
166
242
  resetTemporaryLayout();
167
243
  return false;
168
244
  }
169
- if (dragOverPlugin) {
170
- const { defaultLayout } = dragOverPlugin;
171
- 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 || {};
172
- return { h, w };
245
+ if (!dragOverPlugin) {
246
+ return false;
247
+ }
248
+ let maxW = gridProps.cols;
249
+ const { defaultLayout } = dragOverPlugin;
250
+ const maxH = Math.min(gridProps.maxRows || Infinity, defaultLayout.maxH || Infinity);
251
+ if (gridProps.compactType === common_1.COMPACT_TYPE_HORIZONTAL_NOWRAP) {
252
+ maxW = groupLayout.reduce((memo, item) => memo - item.w, gridProps.cols);
253
+ }
254
+ if (maxW === 0 ||
255
+ maxH === 0 ||
256
+ maxW < defaultLayout.minW ||
257
+ maxH < defaultLayout.minH) {
258
+ return false;
173
259
  }
174
- return false;
260
+ 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 || {};
261
+ return {
262
+ h: maxH ? Math.min(h, maxH) : h,
263
+ w: maxW ? Math.min(w, maxW) : w,
264
+ };
175
265
  }, [resetTemporaryLayout, temporaryLayout, dragOverPlugin, dragProps]);
176
266
  const onDropProp = props.onDrop;
177
267
  const onDrop = react_1.default.useCallback((newLayout, item) => {
@@ -185,16 +275,16 @@ function useMemoStateContext(props) {
185
275
  onDropProp({
186
276
  newLayout: newLayout.reduce((memo, l) => {
187
277
  if (l.i !== item.i) {
188
- memo.push((0, pick_1.default)(l, ['i', 'h', 'w', 'x', 'y', 'parent']));
278
+ memo.push((0, pick_1.default)(l, ITEM_PROPS));
189
279
  }
190
280
  return memo;
191
281
  }, []),
192
- itemLayout: (0, pick_1.default)(item, ['i', 'h', 'w', 'x', 'y', 'parent']),
282
+ itemLayout: (0, pick_1.default)(item, ITEM_PROPS),
193
283
  commit: resetTemporaryLayout,
194
284
  dragProps,
195
285
  });
196
286
  }, [dragProps, onDropProp, setTemporaryLayout, resetTemporaryLayout]);
197
- return react_1.default.useMemo(() => ({
287
+ const dashkitContextValue = react_1.default.useMemo(() => ({
198
288
  layout: resultLayout,
199
289
  temporaryLayout,
200
290
  config: props.config,
@@ -210,10 +300,8 @@ function useMemoStateContext(props) {
210
300
  itemsParams,
211
301
  registerManager: props.registerManager,
212
302
  onItemStateAndParamsChange,
213
- removeItem: onItemRemove,
214
303
  onDrop,
215
304
  onDropDragOver,
216
- editItem: props.onItemEdit,
217
305
  layoutChange: onLayoutChange,
218
306
  getItemsMeta,
219
307
  reloadItems,
@@ -238,10 +326,8 @@ function useMemoStateContext(props) {
238
326
  itemsParams,
239
327
  props.registerManager,
240
328
  onItemStateAndParamsChange,
241
- onItemRemove,
242
329
  onDrop,
243
330
  onDropDragOver,
244
- props.onItemEdit,
245
331
  onLayoutChange,
246
332
  getItemsMeta,
247
333
  reloadItems,
@@ -252,12 +338,34 @@ function useMemoStateContext(props) {
252
338
  outerDnDEnable,
253
339
  dragOverPlugin,
254
340
  ]);
341
+ const overlayMenuItems = props.overlayMenuItems || props.registerManager.settings.menu;
342
+ const controlsContextValue = react_1.default.useMemo(() => ({
343
+ overlayControls: props.overlayControls,
344
+ context: props.context,
345
+ menu: overlayMenuItems,
346
+ itemsParams: itemsParams,
347
+ itemsState: itemsState,
348
+ editItem: props.onItemEdit,
349
+ removeItem: onItemRemove,
350
+ getLayoutItem: getLayoutItem,
351
+ }), [
352
+ itemsParams,
353
+ itemsState,
354
+ props.context,
355
+ props.onItemEdit,
356
+ onItemRemove,
357
+ props.overlayControls,
358
+ overlayMenuItems,
359
+ getLayoutItem,
360
+ ]);
361
+ return { controlsContextValue, dashkitContextValue };
255
362
  }
256
363
  function withContext(Component) {
257
364
  const WithContext = (props) => {
258
- const contextValue = useMemoStateContext(props);
259
- return (react_1.default.createElement(DashKitContext_1.DashKitContext.Provider, { value: contextValue },
260
- react_1.default.createElement(Component, { overlayControls: props.overlayControls })));
365
+ const { dashkitContextValue, controlsContextValue } = useMemoStateContext(props);
366
+ return (react_1.default.createElement(DashKitContext_1.DashKitContext.Provider, { value: dashkitContextValue },
367
+ react_1.default.createElement(DashKitContext_1.DashkitOvelayControlsContext.Provider, { value: controlsContextValue },
368
+ react_1.default.createElement(Component, null))));
261
369
  };
262
370
  WithContext.displayName = `withContext(${Component.displayName || Component.name || 'Component'}`;
263
371
  return WithContext;
@@ -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,17 @@
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
+ return react_1.default.useMemo(() => {
10
+ const value = predicate();
11
+ if (!(0, isEqual_1.default)(previousValueRef.current, value)) {
12
+ previousValueRef.current = value;
13
+ }
14
+ return previousValueRef.current;
15
+ }, [previousValueRef, ...deps]);
16
+ };
17
+ exports.useDeepEqualMemo = useDeepEqualMemo;
@@ -19,7 +19,9 @@ export interface DashkitGroupRenderProps {
19
19
  items: ConfigItem[];
20
20
  layout: ConfigLayout[];
21
21
  }
22
- export type ReactGridLayoutProps = Omit<ReactGridLayout.ReactGridLayoutProps, 'children' | 'innerRef' | 'key' | 'layout' | 'isDraggable' | 'isResizable' | 'onDragStart' | 'onDragStop' | 'onResizeStart' | 'onResizeStop' | 'draggableHandle' | 'isDroppable' | 'onDropDragOver' | 'onDrop' | 'draggableCancel'>;
22
+ export type ReactGridLayoutProps = Omit<ReactGridLayout.ReactGridLayoutProps, 'children' | 'compactType' | 'innerRef' | 'key' | 'layout' | 'isDraggable' | 'isResizable' | 'onDragStart' | 'onDragStop' | 'onResizeStart' | 'onResizeStop' | 'draggableHandle' | 'isDroppable' | 'onDropDragOver' | 'onDrop' | 'draggableCancel'> & {
23
+ compactType?: ReactGridLayout.ReactGridLayoutProps['compactType'] | 'horizontal-nowrap';
24
+ };
23
25
  export interface DashKitGroup {
24
26
  id?: string;
25
27
  render?: (id: string, children: React.ReactNode, props: DashkitGroupRenderProps) => React.ReactNode;
@@ -1,13 +1,14 @@
1
1
  import React from 'react';
2
2
  import type { Config, ConfigItem, GlobalParams, ItemDropProps, ItemsStateAndParams } from '../../shared';
3
- import { ContextProps, DashKitGroup, Plugin, SetConfigItem, SetNewItemOptions, Settings, SettingsProps } from '../../typings';
3
+ import { ContextProps, DashKitGroup, MenuItem, Plugin, SetConfigItem, SetNewItemOptions, Settings, SettingsProps } from '../../typings';
4
4
  import GridLayout from '../GridLayout/GridLayout';
5
5
  import { OverlayControlItem } from '../OverlayControls/OverlayControls';
6
6
  interface DashKitGeneralProps {
7
7
  config: Config;
8
8
  editMode: boolean;
9
9
  draggableHandleClassName?: string;
10
- overlayControls?: Record<string, OverlayControlItem[]>;
10
+ overlayControls?: Record<string, OverlayControlItem[]> | null;
11
+ overlayMenuItems?: MenuItem[] | null;
11
12
  }
12
13
  interface DashKitDefaultProps {
13
14
  onItemEdit: (item: ConfigItem) => void;
@@ -7,10 +7,10 @@ import GridLayout from '../GridLayout/GridLayout';
7
7
  import MobileLayout from '../MobileLayout/MobileLayout';
8
8
  import './DashKitView.css';
9
9
  const b = cn('dashkit');
10
- function DashKitView(props) {
10
+ function DashKitView() {
11
11
  const context = React.useContext(DashKitContext);
12
12
  const { registerManager, forwardedMetaRef } = context;
13
- return (React.createElement("div", { className: b({ theme: registerManager.settings.theme }) }, registerManager.settings.isMobile ? (React.createElement(MobileLayout, null)) : (React.createElement(GridLayout, { ref: forwardedMetaRef, overlayControls: props.overlayControls }))));
13
+ return (React.createElement("div", { className: b({ theme: registerManager.settings.theme }) }, registerManager.settings.isMobile ? (React.createElement(MobileLayout, null)) : (React.createElement(GridLayout, { ref: forwardedMetaRef }))));
14
14
  }
15
15
  const DashKitViewWithContext = withContext(DashKitView);
16
16
  const DashKitViewForwardedMeta = React.forwardRef((props, ref) => {
@@ -56,16 +56,15 @@ class GridItem extends React.PureComponent {
56
56
  };
57
57
  }
58
58
  renderOverlay() {
59
- const { overlayControls, isPlaceholder } = this.props;
59
+ const { isPlaceholder } = this.props;
60
60
  const { editMode } = this.context;
61
61
  if (!editMode || this.props.item.data._editActive || isPlaceholder) {
62
62
  return null;
63
63
  }
64
64
  const { item } = this.props;
65
- const controls = overlayControls && overlayControls[item.type];
66
65
  return (React.createElement(React.Fragment, null,
67
66
  React.createElement("div", { className: b('overlay') }),
68
- React.createElement(OverlayControls, { configItem: item, items: controls, overlayControls: overlayControls })));
67
+ React.createElement(OverlayControls, { configItem: item })));
69
68
  }
70
69
  render() {
71
70
  // из-за бага, что Grid Items unmounts при изменении static, isDraggable, isResaizable
@@ -106,7 +105,6 @@ GridItem.propTypes = {
106
105
  item: PropTypes.object,
107
106
  isDragging: PropTypes.bool,
108
107
  layout: PropTypes.array,
109
- overlayControls: PropTypes.object,
110
108
  forwardedRef: PropTypes.any,
111
109
  forwardedPluginRef: PropTypes.any,
112
110
  isPlaceholder: PropTypes.bool,
@@ -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) => 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;
@@ -1,11 +1,15 @@
1
1
  import React from 'react';
2
- import { DEFAULT_GROUP, OVERLAY_CONTROLS_CLASS_NAME, TEMPORARY_ITEM_ID } from '../../constants';
2
+ import { COMPACT_TYPE_HORIZONTAL_NOWRAP, DEFAULT_GROUP, OVERLAY_CONTROLS_CLASS_NAME, TEMPORARY_ITEM_ID, } from '../../constants';
3
3
  import { DashKitContext } from '../../context/DashKitContext';
4
4
  import GridItem from '../GridItem/GridItem';
5
5
  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) => {
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);
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;
@@ -126,26 +194,29 @@ class GridLayout extends React.PureComponent {
126
194
  }
127
195
  const id = TEMPORARY_ITEM_ID;
128
196
  const { type } = temporaryLayout.dragProps;
129
- 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 }));
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) }));
130
198
  }
131
199
  renderGroup(group, renderLayout, renderItems, offset = 0, groupGridProperties) {
132
200
  const { registerManager, editMode, noOverlay, focusable, draggableHandleClassName, outerDnDEnable, } = this.context;
133
201
  const properties = groupGridProperties
134
202
  ? groupGridProperties(Object.assign({}, registerManager.gridLayout))
135
203
  : registerManager.gridLayout;
136
- return (React.createElement(Layout, Object.assign({}, properties, { 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
204
+ let { compactType } = properties;
205
+ if (compactType === COMPACT_TYPE_HORIZONTAL_NOWRAP) {
206
+ compactType = 'horizontal';
207
+ }
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
137
210
  ? { draggableHandle: `.${draggableHandleClassName}` }
138
211
  : null), (outerDnDEnable
139
212
  ? {
140
213
  isDroppable: true,
141
- onDropDragOver: this._onDropDragOver,
142
- onDrop: (...args) => this._onDrop(group, ...args),
214
+ onDropDragOver: callbacks.onDropDragOver,
215
+ onDrop: callbacks.onDrop,
143
216
  }
144
217
  : null), { draggableCancel: `.${OVERLAY_CONTROLS_CLASS_NAME}` }),
145
218
  renderItems.map((item, i) => {
146
- return (React.createElement(GridItem, { forwardedPluginRef: (pluginRef) => {
147
- this.pluginsRefs[offset + i] = pluginRef;
148
- }, 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) }));
149
220
  }),
150
221
  this.renderTemporaryPlaceholder()));
151
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;