@gravity-ui/dashkit 8.3.0 → 8.5.0-beta.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.
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import type { Config, ConfigItem, GlobalParams, ItemDropProps, ItemsStateAndParams } from '../../shared';
3
- import { ContextProps, Plugin, SetConfigItem, SetNewItemOptions, Settings, SettingsProps } from '../../typings';
3
+ import { ContextProps, DashKitGroup, 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 {
@@ -14,6 +14,7 @@ interface DashKitDefaultProps {
14
14
  onChange: (data: {
15
15
  config: Config;
16
16
  itemsStateAndParams: ItemsStateAndParams;
17
+ groups?: DashKitGroup[];
17
18
  }) => void;
18
19
  onDrop: (dropProps: ItemDropProps) => void;
19
20
  defaultGlobalParams: GlobalParams;
@@ -23,6 +24,7 @@ interface DashKitDefaultProps {
23
24
  context: ContextProps;
24
25
  noOverlay: boolean;
25
26
  focusable?: boolean;
27
+ groups?: DashKitGroup[];
26
28
  }
27
29
  export interface DashKitProps extends DashKitGeneralProps, Partial<DashKitDefaultProps> {
28
30
  }
@@ -44,11 +44,7 @@ class DashKit extends react_1.default.PureComponent {
44
44
  return utils_1.UpdateManager.removeItem({ id, config, itemsStateAndParams });
45
45
  }
46
46
  render() {
47
- return (react_1.default.createElement(DashKitView_1.default
48
- // @ts-ignore
49
- , Object.assign({
50
- // @ts-ignore
51
- registerManager: registerManager, ref: this.metaRef }, this.props)));
47
+ return react_1.default.createElement(DashKitView_1.default, Object.assign({ registerManager: registerManager, ref: this.metaRef }, this.props));
52
48
  }
53
49
  getItemsMeta() {
54
50
  var _a;
@@ -18,12 +18,17 @@ export default class GridLayout extends React.PureComponent<any, any, any> {
18
18
  adjustedWidgetLayout: any;
19
19
  }) => void;
20
20
  getItemsMeta: () => any;
21
+ getActiveLayout(): any;
22
+ mergeGroupsLayout(group: any, newLayout: any, temporaryItem: any): any;
21
23
  reloadItems(): void;
22
24
  _onStart: () => void;
23
- _onStop: (newLayout: any) => void;
24
- _onDropDragOver: (e: any) => any;
25
- _onDrop: (layout: any, item: any, e: any) => false | undefined;
25
+ _onStop: (group: any, newLayout: any) => void;
26
+ _onDropDragOver: (e: any, gridProperties: any, layout: any) => any;
27
+ _onDrop: (group: any, newLayout: any, item: any, e: any) => false | undefined;
26
28
  renderTemporaryPlaceholder(): JSX.Element | null;
27
- render(): JSX.Element;
29
+ caclculateEmptyHorizontalSpace(layout: any, cols: any): any;
30
+ getElementLayout(layout: any, properties: any): any;
31
+ renderGroup(group: any, renderLayout: any, renderItems: any, offset: number | undefined, groupGridProperties: any): JSX.Element;
32
+ render(): any;
28
33
  }
29
34
  import React from 'react';
@@ -35,24 +35,33 @@ class GridLayout extends react_1.default.PureComponent {
35
35
  return;
36
36
  this.setState({ isDragging: true });
37
37
  };
38
- this._onStop = (newLayout) => {
38
+ this._onStop = (group, newLayout) => {
39
39
  const { layoutChange, onDrop, temporaryLayout } = this.context;
40
+ const groupedLayout = this.mergeGroupsLayout(group, newLayout);
40
41
  if (temporaryLayout) {
41
- onDrop === null || onDrop === void 0 ? void 0 : onDrop(newLayout, newLayout.find(({ i }) => i === constants_1.TEMPORARY_ITEM_ID));
42
+ onDrop === null || onDrop === void 0 ? void 0 : onDrop(groupedLayout, groupedLayout.find(({ i }) => i === constants_1.TEMPORARY_ITEM_ID));
42
43
  }
43
44
  else {
44
- layoutChange(newLayout);
45
+ layoutChange(groupedLayout);
45
46
  }
46
47
  this.setState({ isDragging: false });
47
48
  };
48
- this._onDropDragOver = (e) => {
49
+ this._onDropDragOver = (e, gridProperties, layout) => {
49
50
  const { editMode, dragOverPlugin, onDropDragOver } = this.context;
50
51
  if (!editMode || !dragOverPlugin) {
51
52
  return false;
52
53
  }
53
- return onDropDragOver(e);
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 });
54
63
  };
55
- this._onDrop = (layout, item, e) => {
64
+ this._onDrop = (group, newLayout, item, e) => {
56
65
  if (!item) {
57
66
  return false;
58
67
  }
@@ -60,7 +69,11 @@ class GridLayout extends react_1.default.PureComponent {
60
69
  if (!editMode) {
61
70
  return false;
62
71
  }
63
- onDrop === null || onDrop === void 0 ? void 0 : onDrop(layout, item, e);
72
+ if (group !== constants_1.DEFAULT_GROUP) {
73
+ item.parent = group;
74
+ }
75
+ const groupedLayout = this.mergeGroupsLayout(group, newLayout, item);
76
+ onDrop === null || onDrop === void 0 ? void 0 : onDrop(groupedLayout, item, e);
64
77
  };
65
78
  this.pluginsRefs = [];
66
79
  this.state = {
@@ -80,6 +93,29 @@ class GridLayout extends react_1.default.PureComponent {
80
93
  clearTimeout(this._timeout);
81
94
  document.removeEventListener('visibilitychange', this.onVisibilityChange);
82
95
  }
96
+ getActiveLayout() {
97
+ const { layout, temporaryLayout } = this.context;
98
+ return (temporaryLayout === null || temporaryLayout === void 0 ? void 0 : temporaryLayout.data) || layout;
99
+ }
100
+ mergeGroupsLayout(group, newLayout, temporaryItem) {
101
+ const renderLayout = this.getActiveLayout();
102
+ const itemsByGroup = renderLayout.reduce((memo, item) => {
103
+ memo[item.i] = item;
104
+ return memo;
105
+ }, temporaryItem ? { [temporaryItem.i]: temporaryItem } : {});
106
+ const newItemsLayoutById = newLayout.reduce((memo, item) => {
107
+ const parent = itemsByGroup[item.i].parent;
108
+ memo[item.i] = Object.assign(Object.assign({}, item), { parent });
109
+ return memo;
110
+ }, {});
111
+ return renderLayout.map((currentItem) => {
112
+ const itemParent = itemsByGroup[currentItem.i].parent || constants_1.DEFAULT_GROUP;
113
+ if (itemParent === group) {
114
+ return newItemsLayoutById[currentItem.i];
115
+ }
116
+ return currentItem;
117
+ });
118
+ }
83
119
  reloadItems() {
84
120
  const { editMode, settings: { autoupdateInterval, silentLoading } = {}, reloadItems, } = this.context;
85
121
  const { isPageHidden } = this.state;
@@ -103,25 +139,105 @@ class GridLayout extends react_1.default.PureComponent {
103
139
  const { type } = temporaryLayout.dragProps;
104
140
  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 }));
105
141
  }
106
- render() {
107
- const { layout, temporaryLayout, config, registerManager, editMode, noOverlay, focusable, draggableHandleClassName, outerDnDEnable, } = this.context;
108
- this.pluginsRefs.length = config.items.length;
109
- return (react_1.default.createElement(ReactGridLayout_1.Layout, Object.assign({}, registerManager.gridLayout, { layout: (temporaryLayout === null || temporaryLayout === void 0 ? void 0 : temporaryLayout.data) || layout, isDraggable: editMode, isResizable: editMode, onDragStart: this._onStart, onDragStop: this._onStop, onResizeStart: this._onStart, onResizeStop: this._onStop }, (draggableHandleClassName
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
+ renderGroup(group, renderLayout, renderItems, offset = 0, groupGridProperties) {
154
+ const { registerManager, editMode, noOverlay, focusable, draggableHandleClassName, outerDnDEnable, } = this.context;
155
+ const properties = groupGridProperties
156
+ ? groupGridProperties(Object.assign({}, registerManager.gridLayout))
157
+ : registerManager.gridLayout;
158
+ let { compactType } = properties;
159
+ if (compactType === constants_1.COMPACT_TYPE_HORIZONTAL_NOWRAP) {
160
+ renderLayout = this.getElementLayout(renderLayout, properties);
161
+ compactType = 'horizontal';
162
+ }
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
110
164
  ? { draggableHandle: `.${draggableHandleClassName}` }
111
165
  : null), (outerDnDEnable
112
166
  ? {
113
167
  isDroppable: true,
114
- onDropDragOver: this._onDropDragOver,
115
- onDrop: this._onDrop,
168
+ onDropDragOver: (e) => this._onDropDragOver(e, properties, renderLayout),
169
+ onDrop: (...args) => this._onDrop(group, ...args),
116
170
  }
117
171
  : null), { draggableCancel: `.${constants_1.OVERLAY_CONTROLS_CLASS_NAME}` }),
118
- config.items.map((item, i) => {
172
+ renderItems.map((item, i) => {
119
173
  return (react_1.default.createElement(GridItem_1.default, { forwardedPluginRef: (pluginRef) => {
120
- this.pluginsRefs[i] = pluginRef;
121
- }, key: item.id, id: item.id, item: item, layout: (temporaryLayout === null || temporaryLayout === void 0 ? void 0 : temporaryLayout.data) || layout, adjustWidgetLayout: this.adjustWidgetLayout, isDragging: this.state.isDragging, noOverlay: noOverlay, focusable: focusable, withCustomHandle: Boolean(draggableHandleClassName), overlayControls: this.props.overlayControls }));
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 }));
122
176
  }),
123
177
  this.renderTemporaryPlaceholder()));
124
178
  }
179
+ render() {
180
+ const { config, groups, editMode } = this.context;
181
+ this.pluginsRefs.length = config.items.length;
182
+ const defaultRenderLayout = [];
183
+ const defaultRenderItems = [];
184
+ const layoutMap = {};
185
+ const groupedLayout = this.getActiveLayout().reduce((memo, item) => {
186
+ if (item.parent) {
187
+ if (!memo[item.parent]) {
188
+ memo[item.parent] = [];
189
+ }
190
+ memo[item.parent].push(item);
191
+ layoutMap[item.i] = item.parent;
192
+ }
193
+ else {
194
+ defaultRenderLayout.push(item);
195
+ }
196
+ return memo;
197
+ }, []);
198
+ const itemsByGroup = config.items.reduce((memo, item) => {
199
+ const group = layoutMap[item.id];
200
+ if (group) {
201
+ if (!memo[group]) {
202
+ memo[group] = [];
203
+ }
204
+ memo[group].push(item);
205
+ }
206
+ else {
207
+ defaultRenderItems.push(item);
208
+ }
209
+ return memo;
210
+ }, {});
211
+ let offset = 0;
212
+ if (groups) {
213
+ return groups.map((group) => {
214
+ const id = group.id || constants_1.DEFAULT_GROUP;
215
+ let layout, items;
216
+ if (id === constants_1.DEFAULT_GROUP) {
217
+ layout = defaultRenderLayout;
218
+ items = defaultRenderItems;
219
+ }
220
+ else {
221
+ layout = groupedLayout[id] || [];
222
+ items = itemsByGroup[id] || [];
223
+ }
224
+ const element = this.renderGroup(id, layout, items, offset, group.gridProperties);
225
+ offset += items.length;
226
+ if (group.render) {
227
+ return group.render(id, element, {
228
+ config,
229
+ editMode,
230
+ items,
231
+ layout,
232
+ });
233
+ }
234
+ return element;
235
+ });
236
+ }
237
+ else {
238
+ return this.renderGroup(constants_1.DEFAULT_GROUP, defaultRenderLayout, defaultRenderItems, offset);
239
+ }
240
+ }
125
241
  }
126
242
  GridLayout.contextType = DashKitContext_1.DashKitContext;
127
243
  exports.default = GridLayout;
@@ -6,14 +6,29 @@ const react_1 = tslib_1.__importDefault(require("react"));
6
6
  const react_grid_layout_1 = tslib_1.__importStar(require("react-grid-layout"));
7
7
  const constants_1 = require("../../constants");
8
8
  class DragOverLayout extends react_grid_layout_1.default {
9
- constructor() {
10
- super(...arguments);
9
+ constructor(...args) {
10
+ super(...args);
11
11
  this.dragReset = () => {
12
12
  if (this.dragEnterCounter) {
13
13
  this.dragEnterCounter = 0;
14
14
  this.removeDroppingPlaceholder();
15
15
  }
16
16
  };
17
+ this.mouseLeaveHandler = (e) => {
18
+ var _a;
19
+ const rect = ((_a = this.props.innerRef) === null || _a === void 0 ? void 0 : _a.current.getBoundingClientRect()) || {};
20
+ if (rect.bottom <= e.clientY ||
21
+ rect.top >= e.clientY ||
22
+ rect.left >= e.clientX ||
23
+ rect.right <= e.clientX) {
24
+ this.dragReset();
25
+ }
26
+ };
27
+ this.parentOnDrag = this.onDrag;
28
+ this.onDrag = (i, x, y, sintE) => {
29
+ this.parentOnDrag(i, x, y, sintE);
30
+ this.mouseLeaveHandler(sintE.e);
31
+ };
17
32
  }
18
33
  componentDidMount() {
19
34
  var _a;
@@ -23,6 +38,12 @@ class DragOverLayout extends react_grid_layout_1.default {
23
38
  componentWillUnmount() {
24
39
  window.removeEventListener('dragend', this.dragReset);
25
40
  }
41
+ containerHeight() {
42
+ if (this.props.autoSize && this.state.layout.length === 0) {
43
+ return 'unset';
44
+ }
45
+ return super.containerHeight();
46
+ }
26
47
  processGridItem(child, isDroppingItem) {
27
48
  var _a;
28
49
  const gridItem = (_a = super.processGridItem) === null || _a === void 0 ? void 0 : _a.call(this, child, isDroppingItem);
@@ -10,3 +10,5 @@ export declare const MenuItems: {
10
10
  };
11
11
  export declare const DEFAULT_WIDGET_HEIGHT = 3;
12
12
  export declare const DEFAULT_WIDGET_WIDTH = 3;
13
+ export declare const DEFAULT_GROUP = "__default";
14
+ export declare const COMPACT_TYPE_HORIZONTAL_NOWRAP = "horizontal-nowrap";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_WIDGET_WIDTH = exports.DEFAULT_WIDGET_HEIGHT = exports.MenuItems = exports.TEMPORARY_ITEM_ID = exports.OVERLAY_CLASS_NAME = exports.OVERLAY_CONTROLS_CLASS_NAME = exports.DEFAULT_NAMESPACE = exports.COPIED_WIDGET_STORE_KEY = void 0;
3
+ exports.COMPACT_TYPE_HORIZONTAL_NOWRAP = exports.DEFAULT_GROUP = exports.DEFAULT_WIDGET_WIDTH = exports.DEFAULT_WIDGET_HEIGHT = exports.MenuItems = exports.TEMPORARY_ITEM_ID = exports.OVERLAY_CLASS_NAME = exports.OVERLAY_CONTROLS_CLASS_NAME = exports.DEFAULT_NAMESPACE = exports.COPIED_WIDGET_STORE_KEY = void 0;
4
4
  exports.COPIED_WIDGET_STORE_KEY = 'dashCopiedItem';
5
5
  exports.DEFAULT_NAMESPACE = 'default';
6
6
  exports.OVERLAY_CONTROLS_CLASS_NAME = 'dashkit-overlay-controls';
@@ -13,3 +13,5 @@ exports.MenuItems = {
13
13
  };
14
14
  exports.DEFAULT_WIDGET_HEIGHT = 3;
15
15
  exports.DEFAULT_WIDGET_WIDTH = 3;
16
+ exports.DEFAULT_GROUP = '__default';
17
+ exports.COMPACT_TYPE_HORIZONTAL_NOWRAP = 'horizontal-nowrap';
@@ -4,6 +4,7 @@ exports.withContext = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const react_1 = tslib_1.__importDefault(require("react"));
6
6
  const isEqual_1 = tslib_1.__importDefault(require("lodash/isEqual"));
7
+ const pick_1 = tslib_1.__importDefault(require("lodash/pick"));
7
8
  const common_1 = require("../constants/common");
8
9
  const DashKitContext_1 = require("../context/DashKitContext");
9
10
  const shared_1 = require("../shared");
@@ -22,10 +23,11 @@ function useMemoStateContext(props) {
22
23
  const outerDnDEnable = Boolean(dndContext);
23
24
  // TODO: need move originalLayouts, adjustedLayouts to state
24
25
  const [layoutUpdateCounter, forceUpdateLayout] = react_1.default.useState(0);
25
- const onChange = react_1.default.useCallback(({ config = props.config, itemsStateAndParams = props.itemsStateAndParams }) => {
26
+ const onChange = react_1.default.useCallback(({ config = props.config, itemsStateAndParams = props.itemsStateAndParams, groups = props.groups, }) => {
26
27
  if (!((0, isEqual_1.default)(config, props.config) &&
27
- (0, isEqual_1.default)(itemsStateAndParams, props.itemsStateAndParams))) {
28
- props.onChange({ config, itemsStateAndParams });
28
+ (0, isEqual_1.default)(itemsStateAndParams, props.itemsStateAndParams) &&
29
+ (0, isEqual_1.default)(groups, props.groups))) {
30
+ props.onChange({ config, itemsStateAndParams, groups });
29
31
  }
30
32
  }, [props.config, props.itemsStateAndParams, props.onChange]);
31
33
  // каллбэк вызывающийся при изменение лэйаута сетки, первым аргументом приходит актуальный конфиг лэйаута,
@@ -159,7 +161,7 @@ function useMemoStateContext(props) {
159
161
  return null;
160
162
  }
161
163
  }, [dragProps, props.registerManager]);
162
- const onDropDragOver = react_1.default.useCallback(() => {
164
+ const onDropDragOver = react_1.default.useCallback((_e, gridProps = {}) => {
163
165
  if (temporaryLayout) {
164
166
  resetTemporaryLayout();
165
167
  return false;
@@ -167,10 +169,14 @@ function useMemoStateContext(props) {
167
169
  if (dragOverPlugin) {
168
170
  const { defaultLayout } = dragOverPlugin;
169
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 || {};
170
- return { h, w };
172
+ return {
173
+ h: gridProps.maxH ? Math.min(h, gridProps.maxH) : h,
174
+ w: gridProps.maxW ? Math.min(w, gridProps.maxW) : w,
175
+ };
171
176
  }
172
177
  return false;
173
178
  }, [resetTemporaryLayout, temporaryLayout, dragOverPlugin, dragProps]);
179
+ const onDropProp = props.onDrop;
174
180
  const onDrop = react_1.default.useCallback((newLayout, item) => {
175
181
  if (!dragProps) {
176
182
  return;
@@ -179,23 +185,23 @@ function useMemoStateContext(props) {
179
185
  data: newLayout,
180
186
  dragProps,
181
187
  });
182
- const { i, w, h, x, y } = item;
183
- props.onDrop({
188
+ onDropProp({
184
189
  newLayout: newLayout.reduce((memo, l) => {
185
- if (l.i !== i) {
186
- memo.push({ i: l.i, w: l.w, h: l.h, x: l.x, y: l.y });
190
+ if (l.i !== item.i) {
191
+ memo.push((0, pick_1.default)(l, ['i', 'h', 'w', 'x', 'y', 'parent']));
187
192
  }
188
193
  return memo;
189
194
  }, []),
190
- itemLayout: { w, h, x, y },
195
+ itemLayout: (0, pick_1.default)(item, ['i', 'h', 'w', 'x', 'y', 'parent']),
191
196
  commit: resetTemporaryLayout,
192
197
  dragProps,
193
198
  });
194
- }, [dragProps, props.onDrop, setTemporaryLayout, resetTemporaryLayout]);
199
+ }, [dragProps, onDropProp, setTemporaryLayout, resetTemporaryLayout]);
195
200
  return react_1.default.useMemo(() => ({
196
201
  layout: resultLayout,
197
202
  temporaryLayout,
198
203
  config: props.config,
204
+ groups: props.groups,
199
205
  context: props.context,
200
206
  noOverlay: props.noOverlay,
201
207
  focusable: props.focusable,
@@ -224,6 +230,7 @@ function useMemoStateContext(props) {
224
230
  resultLayout,
225
231
  temporaryLayout,
226
232
  props.config,
233
+ props.groups,
227
234
  props.context,
228
235
  props.noOverlay,
229
236
  props.focusable,
@@ -6,6 +6,7 @@ export declare const useCalcPropsLayout: (config: Config, registerManager: Regis
6
6
  w: number;
7
7
  x: number;
8
8
  y: number;
9
+ parent?: string | undefined;
9
10
  minW: number;
10
11
  minH: number;
11
12
  maxW?: number | undefined;
@@ -1,5 +1,8 @@
1
1
  import { StringParams } from './common';
2
- export interface ConfigLayout {
2
+ export interface AdditionalWidgetLayout {
3
+ parent?: string;
4
+ }
5
+ export interface ConfigLayout extends AdditionalWidgetLayout {
3
6
  i: string;
4
7
  h: number;
5
8
  w: number;
@@ -1,6 +1,7 @@
1
1
  import type ReactGridLayout from 'react-grid-layout';
2
2
  import type { OverlayCustomControlItem } from '../components/OverlayControls/OverlayControls';
3
3
  import { MenuItems } from '../constants';
4
+ import { AdditionalWidgetLayout } from '../shared';
4
5
  export interface Settings {
5
6
  gridLayout?: ReactGridLayout.ReactGridLayoutProps;
6
7
  theme?: string;
@@ -15,7 +16,7 @@ export interface SettingsProps {
15
16
  export interface ContextProps {
16
17
  [key: string]: any;
17
18
  }
18
- export interface WidgetLayout {
19
+ export interface WidgetLayout extends AdditionalWidgetLayout {
19
20
  i: string;
20
21
  w: number;
21
22
  h: number;
@@ -1,4 +1,6 @@
1
- import type { ConfigItem, ConfigLayout } from '../shared';
1
+ /// <reference types="react-grid-layout" />
2
+ /// <reference types="react" />
3
+ import type { Config, ConfigItem, ConfigLayout } from '../shared';
2
4
  export interface AddConfigItem extends Omit<ConfigItem, 'id' | 'namespace'> {
3
5
  id?: null;
4
6
  namespace?: string;
@@ -11,3 +13,17 @@ export type SetItemOptions = {
11
13
  export type SetNewItemOptions = SetItemOptions & {
12
14
  updateLayout?: ConfigLayout[];
13
15
  };
16
+ export interface DashkitGroupRenderProps {
17
+ config: Config;
18
+ editMode: boolean;
19
+ items: ConfigItem[];
20
+ layout: ConfigLayout[];
21
+ }
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
+ };
25
+ export interface DashKitGroup {
26
+ id?: string;
27
+ render?: (id: string, children: React.ReactNode, props: DashkitGroupRenderProps) => React.ReactNode;
28
+ gridProperties?: (props: ReactGridLayoutProps) => ReactGridLayoutProps;
29
+ }
@@ -23,16 +23,7 @@ export interface PluginWidgetProps<T = StringParams> {
23
23
  adjustedWidgetLayout?: WidgetLayout;
24
24
  }) => void;
25
25
  }
26
- export interface PluginDefaultLayout {
27
- w?: number;
28
- h?: number;
29
- x?: number;
30
- y?: number;
31
- minW?: number;
32
- maxW?: number;
33
- minH?: number;
34
- maxH?: number;
35
- }
26
+ export type PluginDefaultLayout = Partial<Omit<WidgetLayout, 'i'>>;
36
27
  export interface Plugin<P extends PluginWidgetProps<T> = any, T = StringParams> extends PluginBase {
37
28
  defaultLayout?: PluginDefaultLayout;
38
29
  renderer: (props: P, forwardedRef: React.RefObject<any>) => React.ReactNode;
@@ -234,7 +234,7 @@ class UpdateManager {
234
234
  const newIdData = (0, get_new_id_1.getNewId)({ config, salt, counter, excludeIds: newItemData.excludeIds });
235
235
  counter = newIdData.counter;
236
236
  const newItem = Object.assign(Object.assign({}, item), { id: newIdData.id, data: newItemData.data, namespace });
237
- const saveDefaultLayout = (0, pick_1.default)(layout, ['h', 'w', 'x', 'y']);
237
+ const saveDefaultLayout = (0, pick_1.default)(layout, ['h', 'w', 'x', 'y', 'parent']);
238
238
  if (options.updateLayout) {
239
239
  const byId = options.updateLayout.reduce((memo, t) => {
240
240
  memo[t.i] = t;
@@ -315,7 +315,7 @@ class UpdateManager {
315
315
  static updateLayout({ layout, config }) {
316
316
  return (0, immutability_helper_1.default)(config, {
317
317
  layout: {
318
- $set: layout.map(({ x, y, w, h, i }) => ({ x, y, w, h, i })),
318
+ $set: layout.map((item) => (0, pick_1.default)(item, ['i', 'h', 'w', 'x', 'y', 'parent'])),
319
319
  },
320
320
  });
321
321
  }
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import type { Config, ConfigItem, GlobalParams, ItemDropProps, ItemsStateAndParams } from '../../shared';
3
- import { ContextProps, Plugin, SetConfigItem, SetNewItemOptions, Settings, SettingsProps } from '../../typings';
3
+ import { ContextProps, DashKitGroup, 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 {
@@ -14,6 +14,7 @@ interface DashKitDefaultProps {
14
14
  onChange: (data: {
15
15
  config: Config;
16
16
  itemsStateAndParams: ItemsStateAndParams;
17
+ groups?: DashKitGroup[];
17
18
  }) => void;
18
19
  onDrop: (dropProps: ItemDropProps) => void;
19
20
  defaultGlobalParams: GlobalParams;
@@ -23,6 +24,7 @@ interface DashKitDefaultProps {
23
24
  context: ContextProps;
24
25
  noOverlay: boolean;
25
26
  focusable?: boolean;
27
+ groups?: DashKitGroup[];
26
28
  }
27
29
  export interface DashKitProps extends DashKitGeneralProps, Partial<DashKitDefaultProps> {
28
30
  }
@@ -40,11 +40,7 @@ export class DashKit extends React.PureComponent {
40
40
  return UpdateManager.removeItem({ id, config, itemsStateAndParams });
41
41
  }
42
42
  render() {
43
- return (React.createElement(DashKitView
44
- // @ts-ignore
45
- , Object.assign({
46
- // @ts-ignore
47
- registerManager: registerManager, ref: this.metaRef }, this.props)));
43
+ return React.createElement(DashKitView, Object.assign({ registerManager: registerManager, ref: this.metaRef }, this.props));
48
44
  }
49
45
  getItemsMeta() {
50
46
  var _a;
@@ -18,12 +18,17 @@ export default class GridLayout extends React.PureComponent<any, any, any> {
18
18
  adjustedWidgetLayout: any;
19
19
  }) => void;
20
20
  getItemsMeta: () => any;
21
+ getActiveLayout(): any;
22
+ mergeGroupsLayout(group: any, newLayout: any, temporaryItem: any): any;
21
23
  reloadItems(): void;
22
24
  _onStart: () => void;
23
- _onStop: (newLayout: any) => void;
24
- _onDropDragOver: (e: any) => any;
25
- _onDrop: (layout: any, item: any, e: any) => false | undefined;
25
+ _onStop: (group: any, newLayout: any) => void;
26
+ _onDropDragOver: (e: any, gridProperties: any, layout: any) => any;
27
+ _onDrop: (group: any, newLayout: any, item: any, e: any) => false | undefined;
26
28
  renderTemporaryPlaceholder(): JSX.Element | null;
27
- render(): JSX.Element;
29
+ caclculateEmptyHorizontalSpace(layout: any, cols: any): any;
30
+ getElementLayout(layout: any, properties: any): any;
31
+ renderGroup(group: any, renderLayout: any, renderItems: any, offset: number | undefined, groupGridProperties: any): JSX.Element;
32
+ render(): any;
28
33
  }
29
34
  import React from 'react';
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { 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';
@@ -32,24 +32,33 @@ class GridLayout extends React.PureComponent {
32
32
  return;
33
33
  this.setState({ isDragging: true });
34
34
  };
35
- this._onStop = (newLayout) => {
35
+ this._onStop = (group, newLayout) => {
36
36
  const { layoutChange, onDrop, temporaryLayout } = this.context;
37
+ const groupedLayout = this.mergeGroupsLayout(group, newLayout);
37
38
  if (temporaryLayout) {
38
- onDrop === null || onDrop === void 0 ? void 0 : onDrop(newLayout, newLayout.find(({ i }) => i === TEMPORARY_ITEM_ID));
39
+ onDrop === null || onDrop === void 0 ? void 0 : onDrop(groupedLayout, groupedLayout.find(({ i }) => i === TEMPORARY_ITEM_ID));
39
40
  }
40
41
  else {
41
- layoutChange(newLayout);
42
+ layoutChange(groupedLayout);
42
43
  }
43
44
  this.setState({ isDragging: false });
44
45
  };
45
- this._onDropDragOver = (e) => {
46
+ this._onDropDragOver = (e, gridProperties, layout) => {
46
47
  const { editMode, dragOverPlugin, onDropDragOver } = this.context;
47
48
  if (!editMode || !dragOverPlugin) {
48
49
  return false;
49
50
  }
50
- return onDropDragOver(e);
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 });
51
60
  };
52
- this._onDrop = (layout, item, e) => {
61
+ this._onDrop = (group, newLayout, item, e) => {
53
62
  if (!item) {
54
63
  return false;
55
64
  }
@@ -57,7 +66,11 @@ class GridLayout extends React.PureComponent {
57
66
  if (!editMode) {
58
67
  return false;
59
68
  }
60
- onDrop === null || onDrop === void 0 ? void 0 : onDrop(layout, item, e);
69
+ if (group !== DEFAULT_GROUP) {
70
+ item.parent = group;
71
+ }
72
+ const groupedLayout = this.mergeGroupsLayout(group, newLayout, item);
73
+ onDrop === null || onDrop === void 0 ? void 0 : onDrop(groupedLayout, item, e);
61
74
  };
62
75
  this.pluginsRefs = [];
63
76
  this.state = {
@@ -77,6 +90,29 @@ class GridLayout extends React.PureComponent {
77
90
  clearTimeout(this._timeout);
78
91
  document.removeEventListener('visibilitychange', this.onVisibilityChange);
79
92
  }
93
+ getActiveLayout() {
94
+ const { layout, temporaryLayout } = this.context;
95
+ return (temporaryLayout === null || temporaryLayout === void 0 ? void 0 : temporaryLayout.data) || layout;
96
+ }
97
+ mergeGroupsLayout(group, newLayout, temporaryItem) {
98
+ const renderLayout = this.getActiveLayout();
99
+ const itemsByGroup = renderLayout.reduce((memo, item) => {
100
+ memo[item.i] = item;
101
+ return memo;
102
+ }, temporaryItem ? { [temporaryItem.i]: temporaryItem } : {});
103
+ const newItemsLayoutById = newLayout.reduce((memo, item) => {
104
+ const parent = itemsByGroup[item.i].parent;
105
+ memo[item.i] = Object.assign(Object.assign({}, item), { parent });
106
+ return memo;
107
+ }, {});
108
+ return renderLayout.map((currentItem) => {
109
+ const itemParent = itemsByGroup[currentItem.i].parent || DEFAULT_GROUP;
110
+ if (itemParent === group) {
111
+ return newItemsLayoutById[currentItem.i];
112
+ }
113
+ return currentItem;
114
+ });
115
+ }
80
116
  reloadItems() {
81
117
  const { editMode, settings: { autoupdateInterval, silentLoading } = {}, reloadItems, } = this.context;
82
118
  const { isPageHidden } = this.state;
@@ -100,25 +136,105 @@ class GridLayout extends React.PureComponent {
100
136
  const { type } = temporaryLayout.dragProps;
101
137
  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 }));
102
138
  }
103
- render() {
104
- const { layout, temporaryLayout, config, registerManager, editMode, noOverlay, focusable, draggableHandleClassName, outerDnDEnable, } = this.context;
105
- this.pluginsRefs.length = config.items.length;
106
- return (React.createElement(Layout, Object.assign({}, registerManager.gridLayout, { layout: (temporaryLayout === null || temporaryLayout === void 0 ? void 0 : temporaryLayout.data) || layout, isDraggable: editMode, isResizable: editMode, onDragStart: this._onStart, onDragStop: this._onStop, onResizeStart: this._onStart, onResizeStop: this._onStop }, (draggableHandleClassName
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
+ renderGroup(group, renderLayout, renderItems, offset = 0, groupGridProperties) {
151
+ const { registerManager, editMode, noOverlay, focusable, draggableHandleClassName, outerDnDEnable, } = this.context;
152
+ const properties = groupGridProperties
153
+ ? groupGridProperties(Object.assign({}, registerManager.gridLayout))
154
+ : registerManager.gridLayout;
155
+ let { compactType } = properties;
156
+ if (compactType === COMPACT_TYPE_HORIZONTAL_NOWRAP) {
157
+ renderLayout = this.getElementLayout(renderLayout, properties);
158
+ compactType = 'horizontal';
159
+ }
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
107
161
  ? { draggableHandle: `.${draggableHandleClassName}` }
108
162
  : null), (outerDnDEnable
109
163
  ? {
110
164
  isDroppable: true,
111
- onDropDragOver: this._onDropDragOver,
112
- onDrop: this._onDrop,
165
+ onDropDragOver: (e) => this._onDropDragOver(e, properties, renderLayout),
166
+ onDrop: (...args) => this._onDrop(group, ...args),
113
167
  }
114
168
  : null), { draggableCancel: `.${OVERLAY_CONTROLS_CLASS_NAME}` }),
115
- config.items.map((item, i) => {
169
+ renderItems.map((item, i) => {
116
170
  return (React.createElement(GridItem, { forwardedPluginRef: (pluginRef) => {
117
- this.pluginsRefs[i] = pluginRef;
118
- }, key: item.id, id: item.id, item: item, layout: (temporaryLayout === null || temporaryLayout === void 0 ? void 0 : temporaryLayout.data) || layout, adjustWidgetLayout: this.adjustWidgetLayout, isDragging: this.state.isDragging, noOverlay: noOverlay, focusable: focusable, withCustomHandle: Boolean(draggableHandleClassName), overlayControls: this.props.overlayControls }));
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 }));
119
173
  }),
120
174
  this.renderTemporaryPlaceholder()));
121
175
  }
176
+ render() {
177
+ const { config, groups, editMode } = this.context;
178
+ this.pluginsRefs.length = config.items.length;
179
+ const defaultRenderLayout = [];
180
+ const defaultRenderItems = [];
181
+ const layoutMap = {};
182
+ const groupedLayout = this.getActiveLayout().reduce((memo, item) => {
183
+ if (item.parent) {
184
+ if (!memo[item.parent]) {
185
+ memo[item.parent] = [];
186
+ }
187
+ memo[item.parent].push(item);
188
+ layoutMap[item.i] = item.parent;
189
+ }
190
+ else {
191
+ defaultRenderLayout.push(item);
192
+ }
193
+ return memo;
194
+ }, []);
195
+ const itemsByGroup = config.items.reduce((memo, item) => {
196
+ const group = layoutMap[item.id];
197
+ if (group) {
198
+ if (!memo[group]) {
199
+ memo[group] = [];
200
+ }
201
+ memo[group].push(item);
202
+ }
203
+ else {
204
+ defaultRenderItems.push(item);
205
+ }
206
+ return memo;
207
+ }, {});
208
+ let offset = 0;
209
+ if (groups) {
210
+ return groups.map((group) => {
211
+ const id = group.id || DEFAULT_GROUP;
212
+ let layout, items;
213
+ if (id === DEFAULT_GROUP) {
214
+ layout = defaultRenderLayout;
215
+ items = defaultRenderItems;
216
+ }
217
+ else {
218
+ layout = groupedLayout[id] || [];
219
+ items = itemsByGroup[id] || [];
220
+ }
221
+ const element = this.renderGroup(id, layout, items, offset, group.gridProperties);
222
+ offset += items.length;
223
+ if (group.render) {
224
+ return group.render(id, element, {
225
+ config,
226
+ editMode,
227
+ items,
228
+ layout,
229
+ });
230
+ }
231
+ return element;
232
+ });
233
+ }
234
+ else {
235
+ return this.renderGroup(DEFAULT_GROUP, defaultRenderLayout, defaultRenderItems, offset);
236
+ }
237
+ }
122
238
  }
123
239
  GridLayout.contextType = DashKitContext;
124
240
  export default GridLayout;
@@ -2,14 +2,29 @@ import React from 'react';
2
2
  import ReactGridLayout, { WidthProvider } from 'react-grid-layout';
3
3
  import { OVERLAY_CLASS_NAME } from '../../constants';
4
4
  class DragOverLayout extends ReactGridLayout {
5
- constructor() {
6
- super(...arguments);
5
+ constructor(...args) {
6
+ super(...args);
7
7
  this.dragReset = () => {
8
8
  if (this.dragEnterCounter) {
9
9
  this.dragEnterCounter = 0;
10
10
  this.removeDroppingPlaceholder();
11
11
  }
12
12
  };
13
+ this.mouseLeaveHandler = (e) => {
14
+ var _a;
15
+ const rect = ((_a = this.props.innerRef) === null || _a === void 0 ? void 0 : _a.current.getBoundingClientRect()) || {};
16
+ if (rect.bottom <= e.clientY ||
17
+ rect.top >= e.clientY ||
18
+ rect.left >= e.clientX ||
19
+ rect.right <= e.clientX) {
20
+ this.dragReset();
21
+ }
22
+ };
23
+ this.parentOnDrag = this.onDrag;
24
+ this.onDrag = (i, x, y, sintE) => {
25
+ this.parentOnDrag(i, x, y, sintE);
26
+ this.mouseLeaveHandler(sintE.e);
27
+ };
13
28
  }
14
29
  componentDidMount() {
15
30
  var _a;
@@ -19,6 +34,12 @@ class DragOverLayout extends ReactGridLayout {
19
34
  componentWillUnmount() {
20
35
  window.removeEventListener('dragend', this.dragReset);
21
36
  }
37
+ containerHeight() {
38
+ if (this.props.autoSize && this.state.layout.length === 0) {
39
+ return 'unset';
40
+ }
41
+ return super.containerHeight();
42
+ }
22
43
  processGridItem(child, isDroppingItem) {
23
44
  var _a;
24
45
  const gridItem = (_a = super.processGridItem) === null || _a === void 0 ? void 0 : _a.call(this, child, isDroppingItem);
@@ -10,3 +10,5 @@ export declare const MenuItems: {
10
10
  };
11
11
  export declare const DEFAULT_WIDGET_HEIGHT = 3;
12
12
  export declare const DEFAULT_WIDGET_WIDTH = 3;
13
+ export declare const DEFAULT_GROUP = "__default";
14
+ export declare const COMPACT_TYPE_HORIZONTAL_NOWRAP = "horizontal-nowrap";
@@ -10,3 +10,5 @@ export const MenuItems = {
10
10
  };
11
11
  export const DEFAULT_WIDGET_HEIGHT = 3;
12
12
  export const DEFAULT_WIDGET_WIDTH = 3;
13
+ export const DEFAULT_GROUP = '__default';
14
+ export const COMPACT_TYPE_HORIZONTAL_NOWRAP = 'horizontal-nowrap';
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import isEqual from 'lodash/isEqual';
3
+ import pick from 'lodash/pick';
3
4
  import { DEFAULT_WIDGET_HEIGHT, DEFAULT_WIDGET_WIDTH, TEMPORARY_ITEM_ID } from '../constants/common';
4
5
  import { DashKitContext, DashKitDnDContext } from '../context/DashKitContext';
5
6
  import { getItemsParams, getItemsState } from '../shared';
@@ -18,10 +19,11 @@ function useMemoStateContext(props) {
18
19
  const outerDnDEnable = Boolean(dndContext);
19
20
  // TODO: need move originalLayouts, adjustedLayouts to state
20
21
  const [layoutUpdateCounter, forceUpdateLayout] = React.useState(0);
21
- const onChange = React.useCallback(({ config = props.config, itemsStateAndParams = props.itemsStateAndParams }) => {
22
+ const onChange = React.useCallback(({ config = props.config, itemsStateAndParams = props.itemsStateAndParams, groups = props.groups, }) => {
22
23
  if (!(isEqual(config, props.config) &&
23
- isEqual(itemsStateAndParams, props.itemsStateAndParams))) {
24
- props.onChange({ config, itemsStateAndParams });
24
+ isEqual(itemsStateAndParams, props.itemsStateAndParams) &&
25
+ isEqual(groups, props.groups))) {
26
+ props.onChange({ config, itemsStateAndParams, groups });
25
27
  }
26
28
  }, [props.config, props.itemsStateAndParams, props.onChange]);
27
29
  // каллбэк вызывающийся при изменение лэйаута сетки, первым аргументом приходит актуальный конфиг лэйаута,
@@ -155,7 +157,7 @@ function useMemoStateContext(props) {
155
157
  return null;
156
158
  }
157
159
  }, [dragProps, props.registerManager]);
158
- const onDropDragOver = React.useCallback(() => {
160
+ const onDropDragOver = React.useCallback((_e, gridProps = {}) => {
159
161
  if (temporaryLayout) {
160
162
  resetTemporaryLayout();
161
163
  return false;
@@ -163,10 +165,14 @@ function useMemoStateContext(props) {
163
165
  if (dragOverPlugin) {
164
166
  const { defaultLayout } = dragOverPlugin;
165
167
  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 || {};
166
- return { h, w };
168
+ return {
169
+ h: gridProps.maxH ? Math.min(h, gridProps.maxH) : h,
170
+ w: gridProps.maxW ? Math.min(w, gridProps.maxW) : w,
171
+ };
167
172
  }
168
173
  return false;
169
174
  }, [resetTemporaryLayout, temporaryLayout, dragOverPlugin, dragProps]);
175
+ const onDropProp = props.onDrop;
170
176
  const onDrop = React.useCallback((newLayout, item) => {
171
177
  if (!dragProps) {
172
178
  return;
@@ -175,23 +181,23 @@ function useMemoStateContext(props) {
175
181
  data: newLayout,
176
182
  dragProps,
177
183
  });
178
- const { i, w, h, x, y } = item;
179
- props.onDrop({
184
+ onDropProp({
180
185
  newLayout: newLayout.reduce((memo, l) => {
181
- if (l.i !== i) {
182
- memo.push({ i: l.i, w: l.w, h: l.h, x: l.x, y: l.y });
186
+ if (l.i !== item.i) {
187
+ memo.push(pick(l, ['i', 'h', 'w', 'x', 'y', 'parent']));
183
188
  }
184
189
  return memo;
185
190
  }, []),
186
- itemLayout: { w, h, x, y },
191
+ itemLayout: pick(item, ['i', 'h', 'w', 'x', 'y', 'parent']),
187
192
  commit: resetTemporaryLayout,
188
193
  dragProps,
189
194
  });
190
- }, [dragProps, props.onDrop, setTemporaryLayout, resetTemporaryLayout]);
195
+ }, [dragProps, onDropProp, setTemporaryLayout, resetTemporaryLayout]);
191
196
  return React.useMemo(() => ({
192
197
  layout: resultLayout,
193
198
  temporaryLayout,
194
199
  config: props.config,
200
+ groups: props.groups,
195
201
  context: props.context,
196
202
  noOverlay: props.noOverlay,
197
203
  focusable: props.focusable,
@@ -220,6 +226,7 @@ function useMemoStateContext(props) {
220
226
  resultLayout,
221
227
  temporaryLayout,
222
228
  props.config,
229
+ props.groups,
223
230
  props.context,
224
231
  props.noOverlay,
225
232
  props.focusable,
@@ -6,6 +6,7 @@ export declare const useCalcPropsLayout: (config: Config, registerManager: Regis
6
6
  w: number;
7
7
  x: number;
8
8
  y: number;
9
+ parent?: string | undefined;
9
10
  minW: number;
10
11
  minH: number;
11
12
  maxW?: number | undefined;
@@ -1,5 +1,8 @@
1
1
  import { StringParams } from './common';
2
- export interface ConfigLayout {
2
+ export interface AdditionalWidgetLayout {
3
+ parent?: string;
4
+ }
5
+ export interface ConfigLayout extends AdditionalWidgetLayout {
3
6
  i: string;
4
7
  h: number;
5
8
  w: number;
@@ -1,6 +1,7 @@
1
1
  import type ReactGridLayout from 'react-grid-layout';
2
2
  import type { OverlayCustomControlItem } from '../components/OverlayControls/OverlayControls';
3
3
  import { MenuItems } from '../constants';
4
+ import { AdditionalWidgetLayout } from '../shared';
4
5
  export interface Settings {
5
6
  gridLayout?: ReactGridLayout.ReactGridLayoutProps;
6
7
  theme?: string;
@@ -15,7 +16,7 @@ export interface SettingsProps {
15
16
  export interface ContextProps {
16
17
  [key: string]: any;
17
18
  }
18
- export interface WidgetLayout {
19
+ export interface WidgetLayout extends AdditionalWidgetLayout {
19
20
  i: string;
20
21
  w: number;
21
22
  h: number;
@@ -1,4 +1,6 @@
1
- import type { ConfigItem, ConfigLayout } from '../shared';
1
+ /// <reference types="react-grid-layout" />
2
+ /// <reference types="react" />
3
+ import type { Config, ConfigItem, ConfigLayout } from '../shared';
2
4
  export interface AddConfigItem extends Omit<ConfigItem, 'id' | 'namespace'> {
3
5
  id?: null;
4
6
  namespace?: string;
@@ -11,3 +13,17 @@ export type SetItemOptions = {
11
13
  export type SetNewItemOptions = SetItemOptions & {
12
14
  updateLayout?: ConfigLayout[];
13
15
  };
16
+ export interface DashkitGroupRenderProps {
17
+ config: Config;
18
+ editMode: boolean;
19
+ items: ConfigItem[];
20
+ layout: ConfigLayout[];
21
+ }
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
+ };
25
+ export interface DashKitGroup {
26
+ id?: string;
27
+ render?: (id: string, children: React.ReactNode, props: DashkitGroupRenderProps) => React.ReactNode;
28
+ gridProperties?: (props: ReactGridLayoutProps) => ReactGridLayoutProps;
29
+ }
@@ -23,16 +23,7 @@ export interface PluginWidgetProps<T = StringParams> {
23
23
  adjustedWidgetLayout?: WidgetLayout;
24
24
  }) => void;
25
25
  }
26
- export interface PluginDefaultLayout {
27
- w?: number;
28
- h?: number;
29
- x?: number;
30
- y?: number;
31
- minW?: number;
32
- maxW?: number;
33
- minH?: number;
34
- maxH?: number;
35
- }
26
+ export type PluginDefaultLayout = Partial<Omit<WidgetLayout, 'i'>>;
36
27
  export interface Plugin<P extends PluginWidgetProps<T> = any, T = StringParams> extends PluginBase {
37
28
  defaultLayout?: PluginDefaultLayout;
38
29
  renderer: (props: P, forwardedRef: React.RefObject<any>) => React.ReactNode;
@@ -229,7 +229,7 @@ export class UpdateManager {
229
229
  const newIdData = getNewId({ config, salt, counter, excludeIds: newItemData.excludeIds });
230
230
  counter = newIdData.counter;
231
231
  const newItem = Object.assign(Object.assign({}, item), { id: newIdData.id, data: newItemData.data, namespace });
232
- const saveDefaultLayout = pick(layout, ['h', 'w', 'x', 'y']);
232
+ const saveDefaultLayout = pick(layout, ['h', 'w', 'x', 'y', 'parent']);
233
233
  if (options.updateLayout) {
234
234
  const byId = options.updateLayout.reduce((memo, t) => {
235
235
  memo[t.i] = t;
@@ -310,7 +310,7 @@ export class UpdateManager {
310
310
  static updateLayout({ layout, config }) {
311
311
  return update(config, {
312
312
  layout: {
313
- $set: layout.map(({ x, y, w, h, i }) => ({ x, y, w, h, i })),
313
+ $set: layout.map((item) => pick(item, ['i', 'h', 'w', 'x', 'y', 'parent'])),
314
314
  },
315
315
  });
316
316
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/dashkit",
3
- "version": "8.3.0",
3
+ "version": "8.5.0-beta.0",
4
4
  "description": "Library for rendering dashboard grid layout",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -60,12 +60,12 @@
60
60
  "prepublishOnly": "npm run lint && npm run test && npm run build"
61
61
  },
62
62
  "dependencies": {
63
- "@gravity-ui/icons": "^2.8.1",
64
63
  "@bem-react/classname": "^1.6.0",
64
+ "@gravity-ui/icons": "^2.8.1",
65
65
  "hashids": "^2.2.8",
66
66
  "immutability-helper": "^3.1.1",
67
67
  "prop-types": "^15.8.1",
68
- "react-grid-layout": "^1.3.4",
68
+ "react-grid-layout": "^1.4.4",
69
69
  "react-transition-group": "^4.4.5"
70
70
  },
71
71
  "peerDependencies": {
@@ -90,7 +90,7 @@
90
90
  "@types/jest": "^26.0.20",
91
91
  "@types/lodash": "^4.14.170",
92
92
  "@types/react": "^18.0.27",
93
- "@types/react-grid-layout": "^1.3.2",
93
+ "@types/react-grid-layout": "^1.3.5",
94
94
  "babel-jest": "^26.6.3",
95
95
  "copyfiles": "^2.4.1",
96
96
  "enzyme": "^3.11.0",