@gravity-ui/dashkit 8.17.3 → 8.18.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 (30) hide show
  1. package/README.md +9 -0
  2. package/build/cjs/components/DashKitDnDWrapper/DashKitDnDWrapper.d.ts +2 -1
  3. package/build/cjs/components/DashKitDnDWrapper/DashKitDnDWrapper.js +8 -7
  4. package/build/cjs/components/GridItem/GridItem.css +16 -1
  5. package/build/cjs/components/GridItem/GridItem.js +3 -1
  6. package/build/cjs/components/GridLayout/GridLayout.d.ts +7 -1
  7. package/build/cjs/components/GridLayout/GridLayout.js +86 -19
  8. package/build/cjs/components/GridLayout/ReactGridLayout.js +19 -17
  9. package/build/cjs/components/OverlayControls/OverlayControls.js +4 -5
  10. package/build/cjs/constants/common.d.ts +1 -0
  11. package/build/cjs/constants/common.js +2 -1
  12. package/build/cjs/hocs/withContext.js +23 -12
  13. package/build/cjs/shared/types/common.d.ts +4 -0
  14. package/build/cjs/typings/common.d.ts +4 -1
  15. package/build/cjs/typings/config.d.ts +2 -1
  16. package/build/esm/components/DashKitDnDWrapper/DashKitDnDWrapper.d.ts +2 -1
  17. package/build/esm/components/DashKitDnDWrapper/DashKitDnDWrapper.js +8 -7
  18. package/build/esm/components/GridItem/GridItem.css +16 -1
  19. package/build/esm/components/GridItem/GridItem.js +3 -1
  20. package/build/esm/components/GridLayout/GridLayout.d.ts +7 -1
  21. package/build/esm/components/GridLayout/GridLayout.js +86 -19
  22. package/build/esm/components/GridLayout/ReactGridLayout.js +20 -18
  23. package/build/esm/components/OverlayControls/OverlayControls.js +4 -5
  24. package/build/esm/constants/common.d.ts +1 -0
  25. package/build/esm/constants/common.js +1 -0
  26. package/build/esm/hocs/withContext.js +23 -12
  27. package/build/esm/shared/types/common.d.ts +4 -0
  28. package/build/esm/typings/common.d.ts +4 -1
  29. package/build/esm/typings/config.d.ts +2 -1
  30. package/package.json +1 -1
package/README.md CHANGED
@@ -384,10 +384,19 @@ DashKit.setSettings({menu: [] as Array<MenuItem>});
384
384
  #### DashKitDnDWrapper
385
385
 
386
386
  ```ts
387
+ type DraggedOverItem = {
388
+ h: number;
389
+ w: number;
390
+ type: string;
391
+ parent: string;
392
+ i?: number;
393
+ };
394
+
387
395
  interface DashKitDnDWrapperProps {
388
396
  dragImageSrc?: string;
389
397
  onDragStart?: (dragProps: ItemDragProps) => void;
390
398
  onDragEnd?: () => void;
399
+ onDropDragOver?: (draggedItem: DraggedOverItem, sharedItem: DraggedOverItem | null) => void | boolean;
391
400
  }
392
401
  ```
393
402
 
@@ -1,7 +1,8 @@
1
1
  import React from 'react';
2
- import type { ItemDragProps } from '../../shared';
2
+ import type { DraggedOverItem, ItemDragProps } from '../../shared';
3
3
  type DashKitDnDWrapperProps = {
4
4
  dragImageSrc?: string;
5
+ onDropDragOver?: (draggedItem: DraggedOverItem, sharedItem: DraggedOverItem | null) => void | boolean;
5
6
  onDragStart?: (dragProps: ItemDragProps) => void;
6
7
  onDragEnd?: () => void;
7
8
  children: React.ReactElement;
@@ -12,24 +12,25 @@ const DashKitDnDWrapper = (props) => {
12
12
  img.src = props.dragImageSrc || defaultImageSrc;
13
13
  return img;
14
14
  }, [props.dragImageSrc]);
15
+ const onDragStartProp = props.onDragStart;
15
16
  const onDragStart = react_1.default.useCallback((_, itemDragProps) => {
16
- var _a;
17
17
  setDragProps(itemDragProps);
18
- (_a = props.onDragStart) === null || _a === void 0 ? void 0 : _a.call(props, itemDragProps);
19
- }, [setDragProps, props.onDragStart]);
18
+ onDragStartProp === null || onDragStartProp === void 0 ? void 0 : onDragStartProp(itemDragProps);
19
+ }, [setDragProps, onDragStartProp]);
20
+ const onDragEndProp = props.onDragEnd;
20
21
  const onDragEnd = react_1.default.useCallback((_) => {
21
- var _a;
22
22
  setDragProps(null);
23
- (_a = props.onDragEnd) === null || _a === void 0 ? void 0 : _a.call(props);
24
- }, [setDragProps, props.onDragEnd]);
23
+ onDragEndProp === null || onDragEndProp === void 0 ? void 0 : onDragEndProp();
24
+ }, [setDragProps, onDragEndProp]);
25
25
  const contextValue = react_1.default.useMemo(() => {
26
26
  return {
27
27
  dragProps,
28
28
  dragImagePreview,
29
29
  onDragStart,
30
30
  onDragEnd,
31
+ onDropDragOver: props.onDropDragOver,
31
32
  };
32
- }, [dragProps, dragImagePreview, onDragStart, onDragEnd]);
33
+ }, [dragProps, dragImagePreview, onDragStart, onDragEnd, props.onDropDragOver]);
33
34
  return (react_1.default.createElement(DashKitContext_1.DashKitDnDContext.Provider, { value: contextValue }, props.children));
34
35
  };
35
36
  exports.DashKitDnDWrapper = DashKitDnDWrapper;
@@ -34,6 +34,14 @@
34
34
  border-color: transparent;
35
35
  pointer-events: none;
36
36
  }
37
+ .dashkit-grid-item_is-dragged-out {
38
+ pointer-events: none;
39
+ }
40
+
41
+ .react-grid-item.dropping {
42
+ user-select: none;
43
+ pointer-events: none;
44
+ }
37
45
 
38
46
  .react-grid-layout {
39
47
  position: relative;
@@ -57,7 +65,6 @@
57
65
  transition: none;
58
66
  z-index: 3;
59
67
  will-change: transform;
60
- pointer-events: none;
61
68
  }
62
69
 
63
70
  .react-grid-item.dashkit-grid-item_is-focused {
@@ -74,6 +81,14 @@
74
81
  user-select: none;
75
82
  }
76
83
 
84
+ .react-grid-focus-capture {
85
+ position: absolute;
86
+ display: block;
87
+ z-index: 6;
88
+ max-width: 100%;
89
+ max-height: 100%;
90
+ }
91
+
77
92
  .react-grid-item .react-resizable-handle {
78
93
  position: absolute;
79
94
  width: 20px;
@@ -96,7 +96,7 @@ class GridItem extends react_1.default.PureComponent {
96
96
  render() {
97
97
  // из-за бага, что Grid Items unmounts при изменении static, isDraggable, isResaizable
98
98
  // https://github.com/STRML/react-grid-layout/issues/721
99
- const { style, onMouseDown, onMouseUp, onTouchEnd, onTouchStart, children, className, isDragging, noOverlay, focusable, withCustomHandle, isPlaceholder = false, } = this.props;
99
+ const { style, onMouseDown, onMouseUp, onTouchEnd, onTouchStart, children, className, isDragging, isDraggedOut, noOverlay, focusable, withCustomHandle, isPlaceholder = false, } = this.props;
100
100
  const { editMode } = this.context;
101
101
  const width = Number.parseInt(style.width, 10);
102
102
  const height = Number.parseInt(style.height, 10);
@@ -115,6 +115,7 @@ class GridItem extends react_1.default.PureComponent {
115
115
  const { _editActive } = this.props.item.data;
116
116
  return (react_1.default.createElement("div", Object.assign({ className: b({
117
117
  'is-dragging': isDragging,
118
+ 'is-dragged-out': isDraggedOut,
118
119
  'is-focused': this.state.isFocused,
119
120
  'with-custom-handle': withCustomHandle,
120
121
  }, preparedClassName), "data-qa": "dashkit-grid-item", style: style, ref: this.props.forwardedRef }, reactGridLayoutProps, (focusable
@@ -135,6 +136,7 @@ GridItem.propTypes = {
135
136
  id: prop_types_1.default.string,
136
137
  item: prop_types_1.default.object,
137
138
  isDragging: prop_types_1.default.bool,
139
+ isDraggedOut: prop_types_1.default.bool,
138
140
  layout: prop_types_1.default.array,
139
141
  forwardedRef: prop_types_1.default.any,
140
142
  forwardedPluginRef: prop_types_1.default.any,
@@ -4,6 +4,7 @@ export default class GridLayout extends React.PureComponent<any, any, any> {
4
4
  pluginsRefs: any[];
5
5
  state: {
6
6
  isDragging: boolean;
7
+ isDraggedOut: boolean;
7
8
  isPageHidden: boolean;
8
9
  currentDraggingElement: null;
9
10
  draggedOverGroup: null;
@@ -47,7 +48,12 @@ export default class GridLayout extends React.PureComponent<any, any, any> {
47
48
  e: any;
48
49
  element: any;
49
50
  };
50
- updateeDraggingElementState(group: any, layoutItem: any, e: any): void;
51
+ updateDraggingElementState(group: any, layoutItem: any, e: any): void;
52
+ _initDragCoordinatesWatcher(element: any): void;
53
+ _parendDragNode: any;
54
+ _forceCursorCapture(parentElement: any, position: any, parentRect: any): void;
55
+ _updateDragCoordinates(e: any): void;
56
+ _resetDragWatcher(): void;
51
57
  _onDragStart(group: any, _newLayout: any, layoutItem: any, _newItem: any, _placeholder: any, e: any, element: any): void;
52
58
  _onDrag(group: any, layout: any, oldItem: any, newItem: any, placeholder: any, e: any, element: any): void;
53
59
  _onDragStop(group: any, layout: any, oldItem: any, newItem: any, placeholder: any, e: any, element: any): void;
@@ -142,16 +142,24 @@ class GridLayout extends react_1.default.PureComponent {
142
142
  if (currentDraggingElement) {
143
143
  const { h, w, i } = currentDraggingElement.layoutItem;
144
144
  const { type } = currentDraggingElement.item;
145
- return onDropDragOver(e, properties, layout, { h, w, i, type });
145
+ const sharedItem = {
146
+ h,
147
+ w,
148
+ i,
149
+ type,
150
+ parent: currentDraggingElement.group,
151
+ };
152
+ return onDropDragOver(e, group, properties, layout, sharedItem);
146
153
  }
147
154
  if (dragOverPlugin) {
148
- return onDropDragOver(e, properties, layout);
155
+ return onDropDragOver(e, group, properties, layout);
149
156
  }
150
157
  return false;
151
158
  };
152
159
  this.pluginsRefs = [];
153
160
  this.state = {
154
161
  isDragging: false,
162
+ isDraggedOut: false,
155
163
  isPageHidden: false,
156
164
  currentDraggingElement: null,
157
165
  draggedOverGroup: null,
@@ -269,7 +277,7 @@ class GridLayout extends react_1.default.PureComponent {
269
277
  element,
270
278
  };
271
279
  }
272
- updateeDraggingElementState(group, layoutItem, e) {
280
+ updateDraggingElementState(group, layoutItem, e) {
273
281
  let currentDraggingElement = this.state.currentDraggingElement;
274
282
  if (!currentDraggingElement) {
275
283
  const { temporaryLayout } = this.context;
@@ -287,6 +295,59 @@ class GridLayout extends react_1.default.PureComponent {
287
295
  }
288
296
  this.setState({ currentDraggingElement, draggedOverGroup: group });
289
297
  }
298
+ _initDragCoordinatesWatcher(element) {
299
+ if (!this._parendDragNode) {
300
+ this._parendDragNode = element.parentElement;
301
+ this.setState({ isDraggedOut: false });
302
+ }
303
+ }
304
+ // When element is going back and prointer-event: none is removed mouse enter event is not fired
305
+ // So to trigger it we are forcing this event by adding transparent block under the mouse
306
+ _forceCursorCapture(parentElement, position, parentRect) {
307
+ const block = document.createElement('div');
308
+ block.classList.add('react-grid-focus-capture');
309
+ const blockSize = 44;
310
+ const offset = blockSize / 2;
311
+ // Keeping elemnt inside current grid
312
+ const top = Math.min(Math.max(position.top - offset, 0), parentRect.height - blockSize);
313
+ const left = Math.min(Math.max(position.left - offset, 0), parentRect.width - blockSize);
314
+ block.style.width = `${blockSize}px`;
315
+ block.style.height = `${blockSize}px`;
316
+ block.style.top = `${top}px`;
317
+ block.style.left = `${left}px`;
318
+ parentElement.appendChild(block);
319
+ setTimeout(() => {
320
+ block.remove();
321
+ }, 100);
322
+ }
323
+ _updateDragCoordinates(e) {
324
+ const parent = this._parendDragNode;
325
+ const parentRect = parent.getBoundingClientRect();
326
+ const { clientX, clientY } = e;
327
+ let isDraggedOut = this.state.isDraggedOut;
328
+ if (clientX < parentRect.left ||
329
+ clientX > parentRect.right ||
330
+ clientY < parentRect.top ||
331
+ clientY > parentRect.bottom) {
332
+ isDraggedOut = true;
333
+ }
334
+ else {
335
+ isDraggedOut = false;
336
+ }
337
+ if (isDraggedOut !== this.state.isDraggedOut) {
338
+ this.setState({ isDraggedOut });
339
+ if (!isDraggedOut) {
340
+ this._forceCursorCapture(parent, {
341
+ top: clientY - parentRect.top,
342
+ left: clientX - parentRect.left,
343
+ }, parentRect);
344
+ }
345
+ }
346
+ }
347
+ _resetDragWatcher() {
348
+ this._parendDragNode = null;
349
+ this.setState({ isDraggedOut: false });
350
+ }
290
351
  _onDragStart(group, _newLayout, layoutItem, _newItem, _placeholder, e, element) {
291
352
  var _a;
292
353
  (_a = this.context.onDragStart) === null || _a === void 0 ? void 0 : _a.call(this, this.prepareDefaultArguments(group, _newLayout, layoutItem, _newItem, _placeholder, e, element));
@@ -294,16 +355,21 @@ class GridLayout extends react_1.default.PureComponent {
294
355
  this.setState({ isDragging: true });
295
356
  }
296
357
  else {
297
- this.updateeDraggingElementState(group, layoutItem, e);
358
+ this._initDragCoordinatesWatcher(element);
359
+ this.updateDraggingElementState(group, layoutItem, e);
298
360
  this.setState({ isDragging: true });
299
361
  }
300
362
  }
301
363
  _onDrag(group, layout, oldItem, newItem, placeholder, e, element) {
302
364
  var _a;
365
+ if (!this.context.dragOverPlugin) {
366
+ this._updateDragCoordinates(e);
367
+ }
303
368
  (_a = this.context.onDrag) === null || _a === void 0 ? void 0 : _a.call(this, this.prepareDefaultArguments(group, layout, oldItem, newItem, placeholder, e, element));
304
369
  }
305
370
  _onDragStop(group, layout, oldItem, newItem, placeholder, e, element) {
306
371
  var _a;
372
+ this._resetDragWatcher();
307
373
  this._onStop(group, layout);
308
374
  (_a = this.context.onDragStop) === null || _a === void 0 ? void 0 : _a.call(this, this.prepareDefaultArguments(group, layout, oldItem, newItem, placeholder, e, element));
309
375
  }
@@ -346,7 +412,8 @@ class GridLayout extends react_1.default.PureComponent {
346
412
  renderGroup(group, renderLayout, renderItems, offset = 0, groupGridProperties) {
347
413
  const { registerManager, editMode, noOverlay, focusable, draggableHandleClassName, outerDnDEnable, onItemMountChange, onItemRender, } = this.context;
348
414
  const { currentDraggingElement, draggedOverGroup } = this.state;
349
- const properties = groupGridProperties
415
+ const hasOwnGroupProperties = typeof groupGridProperties === 'function';
416
+ const properties = hasOwnGroupProperties
350
417
  ? groupGridProperties(Object.assign({}, registerManager.gridLayout))
351
418
  : registerManager.gridLayout;
352
419
  let { compactType } = properties;
@@ -359,23 +426,22 @@ class GridLayout extends react_1.default.PureComponent {
359
426
  group === currentDraggingElement.group &&
360
427
  draggedOverGroup !== null &&
361
428
  draggedOverGroup !== group;
362
- return (react_1.default.createElement(ReactGridLayout_1.Layout
363
- // Group properties
364
- , Object.assign({}, properties, {
429
+ return (react_1.default.createElement(ReactGridLayout_1.Layout, Object.assign({ isDraggable: editMode, isResizable: editMode }, properties, { key: `group_${group}`,
365
430
  // Layout props
366
- compactType: compactType, layout: layout, key: `group_${group}`, isDraggable: editMode, isResizable: editMode, draggableCancel: `.${constants_1.DRAGGABLE_CANCEL_CLASS_NAME}` }, (draggableHandleClassName
367
- ? { draggableHandle: `.${draggableHandleClassName}` }
368
- : null), {
431
+ compactType: compactType, layout: layout, draggableCancel: `.${constants_1.DRAGGABLE_CANCEL_CLASS_NAME}`, draggableHandle: draggableHandleClassName ? `.${draggableHandleClassName}` : null,
369
432
  // Default callbacks
370
433
  onDragStart: callbacks.onDragStart, onDrag: callbacks.onDrag, onDragStop: callbacks.onDragStop, onResizeStart: callbacks.onResizeStart, onResize: callbacks.onResize, onResizeStop: callbacks.onResizeStop,
371
434
  // External Drag N Drop options
372
- onDragTargetRestore: callbacks.onDragTargetRestore, onDropDragOver: callbacks.onDropDragOver, onDrop: callbacks.onDrop, hasSharedDragItem: hasSharedDragItem, sharedDragPosition: currentDraggingElement === null || currentDraggingElement === void 0 ? void 0 : currentDraggingElement.cursorPosition, isDragCaptured: isDragCaptured }, (outerDnDEnable
373
- ? {
374
- isDroppable: true,
375
- }
376
- : null)),
435
+ onDragTargetRestore: callbacks.onDragTargetRestore, onDropDragOver: callbacks.onDropDragOver, onDrop: callbacks.onDrop, hasSharedDragItem: hasSharedDragItem, sharedDragPosition: currentDraggingElement === null || currentDraggingElement === void 0 ? void 0 : currentDraggingElement.cursorPosition, isDragCaptured: isDragCaptured, isDroppable: Boolean(outerDnDEnable) && editMode }),
377
436
  renderItems.map((item, i) => {
378
- 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), onItemMountChange: onItemMountChange, onItemRender: onItemRender }));
437
+ const keyId = item.id;
438
+ const isDragging = this.state.isDragging;
439
+ const isCurrentDraggedItem = (currentDraggingElement === null || currentDraggingElement === void 0 ? void 0 : currentDraggingElement.item.id) === keyId;
440
+ const isDraggedOut = isCurrentDraggedItem && this.state.isDraggedOut;
441
+ const itemNoOverlay = hasOwnGroupProperties && 'noOverlay' in properties
442
+ ? properties.noOverlay
443
+ : noOverlay;
444
+ return (react_1.default.createElement(GridItem_1.default, { forwardedPluginRef: this.getMemoForwardRefCallback(offset + i), key: keyId, id: keyId, item: item, layout: layout, adjustWidgetLayout: this.adjustWidgetLayout, isDragging: isDragging, isDraggedOut: isDraggedOut, noOverlay: itemNoOverlay, focusable: focusable, withCustomHandle: Boolean(draggableHandleClassName), onItemMountChange: onItemMountChange, onItemRender: onItemRender }));
379
445
  }),
380
446
  this.renderTemporaryPlaceholder()));
381
447
  }
@@ -427,13 +493,14 @@ class GridLayout extends react_1.default.PureComponent {
427
493
  const element = this.renderGroup(id, layout, items, offset, group.gridProperties);
428
494
  offset += items.length;
429
495
  if (group.render) {
430
- return group.render(id, element, {
496
+ const groupContext = {
431
497
  config,
432
498
  editMode,
433
499
  items,
434
500
  layout,
435
501
  context,
436
- });
502
+ };
503
+ return group.render(id, element, groupContext);
437
504
  }
438
505
  return element;
439
506
  });
@@ -157,6 +157,20 @@ class DragOverLayout extends react_grid_layout_1.default {
157
157
  var _a;
158
158
  return ((_a = this.props.innerRef) === null || _a === void 0 ? void 0 : _a.current) || null;
159
159
  }
160
+ calculateDroppingPosition(itemProps) {
161
+ const { containerWidth, cols, w, h, rowHeight, margin, transformScale, droppingPosition } = itemProps;
162
+ const { sharedDragPosition } = this.props;
163
+ let offsetX, offsetY;
164
+ if (sharedDragPosition) {
165
+ offsetX = sharedDragPosition.offsetX;
166
+ offsetY = sharedDragPosition.offsetY;
167
+ }
168
+ else {
169
+ offsetX = (((containerWidth / cols) * w) / 2 || 0) * transformScale;
170
+ offsetY = ((h * rowHeight + (h - 1) * margin[1]) / 2 || 0) * transformScale;
171
+ }
172
+ return Object.assign(Object.assign({}, droppingPosition), { left: droppingPosition.left - offsetX, top: droppingPosition.top - offsetY });
173
+ }
160
174
  // Drop item from outside gets 0,0 droppingPosition
161
175
  // centering cursor on newly creted grid item
162
176
  // And cause grid-layout using it's own GridItem to make it look
@@ -167,26 +181,14 @@ class DragOverLayout extends react_grid_layout_1.default {
167
181
  if (!gridItem) {
168
182
  return gridItem;
169
183
  }
170
- const { props: itemProps } = gridItem;
171
184
  if (isDroppingItem) {
172
- const { containerWidth, cols, w, h, rowHeight, margin, transformScale, droppingPosition, } = itemProps;
173
- const { sharedDragPosition, hasSharedDragItem } = this.props;
174
- let offsetX, offsetY;
175
- if (sharedDragPosition) {
176
- offsetX = sharedDragPosition.offsetX;
177
- offsetY = sharedDragPosition.offsetY;
178
- }
179
- else {
180
- offsetX = (((containerWidth / cols) * w) / 2 || 0) * transformScale;
181
- offsetY = ((h * rowHeight + (h - 1) * margin[1]) / 2 || 0) * transformScale;
182
- }
183
- const style = gridItem.props.style || null;
184
185
  // React.cloneElement is just cleaner then copy-paste whole processGridItem method
185
186
  return react_1.default.cloneElement(gridItem, {
186
- // hiding previre if dragging shared item
187
- style: hasSharedDragItem ? Object.assign(Object.assign({}, style), { opacity: 0 }) : style,
188
- className: constants_1.OVERLAY_CLASS_NAME,
189
- droppingPosition: Object.assign(Object.assign({}, droppingPosition), { left: droppingPosition.left - offsetX, top: droppingPosition.top - offsetY }),
187
+ // hiding preview if dragging shared item
188
+ style: this.props.hasSharedDragItem
189
+ ? Object.assign(Object.assign({}, gridItem.props.style), { opacity: 0 }) : gridItem.props.style,
190
+ className: `${constants_1.OVERLAY_CLASS_NAME} ${constants_1.DROPPING_ELEMENT_CLASS_NAME}`,
191
+ droppingPosition: this.calculateDroppingPosition(gridItem.props),
190
192
  });
191
193
  }
192
194
  return gridItem;
@@ -32,7 +32,7 @@ class OverlayControls extends react_1.default.Component {
32
32
  const { view, size, onItemClick } = this.props;
33
33
  const { title, handler, icon, iconSize, qa } = item;
34
34
  const onItemClickHandler = typeof handler === 'function' ? handler : noop_1.default;
35
- return (react_1.default.createElement(uikit_1.Button, { key: index, view: view, size: size, title: title, pin: this.getControlItemPinStyle(index, length), onClick: () => {
35
+ return (react_1.default.createElement(uikit_1.Button, { key: `control_${index}`, view: view, size: size, title: title, pin: this.getControlItemPinStyle(index, length), onClick: () => {
36
36
  onItemClickHandler(this.props.configItem);
37
37
  onItemClick === null || onItemClick === void 0 ? void 0 : onItemClick();
38
38
  }, qa: qa },
@@ -43,7 +43,7 @@ class OverlayControls extends react_1.default.Component {
43
43
  if (this.context.overlayControls === null) {
44
44
  return null;
45
45
  }
46
- return (react_1.default.createElement(uikit_1.Button, { view: view, size: size, title: (0, i18n_1.i18n)('label_settings'), pin: "round-brick", onClick: this.onEditItem, qa: "dashkit-overlay-control-settings" },
46
+ return (react_1.default.createElement(uikit_1.Button, { key: 'control_default', view: view, size: size, title: (0, i18n_1.i18n)('label_settings'), pin: "round-brick", onClick: this.onEditItem, qa: "dashkit-overlay-control-settings" },
47
47
  react_1.default.createElement(uikit_1.Icon, { data: icons_1.Gear, size: constants_1.OVERLAY_ICON_SIZE })));
48
48
  };
49
49
  this.getCustomLeftOverlayControls = () => {
@@ -161,10 +161,9 @@ class OverlayControls extends react_1.default.Component {
161
161
  renderControls() {
162
162
  const customLeftControls = this.getCustomLeftOverlayControls();
163
163
  const hasCustomOverlayLeftControls = Boolean(customLeftControls.length);
164
- const defaultControl = this.getDefaultControls();
165
164
  const controls = hasCustomOverlayLeftControls
166
165
  ? customLeftControls.map((item, index, items) => this.renderControlsItem(item, index, items.length + 1))
167
- : defaultControl;
166
+ : this.getDefaultControls();
168
167
  const menu = this.renderMenu(controls === null);
169
168
  return (react_1.default.createElement("div", { className: b('default-controls', {
170
169
  'with-custom-left': hasCustomOverlayLeftControls,
@@ -231,7 +230,7 @@ class OverlayControls extends react_1.default.Component {
231
230
  if (items.length === 0) {
232
231
  return null;
233
232
  }
234
- return (react_1.default.createElement(uikit_1.DropdownMenu, { items: items, renderSwitcher: (props) => (react_1.default.createElement(uikit_1.Button, Object.assign({}, props, { view: view, size: size, pin: isOnlyOneItem ? 'round-round' : 'brick-round', qa: "dashkit-overlay-control-menu" }),
233
+ return (react_1.default.createElement(uikit_1.DropdownMenu, { key: 'controls_dropdown', items: items, renderSwitcher: (props) => (react_1.default.createElement(uikit_1.Button, Object.assign({}, props, { view: view, size: size, pin: isOnlyOneItem ? 'round-round' : 'brick-round', qa: "dashkit-overlay-control-menu" }),
235
234
  react_1.default.createElement(uikit_1.Icon, { data: icons_1.Ellipsis, size: constants_1.OVERLAY_ICON_SIZE }))), popupProps: {
236
235
  contentClassName: constants_1.DRAGGABLE_CANCEL_CLASS_NAME,
237
236
  } }));
@@ -15,3 +15,4 @@ export declare const DEFAULT_GROUP = "__default";
15
15
  export declare const COMPACT_TYPE_HORIZONTAL_NOWRAP = "horizontal-nowrap";
16
16
  export declare const DRAGGABLE_CANCEL_CLASS_NAME = "dashkit_draggable_cancel";
17
17
  export declare const FOCUSED_CLASS_NAME = "dashkit_focused";
18
+ export declare const DROPPING_ELEMENT_CLASS_NAME = "dropping";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FOCUSED_CLASS_NAME = exports.DRAGGABLE_CANCEL_CLASS_NAME = exports.COMPACT_TYPE_HORIZONTAL_NOWRAP = exports.DEFAULT_GROUP = exports.DEFAULT_WIDGET_WIDTH = exports.DEFAULT_WIDGET_HEIGHT = exports.MenuItems = exports.TEMPORARY_ITEM_ID = exports.OVERLAY_ICON_SIZE = exports.OVERLAY_CLASS_NAME = exports.OVERLAY_CONTROLS_CLASS_NAME = exports.DEFAULT_NAMESPACE = exports.COPIED_WIDGET_STORE_KEY = void 0;
3
+ exports.DROPPING_ELEMENT_CLASS_NAME = exports.FOCUSED_CLASS_NAME = exports.DRAGGABLE_CANCEL_CLASS_NAME = exports.COMPACT_TYPE_HORIZONTAL_NOWRAP = exports.DEFAULT_GROUP = exports.DEFAULT_WIDGET_WIDTH = exports.DEFAULT_WIDGET_HEIGHT = exports.MenuItems = exports.TEMPORARY_ITEM_ID = exports.OVERLAY_ICON_SIZE = 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';
@@ -18,3 +18,4 @@ exports.DEFAULT_GROUP = '__default';
18
18
  exports.COMPACT_TYPE_HORIZONTAL_NOWRAP = 'horizontal-nowrap';
19
19
  exports.DRAGGABLE_CANCEL_CLASS_NAME = 'dashkit_draggable_cancel';
20
20
  exports.FOCUSED_CLASS_NAME = 'dashkit_focused';
21
+ exports.DROPPING_ELEMENT_CLASS_NAME = 'dropping';
@@ -232,12 +232,13 @@ function useMemoStateContext(props) {
232
232
  const reloadItems = react_1.default.useCallback((pluginsRefs, data) => {
233
233
  pluginsRefs.forEach((ref) => ref && ref.reload && ref.reload(data));
234
234
  }, []);
235
- const dragProps = dndContext === null || dndContext === void 0 ? void 0 : dndContext.dragProps;
235
+ const dragPropsContext = dndContext === null || dndContext === void 0 ? void 0 : dndContext.dragProps;
236
+ const onDropDragOverContext = dndContext === null || dndContext === void 0 ? void 0 : dndContext.onDropDragOver;
236
237
  const dragOverPlugin = react_1.default.useMemo(() => {
237
- if (!dragProps) {
238
+ if (!dragPropsContext) {
238
239
  return null;
239
240
  }
240
- const pluginType = dragProps.type;
241
+ const pluginType = dragPropsContext.type;
241
242
  if (props.registerManager.check(pluginType)) {
242
243
  return props.registerManager.getItem(pluginType);
243
244
  }
@@ -246,8 +247,8 @@ function useMemoStateContext(props) {
246
247
  console.error(`Uknown pluginType: ${pluginType}`);
247
248
  return null;
248
249
  }
249
- }, [dragProps, props.registerManager]);
250
- const onDropDragOver = react_1.default.useCallback((_e, gridProps, groupLayout, sharedItem) => {
250
+ }, [dragPropsContext, props.registerManager]);
251
+ const onDropDragOver = react_1.default.useCallback((_e, group, gridProps, groupLayout, sharedItem) => {
251
252
  if (temporaryLayout) {
252
253
  resetTemporaryLayout();
253
254
  return false;
@@ -275,20 +276,30 @@ function useMemoStateContext(props) {
275
276
  maxH < defaultLayout.minH) {
276
277
  return false;
277
278
  }
278
- 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 === null || dragProps === void 0 ? void 0 : dragProps.layout) || {};
279
- return {
279
+ 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, } = (dragPropsContext === null || dragPropsContext === void 0 ? void 0 : dragPropsContext.layout) || {};
280
+ const itemLayout = {
280
281
  h: maxH ? Math.min(h, maxH) : h,
281
282
  w: maxW ? Math.min(w, maxW) : w,
282
283
  };
283
- }, [resetTemporaryLayout, temporaryLayout, dragOverPlugin, dragProps]);
284
+ if ((onDropDragOverContext === null || onDropDragOverContext === void 0 ? void 0 : onDropDragOverContext(Object.assign(Object.assign(Object.assign({}, sharedItem), itemLayout), { parent: group }), sharedItem !== null && sharedItem !== void 0 ? sharedItem : null)) === false) {
285
+ return false;
286
+ }
287
+ return itemLayout;
288
+ }, [
289
+ resetTemporaryLayout,
290
+ temporaryLayout,
291
+ dragOverPlugin,
292
+ dragPropsContext,
293
+ onDropDragOverContext,
294
+ ]);
284
295
  const onDropProp = props.onDrop;
285
296
  const onDrop = react_1.default.useCallback((newLayout, item) => {
286
- if (!dragProps) {
297
+ if (!dragPropsContext) {
287
298
  return;
288
299
  }
289
300
  setTemporaryLayout({
290
301
  data: [...newLayout, item],
291
- dragProps,
302
+ dragProps: dragPropsContext,
292
303
  });
293
304
  onDropProp({
294
305
  newLayout: newLayout.reduce((memo, l) => {
@@ -299,9 +310,9 @@ function useMemoStateContext(props) {
299
310
  }, []),
300
311
  itemLayout: (0, pick_1.default)(item, ITEM_PROPS),
301
312
  commit: resetTemporaryLayout,
302
- dragProps,
313
+ dragProps: dragPropsContext,
303
314
  });
304
- }, [dragProps, onDropProp, setTemporaryLayout, resetTemporaryLayout]);
315
+ }, [dragPropsContext, onDropProp, setTemporaryLayout, resetTemporaryLayout]);
305
316
  const dashkitContextValue = react_1.default.useMemo(() => ({
306
317
  layout: resultLayout,
307
318
  temporaryLayout,
@@ -12,6 +12,10 @@ export type ItemDragProps = {
12
12
  };
13
13
  extra?: any;
14
14
  };
15
+ export type DraggedOverItem = Omit<ConfigLayout, 'x' | 'y' | 'i'> & {
16
+ type: string;
17
+ i?: ConfigLayout['i'];
18
+ };
15
19
  export type ItemDropProps = {
16
20
  commit: () => void;
17
21
  dragProps: ItemDragProps;
@@ -2,8 +2,11 @@ import type ReactGridLayout from 'react-grid-layout';
2
2
  import type { OverlayCustomControlItem } from '../components/OverlayControls/OverlayControls';
3
3
  import { MenuItems } from '../constants';
4
4
  import { AdditionalWidgetLayout } from '../shared';
5
+ export type GridLayoutSettings = ReactGridLayout.ReactGridLayoutProps & {
6
+ noOverlay?: boolean;
7
+ };
5
8
  export interface Settings {
6
- gridLayout?: ReactGridLayout.ReactGridLayoutProps;
9
+ gridLayout?: GridLayoutSettings;
7
10
  theme?: string;
8
11
  isMobile?: boolean;
9
12
  menu?: Array<MenuItem>;
@@ -1,6 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  import type { Layout, Layouts } from 'react-grid-layout';
3
3
  import type { Config, ConfigItem, ConfigLayout } from '../shared';
4
+ import { GridLayoutSettings } from './common';
4
5
  export interface AddConfigItem extends Omit<ConfigItem, 'id' | 'namespace'> {
5
6
  id?: null;
6
7
  namespace?: string;
@@ -31,7 +32,7 @@ export interface DashkitGroupRenderProps {
31
32
  layout: ConfigLayout[];
32
33
  context: any;
33
34
  }
34
- export type ReactGridLayoutProps = Omit<ReactGridLayout.ReactGridLayoutProps, 'children' | 'compactType' | 'innerRef' | 'key' | 'layout' | 'isDraggable' | 'isResizable' | 'onDragStart' | 'onDragStop' | 'onResizeStart' | 'onResizeStop' | 'draggableHandle' | 'isDroppable' | 'onDropDragOver' | 'onDrop' | 'draggableCancel'> & {
35
+ export type ReactGridLayoutProps = Omit<GridLayoutSettings, 'children' | 'compactType' | 'innerRef' | 'key' | 'layout' | 'onDragStart' | 'onDragStop' | 'onResizeStart' | 'onResizeStop' | 'draggableHandle' | 'isDroppable' | 'onDropDragOver' | 'onDrop' | 'draggableCancel'> & {
35
36
  compactType?: CompactType;
36
37
  };
37
38
  export interface DashKitGroup {
@@ -1,7 +1,8 @@
1
1
  import React from 'react';
2
- import type { ItemDragProps } from '../../shared';
2
+ import type { DraggedOverItem, ItemDragProps } from '../../shared';
3
3
  type DashKitDnDWrapperProps = {
4
4
  dragImageSrc?: string;
5
+ onDropDragOver?: (draggedItem: DraggedOverItem, sharedItem: DraggedOverItem | null) => void | boolean;
5
6
  onDragStart?: (dragProps: ItemDragProps) => void;
6
7
  onDragEnd?: () => void;
7
8
  children: React.ReactElement;
@@ -8,23 +8,24 @@ export const DashKitDnDWrapper = (props) => {
8
8
  img.src = props.dragImageSrc || defaultImageSrc;
9
9
  return img;
10
10
  }, [props.dragImageSrc]);
11
+ const onDragStartProp = props.onDragStart;
11
12
  const onDragStart = React.useCallback((_, itemDragProps) => {
12
- var _a;
13
13
  setDragProps(itemDragProps);
14
- (_a = props.onDragStart) === null || _a === void 0 ? void 0 : _a.call(props, itemDragProps);
15
- }, [setDragProps, props.onDragStart]);
14
+ onDragStartProp === null || onDragStartProp === void 0 ? void 0 : onDragStartProp(itemDragProps);
15
+ }, [setDragProps, onDragStartProp]);
16
+ const onDragEndProp = props.onDragEnd;
16
17
  const onDragEnd = React.useCallback((_) => {
17
- var _a;
18
18
  setDragProps(null);
19
- (_a = props.onDragEnd) === null || _a === void 0 ? void 0 : _a.call(props);
20
- }, [setDragProps, props.onDragEnd]);
19
+ onDragEndProp === null || onDragEndProp === void 0 ? void 0 : onDragEndProp();
20
+ }, [setDragProps, onDragEndProp]);
21
21
  const contextValue = React.useMemo(() => {
22
22
  return {
23
23
  dragProps,
24
24
  dragImagePreview,
25
25
  onDragStart,
26
26
  onDragEnd,
27
+ onDropDragOver: props.onDropDragOver,
27
28
  };
28
- }, [dragProps, dragImagePreview, onDragStart, onDragEnd]);
29
+ }, [dragProps, dragImagePreview, onDragStart, onDragEnd, props.onDropDragOver]);
29
30
  return (React.createElement(DashKitDnDContext.Provider, { value: contextValue }, props.children));
30
31
  };
@@ -34,6 +34,14 @@
34
34
  border-color: transparent;
35
35
  pointer-events: none;
36
36
  }
37
+ .dashkit-grid-item_is-dragged-out {
38
+ pointer-events: none;
39
+ }
40
+
41
+ .react-grid-item.dropping {
42
+ user-select: none;
43
+ pointer-events: none;
44
+ }
37
45
 
38
46
  .react-grid-layout {
39
47
  position: relative;
@@ -57,7 +65,6 @@
57
65
  transition: none;
58
66
  z-index: 3;
59
67
  will-change: transform;
60
- pointer-events: none;
61
68
  }
62
69
 
63
70
  .react-grid-item.dashkit-grid-item_is-focused {
@@ -74,6 +81,14 @@
74
81
  user-select: none;
75
82
  }
76
83
 
84
+ .react-grid-focus-capture {
85
+ position: absolute;
86
+ display: block;
87
+ z-index: 6;
88
+ max-width: 100%;
89
+ max-height: 100%;
90
+ }
91
+
77
92
  .react-grid-item .react-resizable-handle {
78
93
  position: absolute;
79
94
  width: 20px;
@@ -94,7 +94,7 @@ class GridItem extends React.PureComponent {
94
94
  render() {
95
95
  // из-за бага, что Grid Items unmounts при изменении static, isDraggable, isResaizable
96
96
  // https://github.com/STRML/react-grid-layout/issues/721
97
- const { style, onMouseDown, onMouseUp, onTouchEnd, onTouchStart, children, className, isDragging, noOverlay, focusable, withCustomHandle, isPlaceholder = false, } = this.props;
97
+ const { style, onMouseDown, onMouseUp, onTouchEnd, onTouchStart, children, className, isDragging, isDraggedOut, noOverlay, focusable, withCustomHandle, isPlaceholder = false, } = this.props;
98
98
  const { editMode } = this.context;
99
99
  const width = Number.parseInt(style.width, 10);
100
100
  const height = Number.parseInt(style.height, 10);
@@ -113,6 +113,7 @@ class GridItem extends React.PureComponent {
113
113
  const { _editActive } = this.props.item.data;
114
114
  return (React.createElement("div", Object.assign({ className: b({
115
115
  'is-dragging': isDragging,
116
+ 'is-dragged-out': isDraggedOut,
116
117
  'is-focused': this.state.isFocused,
117
118
  'with-custom-handle': withCustomHandle,
118
119
  }, preparedClassName), "data-qa": "dashkit-grid-item", style: style, ref: this.props.forwardedRef }, reactGridLayoutProps, (focusable
@@ -133,6 +134,7 @@ GridItem.propTypes = {
133
134
  id: PropTypes.string,
134
135
  item: PropTypes.object,
135
136
  isDragging: PropTypes.bool,
137
+ isDraggedOut: PropTypes.bool,
136
138
  layout: PropTypes.array,
137
139
  forwardedRef: PropTypes.any,
138
140
  forwardedPluginRef: PropTypes.any,
@@ -4,6 +4,7 @@ export default class GridLayout extends React.PureComponent<any, any, any> {
4
4
  pluginsRefs: any[];
5
5
  state: {
6
6
  isDragging: boolean;
7
+ isDraggedOut: boolean;
7
8
  isPageHidden: boolean;
8
9
  currentDraggingElement: null;
9
10
  draggedOverGroup: null;
@@ -47,7 +48,12 @@ export default class GridLayout extends React.PureComponent<any, any, any> {
47
48
  e: any;
48
49
  element: any;
49
50
  };
50
- updateeDraggingElementState(group: any, layoutItem: any, e: any): void;
51
+ updateDraggingElementState(group: any, layoutItem: any, e: any): void;
52
+ _initDragCoordinatesWatcher(element: any): void;
53
+ _parendDragNode: any;
54
+ _forceCursorCapture(parentElement: any, position: any, parentRect: any): void;
55
+ _updateDragCoordinates(e: any): void;
56
+ _resetDragWatcher(): void;
51
57
  _onDragStart(group: any, _newLayout: any, layoutItem: any, _newItem: any, _placeholder: any, e: any, element: any): void;
52
58
  _onDrag(group: any, layout: any, oldItem: any, newItem: any, placeholder: any, e: any, element: any): void;
53
59
  _onDragStop(group: any, layout: any, oldItem: any, newItem: any, placeholder: any, e: any, element: any): void;
@@ -139,16 +139,24 @@ class GridLayout extends React.PureComponent {
139
139
  if (currentDraggingElement) {
140
140
  const { h, w, i } = currentDraggingElement.layoutItem;
141
141
  const { type } = currentDraggingElement.item;
142
- return onDropDragOver(e, properties, layout, { h, w, i, type });
142
+ const sharedItem = {
143
+ h,
144
+ w,
145
+ i,
146
+ type,
147
+ parent: currentDraggingElement.group,
148
+ };
149
+ return onDropDragOver(e, group, properties, layout, sharedItem);
143
150
  }
144
151
  if (dragOverPlugin) {
145
- return onDropDragOver(e, properties, layout);
152
+ return onDropDragOver(e, group, properties, layout);
146
153
  }
147
154
  return false;
148
155
  };
149
156
  this.pluginsRefs = [];
150
157
  this.state = {
151
158
  isDragging: false,
159
+ isDraggedOut: false,
152
160
  isPageHidden: false,
153
161
  currentDraggingElement: null,
154
162
  draggedOverGroup: null,
@@ -266,7 +274,7 @@ class GridLayout extends React.PureComponent {
266
274
  element,
267
275
  };
268
276
  }
269
- updateeDraggingElementState(group, layoutItem, e) {
277
+ updateDraggingElementState(group, layoutItem, e) {
270
278
  let currentDraggingElement = this.state.currentDraggingElement;
271
279
  if (!currentDraggingElement) {
272
280
  const { temporaryLayout } = this.context;
@@ -284,6 +292,59 @@ class GridLayout extends React.PureComponent {
284
292
  }
285
293
  this.setState({ currentDraggingElement, draggedOverGroup: group });
286
294
  }
295
+ _initDragCoordinatesWatcher(element) {
296
+ if (!this._parendDragNode) {
297
+ this._parendDragNode = element.parentElement;
298
+ this.setState({ isDraggedOut: false });
299
+ }
300
+ }
301
+ // When element is going back and prointer-event: none is removed mouse enter event is not fired
302
+ // So to trigger it we are forcing this event by adding transparent block under the mouse
303
+ _forceCursorCapture(parentElement, position, parentRect) {
304
+ const block = document.createElement('div');
305
+ block.classList.add('react-grid-focus-capture');
306
+ const blockSize = 44;
307
+ const offset = blockSize / 2;
308
+ // Keeping elemnt inside current grid
309
+ const top = Math.min(Math.max(position.top - offset, 0), parentRect.height - blockSize);
310
+ const left = Math.min(Math.max(position.left - offset, 0), parentRect.width - blockSize);
311
+ block.style.width = `${blockSize}px`;
312
+ block.style.height = `${blockSize}px`;
313
+ block.style.top = `${top}px`;
314
+ block.style.left = `${left}px`;
315
+ parentElement.appendChild(block);
316
+ setTimeout(() => {
317
+ block.remove();
318
+ }, 100);
319
+ }
320
+ _updateDragCoordinates(e) {
321
+ const parent = this._parendDragNode;
322
+ const parentRect = parent.getBoundingClientRect();
323
+ const { clientX, clientY } = e;
324
+ let isDraggedOut = this.state.isDraggedOut;
325
+ if (clientX < parentRect.left ||
326
+ clientX > parentRect.right ||
327
+ clientY < parentRect.top ||
328
+ clientY > parentRect.bottom) {
329
+ isDraggedOut = true;
330
+ }
331
+ else {
332
+ isDraggedOut = false;
333
+ }
334
+ if (isDraggedOut !== this.state.isDraggedOut) {
335
+ this.setState({ isDraggedOut });
336
+ if (!isDraggedOut) {
337
+ this._forceCursorCapture(parent, {
338
+ top: clientY - parentRect.top,
339
+ left: clientX - parentRect.left,
340
+ }, parentRect);
341
+ }
342
+ }
343
+ }
344
+ _resetDragWatcher() {
345
+ this._parendDragNode = null;
346
+ this.setState({ isDraggedOut: false });
347
+ }
287
348
  _onDragStart(group, _newLayout, layoutItem, _newItem, _placeholder, e, element) {
288
349
  var _a;
289
350
  (_a = this.context.onDragStart) === null || _a === void 0 ? void 0 : _a.call(this, this.prepareDefaultArguments(group, _newLayout, layoutItem, _newItem, _placeholder, e, element));
@@ -291,16 +352,21 @@ class GridLayout extends React.PureComponent {
291
352
  this.setState({ isDragging: true });
292
353
  }
293
354
  else {
294
- this.updateeDraggingElementState(group, layoutItem, e);
355
+ this._initDragCoordinatesWatcher(element);
356
+ this.updateDraggingElementState(group, layoutItem, e);
295
357
  this.setState({ isDragging: true });
296
358
  }
297
359
  }
298
360
  _onDrag(group, layout, oldItem, newItem, placeholder, e, element) {
299
361
  var _a;
362
+ if (!this.context.dragOverPlugin) {
363
+ this._updateDragCoordinates(e);
364
+ }
300
365
  (_a = this.context.onDrag) === null || _a === void 0 ? void 0 : _a.call(this, this.prepareDefaultArguments(group, layout, oldItem, newItem, placeholder, e, element));
301
366
  }
302
367
  _onDragStop(group, layout, oldItem, newItem, placeholder, e, element) {
303
368
  var _a;
369
+ this._resetDragWatcher();
304
370
  this._onStop(group, layout);
305
371
  (_a = this.context.onDragStop) === null || _a === void 0 ? void 0 : _a.call(this, this.prepareDefaultArguments(group, layout, oldItem, newItem, placeholder, e, element));
306
372
  }
@@ -343,7 +409,8 @@ class GridLayout extends React.PureComponent {
343
409
  renderGroup(group, renderLayout, renderItems, offset = 0, groupGridProperties) {
344
410
  const { registerManager, editMode, noOverlay, focusable, draggableHandleClassName, outerDnDEnable, onItemMountChange, onItemRender, } = this.context;
345
411
  const { currentDraggingElement, draggedOverGroup } = this.state;
346
- const properties = groupGridProperties
412
+ const hasOwnGroupProperties = typeof groupGridProperties === 'function';
413
+ const properties = hasOwnGroupProperties
347
414
  ? groupGridProperties(Object.assign({}, registerManager.gridLayout))
348
415
  : registerManager.gridLayout;
349
416
  let { compactType } = properties;
@@ -356,23 +423,22 @@ class GridLayout extends React.PureComponent {
356
423
  group === currentDraggingElement.group &&
357
424
  draggedOverGroup !== null &&
358
425
  draggedOverGroup !== group;
359
- return (React.createElement(Layout
360
- // Group properties
361
- , Object.assign({}, properties, {
426
+ return (React.createElement(Layout, Object.assign({ isDraggable: editMode, isResizable: editMode }, properties, { key: `group_${group}`,
362
427
  // Layout props
363
- compactType: compactType, layout: layout, key: `group_${group}`, isDraggable: editMode, isResizable: editMode, draggableCancel: `.${DRAGGABLE_CANCEL_CLASS_NAME}` }, (draggableHandleClassName
364
- ? { draggableHandle: `.${draggableHandleClassName}` }
365
- : null), {
428
+ compactType: compactType, layout: layout, draggableCancel: `.${DRAGGABLE_CANCEL_CLASS_NAME}`, draggableHandle: draggableHandleClassName ? `.${draggableHandleClassName}` : null,
366
429
  // Default callbacks
367
430
  onDragStart: callbacks.onDragStart, onDrag: callbacks.onDrag, onDragStop: callbacks.onDragStop, onResizeStart: callbacks.onResizeStart, onResize: callbacks.onResize, onResizeStop: callbacks.onResizeStop,
368
431
  // External Drag N Drop options
369
- onDragTargetRestore: callbacks.onDragTargetRestore, onDropDragOver: callbacks.onDropDragOver, onDrop: callbacks.onDrop, hasSharedDragItem: hasSharedDragItem, sharedDragPosition: currentDraggingElement === null || currentDraggingElement === void 0 ? void 0 : currentDraggingElement.cursorPosition, isDragCaptured: isDragCaptured }, (outerDnDEnable
370
- ? {
371
- isDroppable: true,
372
- }
373
- : null)),
432
+ onDragTargetRestore: callbacks.onDragTargetRestore, onDropDragOver: callbacks.onDropDragOver, onDrop: callbacks.onDrop, hasSharedDragItem: hasSharedDragItem, sharedDragPosition: currentDraggingElement === null || currentDraggingElement === void 0 ? void 0 : currentDraggingElement.cursorPosition, isDragCaptured: isDragCaptured, isDroppable: Boolean(outerDnDEnable) && editMode }),
374
433
  renderItems.map((item, i) => {
375
- 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), onItemMountChange: onItemMountChange, onItemRender: onItemRender }));
434
+ const keyId = item.id;
435
+ const isDragging = this.state.isDragging;
436
+ const isCurrentDraggedItem = (currentDraggingElement === null || currentDraggingElement === void 0 ? void 0 : currentDraggingElement.item.id) === keyId;
437
+ const isDraggedOut = isCurrentDraggedItem && this.state.isDraggedOut;
438
+ const itemNoOverlay = hasOwnGroupProperties && 'noOverlay' in properties
439
+ ? properties.noOverlay
440
+ : noOverlay;
441
+ return (React.createElement(GridItem, { forwardedPluginRef: this.getMemoForwardRefCallback(offset + i), key: keyId, id: keyId, item: item, layout: layout, adjustWidgetLayout: this.adjustWidgetLayout, isDragging: isDragging, isDraggedOut: isDraggedOut, noOverlay: itemNoOverlay, focusable: focusable, withCustomHandle: Boolean(draggableHandleClassName), onItemMountChange: onItemMountChange, onItemRender: onItemRender }));
376
442
  }),
377
443
  this.renderTemporaryPlaceholder()));
378
444
  }
@@ -424,13 +490,14 @@ class GridLayout extends React.PureComponent {
424
490
  const element = this.renderGroup(id, layout, items, offset, group.gridProperties);
425
491
  offset += items.length;
426
492
  if (group.render) {
427
- return group.render(id, element, {
493
+ const groupContext = {
428
494
  config,
429
495
  editMode,
430
496
  items,
431
497
  layout,
432
498
  context,
433
- });
499
+ };
500
+ return group.render(id, element, groupContext);
434
501
  }
435
502
  return element;
436
503
  });
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import ReactGridLayout, { WidthProvider, utils } from 'react-grid-layout';
3
- import { OVERLAY_CLASS_NAME } from '../../constants';
3
+ import { DROPPING_ELEMENT_CLASS_NAME, OVERLAY_CLASS_NAME } from '../../constants';
4
4
  class DragOverLayout extends ReactGridLayout {
5
5
  constructor(...args) {
6
6
  super(...args);
@@ -153,6 +153,20 @@ class DragOverLayout extends ReactGridLayout {
153
153
  var _a;
154
154
  return ((_a = this.props.innerRef) === null || _a === void 0 ? void 0 : _a.current) || null;
155
155
  }
156
+ calculateDroppingPosition(itemProps) {
157
+ const { containerWidth, cols, w, h, rowHeight, margin, transformScale, droppingPosition } = itemProps;
158
+ const { sharedDragPosition } = this.props;
159
+ let offsetX, offsetY;
160
+ if (sharedDragPosition) {
161
+ offsetX = sharedDragPosition.offsetX;
162
+ offsetY = sharedDragPosition.offsetY;
163
+ }
164
+ else {
165
+ offsetX = (((containerWidth / cols) * w) / 2 || 0) * transformScale;
166
+ offsetY = ((h * rowHeight + (h - 1) * margin[1]) / 2 || 0) * transformScale;
167
+ }
168
+ return Object.assign(Object.assign({}, droppingPosition), { left: droppingPosition.left - offsetX, top: droppingPosition.top - offsetY });
169
+ }
156
170
  // Drop item from outside gets 0,0 droppingPosition
157
171
  // centering cursor on newly creted grid item
158
172
  // And cause grid-layout using it's own GridItem to make it look
@@ -163,26 +177,14 @@ class DragOverLayout extends ReactGridLayout {
163
177
  if (!gridItem) {
164
178
  return gridItem;
165
179
  }
166
- const { props: itemProps } = gridItem;
167
180
  if (isDroppingItem) {
168
- const { containerWidth, cols, w, h, rowHeight, margin, transformScale, droppingPosition, } = itemProps;
169
- const { sharedDragPosition, hasSharedDragItem } = this.props;
170
- let offsetX, offsetY;
171
- if (sharedDragPosition) {
172
- offsetX = sharedDragPosition.offsetX;
173
- offsetY = sharedDragPosition.offsetY;
174
- }
175
- else {
176
- offsetX = (((containerWidth / cols) * w) / 2 || 0) * transformScale;
177
- offsetY = ((h * rowHeight + (h - 1) * margin[1]) / 2 || 0) * transformScale;
178
- }
179
- const style = gridItem.props.style || null;
180
181
  // React.cloneElement is just cleaner then copy-paste whole processGridItem method
181
182
  return React.cloneElement(gridItem, {
182
- // hiding previre if dragging shared item
183
- style: hasSharedDragItem ? Object.assign(Object.assign({}, style), { opacity: 0 }) : style,
184
- className: OVERLAY_CLASS_NAME,
185
- droppingPosition: Object.assign(Object.assign({}, droppingPosition), { left: droppingPosition.left - offsetX, top: droppingPosition.top - offsetY }),
183
+ // hiding preview if dragging shared item
184
+ style: this.props.hasSharedDragItem
185
+ ? Object.assign(Object.assign({}, gridItem.props.style), { opacity: 0 }) : gridItem.props.style,
186
+ className: `${OVERLAY_CLASS_NAME} ${DROPPING_ELEMENT_CLASS_NAME}`,
187
+ droppingPosition: this.calculateDroppingPosition(gridItem.props),
186
188
  });
187
189
  }
188
190
  return gridItem;
@@ -29,7 +29,7 @@ class OverlayControls extends React.Component {
29
29
  const { view, size, onItemClick } = this.props;
30
30
  const { title, handler, icon, iconSize, qa } = item;
31
31
  const onItemClickHandler = typeof handler === 'function' ? handler : noop;
32
- return (React.createElement(Button, { key: index, view: view, size: size, title: title, pin: this.getControlItemPinStyle(index, length), onClick: () => {
32
+ return (React.createElement(Button, { key: `control_${index}`, view: view, size: size, title: title, pin: this.getControlItemPinStyle(index, length), onClick: () => {
33
33
  onItemClickHandler(this.props.configItem);
34
34
  onItemClick === null || onItemClick === void 0 ? void 0 : onItemClick();
35
35
  }, qa: qa },
@@ -40,7 +40,7 @@ class OverlayControls extends React.Component {
40
40
  if (this.context.overlayControls === null) {
41
41
  return null;
42
42
  }
43
- return (React.createElement(Button, { view: view, size: size, title: i18n('label_settings'), pin: "round-brick", onClick: this.onEditItem, qa: "dashkit-overlay-control-settings" },
43
+ return (React.createElement(Button, { key: 'control_default', view: view, size: size, title: i18n('label_settings'), pin: "round-brick", onClick: this.onEditItem, qa: "dashkit-overlay-control-settings" },
44
44
  React.createElement(Icon, { data: Gear, size: OVERLAY_ICON_SIZE })));
45
45
  };
46
46
  this.getCustomLeftOverlayControls = () => {
@@ -158,10 +158,9 @@ class OverlayControls extends React.Component {
158
158
  renderControls() {
159
159
  const customLeftControls = this.getCustomLeftOverlayControls();
160
160
  const hasCustomOverlayLeftControls = Boolean(customLeftControls.length);
161
- const defaultControl = this.getDefaultControls();
162
161
  const controls = hasCustomOverlayLeftControls
163
162
  ? customLeftControls.map((item, index, items) => this.renderControlsItem(item, index, items.length + 1))
164
- : defaultControl;
163
+ : this.getDefaultControls();
165
164
  const menu = this.renderMenu(controls === null);
166
165
  return (React.createElement("div", { className: b('default-controls', {
167
166
  'with-custom-left': hasCustomOverlayLeftControls,
@@ -228,7 +227,7 @@ class OverlayControls extends React.Component {
228
227
  if (items.length === 0) {
229
228
  return null;
230
229
  }
231
- return (React.createElement(DropdownMenu, { items: items, renderSwitcher: (props) => (React.createElement(Button, Object.assign({}, props, { view: view, size: size, pin: isOnlyOneItem ? 'round-round' : 'brick-round', qa: "dashkit-overlay-control-menu" }),
230
+ return (React.createElement(DropdownMenu, { key: 'controls_dropdown', items: items, renderSwitcher: (props) => (React.createElement(Button, Object.assign({}, props, { view: view, size: size, pin: isOnlyOneItem ? 'round-round' : 'brick-round', qa: "dashkit-overlay-control-menu" }),
232
231
  React.createElement(Icon, { data: Ellipsis, size: OVERLAY_ICON_SIZE }))), popupProps: {
233
232
  contentClassName: DRAGGABLE_CANCEL_CLASS_NAME,
234
233
  } }));
@@ -15,3 +15,4 @@ export declare const DEFAULT_GROUP = "__default";
15
15
  export declare const COMPACT_TYPE_HORIZONTAL_NOWRAP = "horizontal-nowrap";
16
16
  export declare const DRAGGABLE_CANCEL_CLASS_NAME = "dashkit_draggable_cancel";
17
17
  export declare const FOCUSED_CLASS_NAME = "dashkit_focused";
18
+ export declare const DROPPING_ELEMENT_CLASS_NAME = "dropping";
@@ -15,3 +15,4 @@ export const DEFAULT_GROUP = '__default';
15
15
  export const COMPACT_TYPE_HORIZONTAL_NOWRAP = 'horizontal-nowrap';
16
16
  export const DRAGGABLE_CANCEL_CLASS_NAME = 'dashkit_draggable_cancel';
17
17
  export const FOCUSED_CLASS_NAME = 'dashkit_focused';
18
+ export const DROPPING_ELEMENT_CLASS_NAME = 'dropping';
@@ -229,12 +229,13 @@ function useMemoStateContext(props) {
229
229
  const reloadItems = React.useCallback((pluginsRefs, data) => {
230
230
  pluginsRefs.forEach((ref) => ref && ref.reload && ref.reload(data));
231
231
  }, []);
232
- const dragProps = dndContext === null || dndContext === void 0 ? void 0 : dndContext.dragProps;
232
+ const dragPropsContext = dndContext === null || dndContext === void 0 ? void 0 : dndContext.dragProps;
233
+ const onDropDragOverContext = dndContext === null || dndContext === void 0 ? void 0 : dndContext.onDropDragOver;
233
234
  const dragOverPlugin = React.useMemo(() => {
234
- if (!dragProps) {
235
+ if (!dragPropsContext) {
235
236
  return null;
236
237
  }
237
- const pluginType = dragProps.type;
238
+ const pluginType = dragPropsContext.type;
238
239
  if (props.registerManager.check(pluginType)) {
239
240
  return props.registerManager.getItem(pluginType);
240
241
  }
@@ -243,8 +244,8 @@ function useMemoStateContext(props) {
243
244
  console.error(`Uknown pluginType: ${pluginType}`);
244
245
  return null;
245
246
  }
246
- }, [dragProps, props.registerManager]);
247
- const onDropDragOver = React.useCallback((_e, gridProps, groupLayout, sharedItem) => {
247
+ }, [dragPropsContext, props.registerManager]);
248
+ const onDropDragOver = React.useCallback((_e, group, gridProps, groupLayout, sharedItem) => {
248
249
  if (temporaryLayout) {
249
250
  resetTemporaryLayout();
250
251
  return false;
@@ -272,20 +273,30 @@ function useMemoStateContext(props) {
272
273
  maxH < defaultLayout.minH) {
273
274
  return false;
274
275
  }
275
- 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 === null || dragProps === void 0 ? void 0 : dragProps.layout) || {};
276
- return {
276
+ 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, } = (dragPropsContext === null || dragPropsContext === void 0 ? void 0 : dragPropsContext.layout) || {};
277
+ const itemLayout = {
277
278
  h: maxH ? Math.min(h, maxH) : h,
278
279
  w: maxW ? Math.min(w, maxW) : w,
279
280
  };
280
- }, [resetTemporaryLayout, temporaryLayout, dragOverPlugin, dragProps]);
281
+ if ((onDropDragOverContext === null || onDropDragOverContext === void 0 ? void 0 : onDropDragOverContext(Object.assign(Object.assign(Object.assign({}, sharedItem), itemLayout), { parent: group }), sharedItem !== null && sharedItem !== void 0 ? sharedItem : null)) === false) {
282
+ return false;
283
+ }
284
+ return itemLayout;
285
+ }, [
286
+ resetTemporaryLayout,
287
+ temporaryLayout,
288
+ dragOverPlugin,
289
+ dragPropsContext,
290
+ onDropDragOverContext,
291
+ ]);
281
292
  const onDropProp = props.onDrop;
282
293
  const onDrop = React.useCallback((newLayout, item) => {
283
- if (!dragProps) {
294
+ if (!dragPropsContext) {
284
295
  return;
285
296
  }
286
297
  setTemporaryLayout({
287
298
  data: [...newLayout, item],
288
- dragProps,
299
+ dragProps: dragPropsContext,
289
300
  });
290
301
  onDropProp({
291
302
  newLayout: newLayout.reduce((memo, l) => {
@@ -296,9 +307,9 @@ function useMemoStateContext(props) {
296
307
  }, []),
297
308
  itemLayout: pick(item, ITEM_PROPS),
298
309
  commit: resetTemporaryLayout,
299
- dragProps,
310
+ dragProps: dragPropsContext,
300
311
  });
301
- }, [dragProps, onDropProp, setTemporaryLayout, resetTemporaryLayout]);
312
+ }, [dragPropsContext, onDropProp, setTemporaryLayout, resetTemporaryLayout]);
302
313
  const dashkitContextValue = React.useMemo(() => ({
303
314
  layout: resultLayout,
304
315
  temporaryLayout,
@@ -12,6 +12,10 @@ export type ItemDragProps = {
12
12
  };
13
13
  extra?: any;
14
14
  };
15
+ export type DraggedOverItem = Omit<ConfigLayout, 'x' | 'y' | 'i'> & {
16
+ type: string;
17
+ i?: ConfigLayout['i'];
18
+ };
15
19
  export type ItemDropProps = {
16
20
  commit: () => void;
17
21
  dragProps: ItemDragProps;
@@ -2,8 +2,11 @@ import type ReactGridLayout from 'react-grid-layout';
2
2
  import type { OverlayCustomControlItem } from '../components/OverlayControls/OverlayControls';
3
3
  import { MenuItems } from '../constants';
4
4
  import { AdditionalWidgetLayout } from '../shared';
5
+ export type GridLayoutSettings = ReactGridLayout.ReactGridLayoutProps & {
6
+ noOverlay?: boolean;
7
+ };
5
8
  export interface Settings {
6
- gridLayout?: ReactGridLayout.ReactGridLayoutProps;
9
+ gridLayout?: GridLayoutSettings;
7
10
  theme?: string;
8
11
  isMobile?: boolean;
9
12
  menu?: Array<MenuItem>;
@@ -1,6 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  import type { Layout, Layouts } from 'react-grid-layout';
3
3
  import type { Config, ConfigItem, ConfigLayout } from '../shared';
4
+ import { GridLayoutSettings } from './common';
4
5
  export interface AddConfigItem extends Omit<ConfigItem, 'id' | 'namespace'> {
5
6
  id?: null;
6
7
  namespace?: string;
@@ -31,7 +32,7 @@ export interface DashkitGroupRenderProps {
31
32
  layout: ConfigLayout[];
32
33
  context: any;
33
34
  }
34
- export type ReactGridLayoutProps = Omit<ReactGridLayout.ReactGridLayoutProps, 'children' | 'compactType' | 'innerRef' | 'key' | 'layout' | 'isDraggable' | 'isResizable' | 'onDragStart' | 'onDragStop' | 'onResizeStart' | 'onResizeStop' | 'draggableHandle' | 'isDroppable' | 'onDropDragOver' | 'onDrop' | 'draggableCancel'> & {
35
+ export type ReactGridLayoutProps = Omit<GridLayoutSettings, 'children' | 'compactType' | 'innerRef' | 'key' | 'layout' | 'onDragStart' | 'onDragStop' | 'onResizeStart' | 'onResizeStop' | 'draggableHandle' | 'isDroppable' | 'onDropDragOver' | 'onDrop' | 'draggableCancel'> & {
35
36
  compactType?: CompactType;
36
37
  };
37
38
  export interface DashKitGroup {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/dashkit",
3
- "version": "8.17.3",
3
+ "version": "8.18.0",
4
4
  "description": "Library for rendering dashboard grid layout",
5
5
  "license": "MIT",
6
6
  "repository": {