@gravity-ui/dashkit 8.1.0 → 8.2.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 (72) hide show
  1. package/README.md +95 -0
  2. package/build/cjs/components/ActionPanel/ActionPanel.d.ts +4 -1
  3. package/build/cjs/components/ActionPanel/ActionPanel.js +10 -6
  4. package/build/cjs/components/ActionPanel/types.d.ts +3 -1
  5. package/build/cjs/components/DashKit/DashKit.d.ts +4 -3
  6. package/build/cjs/components/DashKit/DashKit.js +1 -0
  7. package/build/cjs/components/DashKitDnDWrapper/DashKitDnDWrapper.d.ts +10 -0
  8. package/build/cjs/components/DashKitDnDWrapper/DashKitDnDWrapper.js +35 -0
  9. package/build/cjs/components/GridItem/GridItem.d.ts +2 -2
  10. package/build/cjs/components/GridItem/GridItem.js +8 -5
  11. package/build/cjs/components/GridLayout/GridLayout.d.ts +3 -0
  12. package/build/cjs/components/GridLayout/GridLayout.js +51 -11
  13. package/build/cjs/components/GridLayout/ReactGridLayout.d.ts +3 -0
  14. package/build/cjs/components/GridLayout/ReactGridLayout.js +49 -0
  15. package/build/cjs/components/Item/Item.css +1 -1
  16. package/build/cjs/components/Item/Item.d.ts +1 -0
  17. package/build/cjs/components/Item/Item.js +7 -1
  18. package/build/cjs/components/OverlayControls/OverlayControls.js +2 -2
  19. package/build/cjs/components/index.d.ts +1 -0
  20. package/build/cjs/components/index.js +1 -0
  21. package/build/cjs/constants/common.d.ts +4 -0
  22. package/build/cjs/constants/common.js +5 -1
  23. package/build/cjs/context/DashKitContext.d.ts +1 -0
  24. package/build/cjs/context/DashKitContext.js +3 -1
  25. package/build/cjs/hocs/prepareItem.d.ts +1 -0
  26. package/build/cjs/hocs/prepareItem.js +4 -2
  27. package/build/cjs/hocs/withContext.js +84 -6
  28. package/build/cjs/hooks/useDnDItemProps.d.ts +10 -0
  29. package/build/cjs/hooks/useDnDItemProps.js +30 -0
  30. package/build/cjs/shared/constants/common.d.ts +1 -0
  31. package/build/cjs/shared/constants/common.js +2 -1
  32. package/build/cjs/shared/types/common.d.ts +15 -0
  33. package/build/cjs/typings/config.d.ts +3 -0
  34. package/build/cjs/typings/plugin.d.ts +1 -0
  35. package/build/cjs/utils/update-manager.d.ts +2 -2
  36. package/build/cjs/utils/update-manager.js +25 -7
  37. package/build/esm/components/ActionPanel/ActionPanel.d.ts +4 -1
  38. package/build/esm/components/ActionPanel/ActionPanel.js +8 -5
  39. package/build/esm/components/ActionPanel/types.d.ts +3 -1
  40. package/build/esm/components/DashKit/DashKit.d.ts +4 -3
  41. package/build/esm/components/DashKit/DashKit.js +1 -0
  42. package/build/esm/components/DashKitDnDWrapper/DashKitDnDWrapper.d.ts +10 -0
  43. package/build/esm/components/DashKitDnDWrapper/DashKitDnDWrapper.js +30 -0
  44. package/build/esm/components/GridItem/GridItem.d.ts +2 -2
  45. package/build/esm/components/GridItem/GridItem.js +8 -5
  46. package/build/esm/components/GridLayout/GridLayout.d.ts +3 -0
  47. package/build/esm/components/GridLayout/GridLayout.js +52 -12
  48. package/build/esm/components/GridLayout/ReactGridLayout.d.ts +3 -0
  49. package/build/esm/components/GridLayout/ReactGridLayout.js +45 -0
  50. package/build/esm/components/Item/Item.css +1 -1
  51. package/build/esm/components/Item/Item.d.ts +1 -0
  52. package/build/esm/components/Item/Item.js +7 -1
  53. package/build/esm/components/OverlayControls/OverlayControls.js +2 -2
  54. package/build/esm/components/index.d.ts +1 -0
  55. package/build/esm/components/index.js +1 -0
  56. package/build/esm/constants/common.d.ts +4 -0
  57. package/build/esm/constants/common.js +4 -0
  58. package/build/esm/context/DashKitContext.d.ts +1 -0
  59. package/build/esm/context/DashKitContext.js +2 -1
  60. package/build/esm/hocs/prepareItem.d.ts +1 -0
  61. package/build/esm/hocs/prepareItem.js +4 -2
  62. package/build/esm/hocs/withContext.js +85 -7
  63. package/build/esm/hooks/useDnDItemProps.d.ts +10 -0
  64. package/build/esm/hooks/useDnDItemProps.js +25 -0
  65. package/build/esm/shared/constants/common.d.ts +1 -0
  66. package/build/esm/shared/constants/common.js +1 -0
  67. package/build/esm/shared/types/common.d.ts +15 -0
  68. package/build/esm/typings/config.d.ts +3 -0
  69. package/build/esm/typings/plugin.d.ts +1 -0
  70. package/build/esm/utils/update-manager.d.ts +2 -2
  71. package/build/esm/utils/update-manager.js +25 -7
  72. package/package.json +1 -1
@@ -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 common_1 = require("../constants/common");
7
8
  const DashKitContext_1 = require("../context/DashKitContext");
8
9
  const shared_1 = require("../shared");
9
10
  const utils_1 = require("../utils");
@@ -15,6 +16,10 @@ function useMemoStateContext(props) {
15
16
  // (высота) из конфига будет запоминаться в originalLayouts, новое значение высоты в adjustedLayouts
16
17
  const originalLayouts = react_1.default.useRef({});
17
18
  const adjustedLayouts = react_1.default.useRef({});
19
+ const [temporaryLayout, setTemporaryLayout] = react_1.default.useState(null);
20
+ const resetTemporaryLayout = react_1.default.useCallback(() => setTemporaryLayout(null), [setTemporaryLayout]);
21
+ const dndContext = react_1.default.useContext(DashKitContext_1.DashKitDnDContext);
22
+ const outerDnDEnable = Boolean(dndContext);
18
23
  // TODO: need move originalLayouts, adjustedLayouts to state
19
24
  const [layoutUpdateCounter, forceUpdateLayout] = react_1.default.useState(0);
20
25
  const onChange = react_1.default.useCallback(({ config = props.config, itemsStateAndParams = props.itemsStateAndParams }) => {
@@ -45,12 +50,27 @@ function useMemoStateContext(props) {
45
50
  }
46
51
  }, [props.config, onChange]);
47
52
  const onItemRemove = react_1.default.useCallback((id) => {
48
- onChange(utils_1.UpdateManager.removeItem({
49
- id,
50
- config: props.config,
51
- itemsStateAndParams: props.itemsStateAndParams,
52
- }));
53
- }, [props.config, props.itemsStateAndParams, onChange]);
53
+ if (id === common_1.TEMPORARY_ITEM_ID) {
54
+ resetTemporaryLayout();
55
+ }
56
+ else {
57
+ if (temporaryLayout) {
58
+ setTemporaryLayout(Object.assign(Object.assign({}, temporaryLayout), { data: temporaryLayout.data.filter(({ i }) => i !== id) }));
59
+ }
60
+ onChange(utils_1.UpdateManager.removeItem({
61
+ id,
62
+ config: props.config,
63
+ itemsStateAndParams: props.itemsStateAndParams,
64
+ }));
65
+ }
66
+ }, [
67
+ props.config,
68
+ props.itemsStateAndParams,
69
+ temporaryLayout,
70
+ onChange,
71
+ setTemporaryLayout,
72
+ resetTemporaryLayout,
73
+ ]);
54
74
  const onItemStateAndParamsChange = react_1.default.useCallback((id, stateAndParams, options) => {
55
75
  onChange({
56
76
  itemsStateAndParams: utils_1.UpdateManager.changeStateAndParams({
@@ -124,8 +144,57 @@ function useMemoStateContext(props) {
124
144
  const reloadItems = react_1.default.useCallback((pluginsRefs, data) => {
125
145
  pluginsRefs.forEach((ref) => ref && ref.reload && ref.reload(data));
126
146
  }, []);
147
+ const dragProps = dndContext === null || dndContext === void 0 ? void 0 : dndContext.dragProps;
148
+ const dragOverPlugin = react_1.default.useMemo(() => {
149
+ if (!dragProps) {
150
+ return null;
151
+ }
152
+ const pluginType = dragProps.type;
153
+ if (props.registerManager.check(pluginType)) {
154
+ return props.registerManager.getItem(pluginType);
155
+ }
156
+ else {
157
+ // eslint-disable-next-line no-console
158
+ console.error(`Uknown pluginType: ${pluginType}`);
159
+ return null;
160
+ }
161
+ }, [dragProps, props.registerManager]);
162
+ const onDropDragOver = react_1.default.useCallback(() => {
163
+ if (temporaryLayout) {
164
+ resetTemporaryLayout();
165
+ return false;
166
+ }
167
+ if (dragOverPlugin) {
168
+ const { defaultLayout } = dragOverPlugin;
169
+ 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 };
171
+ }
172
+ return false;
173
+ }, [resetTemporaryLayout, temporaryLayout, dragOverPlugin, dragProps]);
174
+ const onDrop = react_1.default.useCallback((newLayout, item) => {
175
+ if (!dragProps) {
176
+ return;
177
+ }
178
+ setTemporaryLayout({
179
+ data: newLayout,
180
+ dragProps,
181
+ });
182
+ const { i, w, h, x, y } = item;
183
+ props.onDrop({
184
+ 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 });
187
+ }
188
+ return memo;
189
+ }, []),
190
+ itemLayout: { w, h, x, y },
191
+ commit: resetTemporaryLayout,
192
+ dragProps,
193
+ });
194
+ }, [dragProps, props.onDrop, setTemporaryLayout, resetTemporaryLayout]);
127
195
  return react_1.default.useMemo(() => ({
128
196
  layout: resultLayout,
197
+ temporaryLayout,
129
198
  config: props.config,
130
199
  context: props.context,
131
200
  noOverlay: props.noOverlay,
@@ -139,6 +208,8 @@ function useMemoStateContext(props) {
139
208
  registerManager: props.registerManager,
140
209
  onItemStateAndParamsChange,
141
210
  removeItem: onItemRemove,
211
+ onDrop,
212
+ onDropDragOver,
142
213
  editItem: props.onItemEdit,
143
214
  layoutChange: onLayoutChange,
144
215
  getItemsMeta,
@@ -147,8 +218,11 @@ function useMemoStateContext(props) {
147
218
  revertToOriginalLayout,
148
219
  forwardedMetaRef: props.forwardedMetaRef,
149
220
  draggableHandleClassName: props.draggableHandleClassName,
221
+ outerDnDEnable,
222
+ dragOverPlugin,
150
223
  }), [
151
224
  resultLayout,
225
+ temporaryLayout,
152
226
  props.config,
153
227
  props.context,
154
228
  props.noOverlay,
@@ -161,6 +235,8 @@ function useMemoStateContext(props) {
161
235
  props.registerManager,
162
236
  onItemStateAndParamsChange,
163
237
  onItemRemove,
238
+ onDrop,
239
+ onDropDragOver,
164
240
  props.onItemEdit,
165
241
  onLayoutChange,
166
242
  getItemsMeta,
@@ -169,6 +245,8 @@ function useMemoStateContext(props) {
169
245
  revertToOriginalLayout,
170
246
  props.forwardedMetaRef,
171
247
  props.draggableHandleClassName,
248
+ outerDnDEnable,
249
+ dragOverPlugin,
172
250
  ]);
173
251
  }
174
252
  function withContext(Component) {
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { ActionPanelItem } from '../components';
3
+ type DndProps = null | {
4
+ draggable: true;
5
+ unselectable: 'on';
6
+ onDragStart: React.DragEventHandler<HTMLDivElement>;
7
+ onDragEnd: React.DragEventHandler<HTMLDivElement>;
8
+ };
9
+ export declare const useDnDItemProps: (item: ActionPanelItem) => DndProps;
10
+ export {};
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useDnDItemProps = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importDefault(require("react"));
6
+ const DashKitContext_1 = require("../context/DashKitContext");
7
+ const useDnDItemProps = (item) => {
8
+ const dragContext = react_1.default.useContext(DashKitContext_1.DashKitDnDContext);
9
+ const onDragStart = react_1.default.useCallback((e) => {
10
+ if (dragContext && item.dragProps) {
11
+ dragContext.onDragStart(e, item.dragProps);
12
+ e.dataTransfer.setDragImage(dragContext.dragImagePreview, 0, 0);
13
+ }
14
+ }, [dragContext, item.dragProps]);
15
+ const onDragEnd = react_1.default.useCallback((e) => {
16
+ if (dragContext) {
17
+ dragContext.onDragEnd(e);
18
+ }
19
+ }, [dragContext]);
20
+ if (dragContext && item.dragProps) {
21
+ return {
22
+ draggable: true,
23
+ unselectable: 'on',
24
+ onDragStart,
25
+ onDragEnd,
26
+ };
27
+ }
28
+ return null;
29
+ };
30
+ exports.useDnDItemProps = useDnDItemProps;
@@ -1,3 +1,4 @@
1
1
  export declare const CURRENT_VERSION = 2;
2
2
  export declare const META_KEY = "__meta__";
3
3
  export declare const ACTION_PARAM_PREFIX = "_ap_";
4
+ export declare const TERMORARY_ITEM_ID = "__dropping-elem__";
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ACTION_PARAM_PREFIX = exports.META_KEY = exports.CURRENT_VERSION = void 0;
3
+ exports.TERMORARY_ITEM_ID = exports.ACTION_PARAM_PREFIX = exports.META_KEY = exports.CURRENT_VERSION = void 0;
4
4
  exports.CURRENT_VERSION = 2;
5
5
  exports.META_KEY = '__meta__';
6
6
  exports.ACTION_PARAM_PREFIX = '_ap_';
7
+ exports.TERMORARY_ITEM_ID = '__dropping-elem__';
@@ -1,5 +1,20 @@
1
+ import { ConfigLayout } from './config';
1
2
  export type Dictionary<T = unknown> = Record<string, T>;
2
3
  export interface StringParams extends Dictionary<string | string[]> {
3
4
  }
4
5
  export interface GlobalParams extends StringParams {
5
6
  }
7
+ export type ItemDragProps = {
8
+ type: string;
9
+ layout?: {
10
+ w?: number;
11
+ h?: number;
12
+ };
13
+ extra?: any;
14
+ };
15
+ export type ItemDropProps = {
16
+ commit: () => void;
17
+ dragProps: ItemDragProps;
18
+ itemLayout: ConfigLayout;
19
+ newLayout: ConfigLayout[];
20
+ };
@@ -8,3 +8,6 @@ export type SetConfigItem = ConfigItem | AddConfigItem;
8
8
  export type SetItemOptions = {
9
9
  excludeIds?: string[];
10
10
  };
11
+ export type SetNewItemOptions = SetItemOptions & {
12
+ updateLayout?: ConfigLayout[];
13
+ };
@@ -36,4 +36,5 @@ export interface PluginDefaultLayout {
36
36
  export interface Plugin<P extends PluginWidgetProps<T> = any, T = StringParams> extends PluginBase {
37
37
  defaultLayout?: PluginDefaultLayout;
38
38
  renderer: (props: P, forwardedRef: React.RefObject<any>) => React.ReactNode;
39
+ placeholderRenderer?: (props: P, forwardedRef: React.RefObject<any>) => React.ReactNode;
39
40
  }
@@ -1,5 +1,5 @@
1
1
  import type { Config, ConfigItem, ItemStateAndParams, ItemStateAndParamsChangeOptions, ItemsStateAndParams } from '../shared';
2
- import type { AddConfigItem, SetItemOptions, WidgetLayout } from '../typings';
2
+ import type { AddConfigItem, SetItemOptions, SetNewItemOptions, WidgetLayout } from '../typings';
3
3
  import { RegisterManagerPluginLayout } from './register-manager';
4
4
  interface RemoveItemArg {
5
5
  id: string;
@@ -19,7 +19,7 @@ export declare class UpdateManager {
19
19
  namespace: string;
20
20
  layout: RegisterManagerPluginLayout;
21
21
  config: Config;
22
- options: SetItemOptions;
22
+ options: SetNewItemOptions;
23
23
  }): Config;
24
24
  static editItem({ item, namespace, config, options, }: {
25
25
  item: ConfigItem;
@@ -216,19 +216,37 @@ function changeGroupParams({ groupItemIds, initiatorId, initiatorItem, itemsStat
216
216
  }
217
217
  class UpdateManager {
218
218
  static addItem({ item, namespace = constants_1.DEFAULT_NAMESPACE, layout, config, options, }) {
219
- const layoutY = Math.max(0, ...config.layout.map(({ h, y }) => h + y));
220
- const saveDefaultLayout = (0, pick_1.default)(layout, ['h', 'w', 'x', 'y']);
221
219
  const salt = config.salt;
222
220
  const newItemData = getNewItemData({ item, config, salt, counter: config.counter, options });
223
221
  let counter = newItemData.counter;
224
222
  const newIdData = (0, get_new_id_1.getNewId)({ config, salt, counter, excludeIds: newItemData.excludeIds });
225
223
  counter = newIdData.counter;
226
224
  const newItem = Object.assign(Object.assign({}, item), { id: newIdData.id, data: newItemData.data, namespace });
227
- return (0, immutability_helper_1.default)(config, {
228
- items: { $push: [newItem] },
229
- layout: { $push: [Object.assign(Object.assign({}, saveDefaultLayout), { y: layoutY, i: newItem.id })] },
230
- counter: { $set: counter },
231
- });
225
+ const saveDefaultLayout = (0, pick_1.default)(layout, ['h', 'w', 'x', 'y']);
226
+ if (options.updateLayout) {
227
+ const byId = options.updateLayout.reduce((memo, t) => {
228
+ memo[t.i] = t;
229
+ return memo;
230
+ }, {});
231
+ const newLayout = [
232
+ ...config.layout.map((t) => (Object.assign(Object.assign({}, t), (byId[t.i] || {})))),
233
+ Object.assign(Object.assign({}, saveDefaultLayout), { i: newItem.id }),
234
+ ];
235
+ return (0, immutability_helper_1.default)(config, {
236
+ items: { $push: [newItem] },
237
+ layout: { $set: newLayout },
238
+ counter: { $set: counter },
239
+ });
240
+ }
241
+ else {
242
+ const layoutY = Math.max(0, ...config.layout.map(({ h, y }) => h + y));
243
+ const newLayoutItem = Object.assign(Object.assign({}, saveDefaultLayout), { y: layoutY, i: newItem.id });
244
+ return (0, immutability_helper_1.default)(config, {
245
+ items: { $push: [newItem] },
246
+ layout: { $push: [newLayoutItem] },
247
+ counter: { $set: counter },
248
+ });
249
+ }
232
250
  }
233
251
  static editItem({ item, namespace = constants_1.DEFAULT_NAMESPACE, config, options, }) {
234
252
  const itemIndex = config.items.findIndex(({ id }) => item.id === id);
@@ -1,4 +1,7 @@
1
1
  /// <reference types="react" />
2
- import { ActionPanelProps } from './types';
2
+ import { ActionPanelItem, ActionPanelProps } from './types';
3
3
  import './ActionPanel.css';
4
+ export declare const ActionPanelItemContainer: ({ item }: {
5
+ item: ActionPanelItem;
6
+ }) => JSX.Element;
4
7
  export declare const ActionPanel: (props: ActionPanelProps) => JSX.Element | null;
@@ -1,18 +1,21 @@
1
1
  import React from 'react';
2
2
  import { CSSTransition } from 'react-transition-group';
3
+ import { useDnDItemProps } from '../../hooks/useDnDItemProps';
3
4
  import { cn } from '../../utils/cn';
4
5
  import './ActionPanel.css';
5
6
  const b = cn('dashkit-action-panel');
7
+ export const ActionPanelItemContainer = ({ item }) => {
8
+ const dndProps = useDnDItemProps(item);
9
+ return (React.createElement("div", Object.assign({ role: "button", className: b('item', { draggable: Boolean(dndProps) }, item.className), onClick: item.onClick, "data-qa": item.qa }, dndProps),
10
+ React.createElement("div", { className: b('icon') }, item.icon),
11
+ React.createElement("div", { className: b('title'), title: item.title }, item.title)));
12
+ };
6
13
  export const ActionPanel = (props) => {
7
14
  var _a, _b;
8
15
  const isDisabled = (_a = props.disable) !== null && _a !== void 0 ? _a : false;
9
16
  const isAnimated = (_b = props.toggleAnimation) !== null && _b !== void 0 ? _b : false;
10
17
  const nodeRef = React.useRef(null);
11
- const content = (React.createElement("div", { ref: nodeRef, className: b(null, props.className) }, props.items.map((item) => {
12
- return (React.createElement("div", { role: "button", className: b('item', item.className), key: `dk-action-panel-${item.id}`, onClick: item.onClick, "data-qa": item.qa },
13
- React.createElement("div", { className: b('icon') }, item.icon),
14
- React.createElement("div", { className: b('title'), title: item.title }, item.title)));
15
- })));
18
+ const content = (React.createElement("div", { ref: nodeRef, className: b(null, props.className) }, props.items.map((item) => (React.createElement(ActionPanelItemContainer, { key: `dk-action-panel-${item.id}`, item: item })))));
16
19
  if (isAnimated) {
17
20
  return (React.createElement(CSSTransition, { in: !isDisabled, nodeRef: nodeRef, classNames: b(null), timeout: 300, unmountOnExit: true }, content));
18
21
  }
@@ -1,11 +1,13 @@
1
1
  import React from 'react';
2
+ import type { ItemDragProps } from '../../shared';
2
3
  export type ActionPanelItem = {
3
4
  id: string;
4
5
  icon: React.ReactNode;
5
6
  title: string;
6
- onClick?: () => void;
7
7
  className?: string;
8
8
  qa?: string;
9
+ onClick?: () => void;
10
+ dragProps?: ItemDragProps;
9
11
  };
10
12
  export type ActionPanelProps = {
11
13
  items: ActionPanelItem[];
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import type { Config, ConfigItem, GlobalParams, ItemsStateAndParams } from '../../shared';
3
- import { ContextProps, Plugin, SetConfigItem, SetItemOptions, Settings, SettingsProps } from '../../typings';
2
+ import type { Config, ConfigItem, GlobalParams, ItemDropProps, ItemsStateAndParams } from '../../shared';
3
+ import { ContextProps, 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 {
@@ -15,6 +15,7 @@ interface DashKitDefaultProps {
15
15
  config: Config;
16
16
  itemsStateAndParams: ItemsStateAndParams;
17
17
  }) => void;
18
+ onDrop: (dropProps: ItemDropProps) => void;
18
19
  defaultGlobalParams: GlobalParams;
19
20
  globalParams: GlobalParams;
20
21
  itemsStateAndParams: ItemsStateAndParams;
@@ -34,7 +35,7 @@ export declare class DashKit extends React.PureComponent<DashKitInnerProps> {
34
35
  item: SetConfigItem;
35
36
  namespace?: string;
36
37
  config: Config;
37
- options?: SetItemOptions;
38
+ options?: SetNewItemOptions;
38
39
  }): Config;
39
40
  static removeItem({ id, config, itemsStateAndParams, }: {
40
41
  id: string;
@@ -54,6 +54,7 @@ export class DashKit extends React.PureComponent {
54
54
  DashKit.defaultProps = {
55
55
  onItemEdit: noop,
56
56
  onChange: noop,
57
+ onDrop: noop,
57
58
  defaultGlobalParams: {},
58
59
  globalParams: {},
59
60
  itemsStateAndParams: {},
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import type { ItemDragProps } from '../../shared';
3
+ type DashKitDnDWrapperProps = {
4
+ dragImageSrc?: string;
5
+ onDragStart?: (dragProps: ItemDragProps) => void;
6
+ onDragEnd?: () => void;
7
+ children: React.ReactElement;
8
+ };
9
+ export declare const DashKitDnDWrapper: React.FC<DashKitDnDWrapperProps>;
10
+ export {};
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import { DashKitDnDContext } from '../../context/DashKitContext';
3
+ const defaultImageSrc = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
4
+ export const DashKitDnDWrapper = (props) => {
5
+ const [dragProps, setDragProps] = React.useState(null);
6
+ const dragImagePreview = React.useMemo(() => {
7
+ const img = new Image();
8
+ img.src = props.dragImageSrc || defaultImageSrc;
9
+ return img;
10
+ }, [props.dragImageSrc]);
11
+ const onDragStart = React.useCallback((_, itemDragProps) => {
12
+ var _a;
13
+ setDragProps(itemDragProps);
14
+ (_a = props.onDragStart) === null || _a === void 0 ? void 0 : _a.call(props, itemDragProps);
15
+ }, [setDragProps, props.onDragStart]);
16
+ const onDragEnd = React.useCallback((_) => {
17
+ var _a;
18
+ setDragProps(null);
19
+ (_a = props.onDragEnd) === null || _a === void 0 ? void 0 : _a.call(props);
20
+ }, [setDragProps, props.onDragEnd]);
21
+ const contextValue = React.useMemo(() => {
22
+ return {
23
+ dragProps,
24
+ dragImagePreview,
25
+ onDragStart,
26
+ onDragEnd,
27
+ };
28
+ }, [dragProps, dragImagePreview, onDragStart, onDragEnd]);
29
+ return (React.createElement(DashKitDnDContext.Provider, { value: contextValue }, props.children));
30
+ };
@@ -1,3 +1,3 @@
1
- declare const _default: React.ForwardRefExoticComponent<React.RefAttributes<any>>;
2
- export default _default;
1
+ export default GridItemForwarderRef;
2
+ declare const GridItemForwarderRef: React.ForwardRefExoticComponent<React.RefAttributes<any>>;
3
3
  import React from 'react';
@@ -56,9 +56,9 @@ class GridItem extends React.PureComponent {
56
56
  };
57
57
  }
58
58
  renderOverlay() {
59
- const { overlayControls } = this.props;
59
+ const { overlayControls, isPlaceholder } = this.props;
60
60
  const { editMode } = this.context;
61
- if (!editMode || this.props.item.data._editActive) {
61
+ if (!editMode || this.props.item.data._editActive || isPlaceholder) {
62
62
  return null;
63
63
  }
64
64
  const { item } = this.props;
@@ -70,7 +70,7 @@ class GridItem extends React.PureComponent {
70
70
  render() {
71
71
  // из-за бага, что Grid Items unmounts при изменении static, isDraggable, isResaizable
72
72
  // https://github.com/STRML/react-grid-layout/issues/721
73
- const { style, onMouseDown, onMouseUp, onTouchEnd, onTouchStart, children, className, isDragging, noOverlay, focusable, withCustomHandle, } = this.props;
73
+ const { style, onMouseDown, onMouseUp, onTouchEnd, onTouchStart, children, className, isDragging, noOverlay, focusable, withCustomHandle, isPlaceholder, } = this.props;
74
74
  const { editMode } = this.context;
75
75
  const width = Number.parseInt(style.width, 10);
76
76
  const height = Number.parseInt(style.height, 10);
@@ -95,7 +95,7 @@ class GridItem extends React.PureComponent {
95
95
  }
96
96
  : {})),
97
97
  React.createElement("div", { className: b('item', { editMode: editMode && !_editActive && !noOverlay }) },
98
- React.createElement(Item, { id: this.props.id, item: this.props.item, shouldItemUpdate: !isDragging, width: width, height: height, transform: transform, adjustWidgetLayout: this.props.adjustWidgetLayout, layout: this.props.layout, forwardedPluginRef: this.props.forwardedPluginRef })),
98
+ React.createElement(Item, { id: this.props.id, item: this.props.item, shouldItemUpdate: !isDragging, width: width, height: height, transform: transform, isPlaceholder: isPlaceholder, adjustWidgetLayout: this.props.adjustWidgetLayout, layout: this.props.layout, forwardedPluginRef: this.props.forwardedPluginRef })),
99
99
  !noOverlay && this.renderOverlay(),
100
100
  preparedChildren));
101
101
  }
@@ -109,6 +109,7 @@ GridItem.propTypes = {
109
109
  overlayControls: PropTypes.object,
110
110
  forwardedRef: PropTypes.any,
111
111
  forwardedPluginRef: PropTypes.any,
112
+ isPlaceholder: PropTypes.bool,
112
113
  // from react-grid-layout:
113
114
  children: PropTypes.node,
114
115
  className: PropTypes.string,
@@ -122,6 +123,8 @@ GridItem.propTypes = {
122
123
  onTouchStart: PropTypes.func,
123
124
  };
124
125
  GridItem.contextType = DashKitContext;
125
- export default React.forwardRef((props, ref) => {
126
+ const GridItemForwarderRef = React.forwardRef((props, ref) => {
126
127
  return React.createElement(GridItem, Object.assign({}, props, { forwardedRef: ref }));
127
128
  });
129
+ GridItemForwarderRef.displayName = 'forwardRef(GridItem)';
130
+ export default GridItemForwarderRef;
@@ -21,6 +21,9 @@ export default class GridLayout extends React.PureComponent<any, any, any> {
21
21
  reloadItems(): void;
22
22
  _onStart: () => void;
23
23
  _onStop: (newLayout: any) => void;
24
+ _onDropDragOver: (e: any) => any;
25
+ _onDrop: (layout: any, item: any, e: any) => false | undefined;
26
+ renderTemporaryPlaceholder(): JSX.Element | null;
24
27
  render(): JSX.Element;
25
28
  }
26
29
  import React from 'react';
@@ -1,9 +1,8 @@
1
1
  import React from 'react';
2
- import ReactGridLayout, { WidthProvider } from 'react-grid-layout';
3
- import { OVERLAY_CONTROLS_CLASS_NAME } from '../../constants';
2
+ import { OVERLAY_CONTROLS_CLASS_NAME, TEMPORARY_ITEM_ID } from '../../constants';
4
3
  import { DashKitContext } from '../../context/DashKitContext';
5
4
  import GridItem from '../GridItem/GridItem';
6
- const Layout = WidthProvider(ReactGridLayout); // eslint-disable-line new-cap
5
+ import { Layout } from './ReactGridLayout';
7
6
  class GridLayout extends React.PureComponent {
8
7
  constructor(props, context) {
9
8
  super(props, context);
@@ -29,13 +28,37 @@ class GridLayout extends React.PureComponent {
29
28
  return getItemsMeta(this.pluginsRefs);
30
29
  };
31
30
  this._onStart = () => {
31
+ if (this.temporaryLayout)
32
+ return;
32
33
  this.setState({ isDragging: true });
33
34
  };
34
35
  this._onStop = (newLayout) => {
35
- const { layoutChange } = this.context;
36
- layoutChange(newLayout);
36
+ const { layoutChange, onDrop, temporaryLayout } = this.context;
37
+ if (temporaryLayout) {
38
+ onDrop === null || onDrop === void 0 ? void 0 : onDrop(newLayout, newLayout.find(({ i }) => i === TEMPORARY_ITEM_ID));
39
+ }
40
+ else {
41
+ layoutChange(newLayout);
42
+ }
37
43
  this.setState({ isDragging: false });
38
44
  };
45
+ this._onDropDragOver = (e) => {
46
+ const { editMode, dragOverPlugin, onDropDragOver } = this.context;
47
+ if (!editMode || !dragOverPlugin) {
48
+ return false;
49
+ }
50
+ return onDropDragOver(e);
51
+ };
52
+ this._onDrop = (layout, item, e) => {
53
+ if (!item) {
54
+ return false;
55
+ }
56
+ const { editMode, onDrop } = this.context;
57
+ if (!editMode) {
58
+ return false;
59
+ }
60
+ onDrop === null || onDrop === void 0 ? void 0 : onDrop(layout, item, e);
61
+ };
39
62
  this.pluginsRefs = [];
40
63
  this.state = {
41
64
  isDragging: false,
@@ -68,16 +91,33 @@ class GridLayout extends React.PureComponent {
68
91
  this._timeout = setTimeout(() => this.reloadItems(), reloadIntervalRemains <= 0 ? autoupdateIntervalMs : reloadIntervalRemains);
69
92
  }
70
93
  }
94
+ renderTemporaryPlaceholder() {
95
+ const { temporaryLayout, noOverlay, draggableHandleClassName } = this.context;
96
+ if (!temporaryLayout || !temporaryLayout.dragProps) {
97
+ return null;
98
+ }
99
+ const id = TEMPORARY_ITEM_ID;
100
+ const { type } = temporaryLayout.dragProps;
101
+ 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
+ }
71
103
  render() {
72
- const { layout, config, registerManager, editMode, noOverlay, focusable, draggableHandleClassName, } = this.context;
104
+ const { layout, temporaryLayout, config, registerManager, editMode, noOverlay, focusable, draggableHandleClassName, outerDnDEnable, } = this.context;
73
105
  this.pluginsRefs.length = config.items.length;
74
- return (React.createElement(Layout, Object.assign({}, registerManager.gridLayout, { layout: layout, isDraggable: editMode, isResizable: editMode, onDragStart: this._onStart, onDragStop: this._onStop, onResizeStart: this._onStart, onResizeStop: this._onStop }, (draggableHandleClassName
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
75
107
  ? { draggableHandle: `.${draggableHandleClassName}` }
76
- : null), { draggableCancel: `.${OVERLAY_CONTROLS_CLASS_NAME}` }), config.items.map((item, i) => {
77
- return (React.createElement(GridItem, { forwardedPluginRef: (pluginRef) => {
78
- this.pluginsRefs[i] = pluginRef;
79
- }, key: item.id, id: item.id, item: item, layout: layout, adjustWidgetLayout: this.adjustWidgetLayout, isDragging: this.state.isDragging, noOverlay: noOverlay, focusable: focusable, withCustomHandle: Boolean(draggableHandleClassName), overlayControls: this.props.overlayControls }));
80
- })));
108
+ : null), (outerDnDEnable
109
+ ? {
110
+ isDroppable: true,
111
+ onDropDragOver: this._onDropDragOver,
112
+ onDrop: this._onDrop,
113
+ }
114
+ : null), { draggableCancel: `.${OVERLAY_CONTROLS_CLASS_NAME}` }),
115
+ config.items.map((item, i) => {
116
+ 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 }));
119
+ }),
120
+ this.renderTemporaryPlaceholder()));
81
121
  }
82
122
  }
83
123
  GridLayout.contextType = DashKitContext;
@@ -0,0 +1,3 @@
1
+ export const Layout: React.ComponentClass<ReactGridLayout.ReactGridLayoutProps & ReactGridLayout.WidthProviderProps, any>;
2
+ import ReactGridLayout from 'react-grid-layout';
3
+ import React from 'react';
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+ import ReactGridLayout, { WidthProvider } from 'react-grid-layout';
3
+ import { OVERLAY_CLASS_NAME } from '../../constants';
4
+ class DragOverLayout extends ReactGridLayout {
5
+ constructor() {
6
+ super(...arguments);
7
+ this.dragReset = () => {
8
+ if (this.dragEnterCounter) {
9
+ this.dragEnterCounter = 0;
10
+ this.removeDroppingPlaceholder();
11
+ }
12
+ };
13
+ }
14
+ componentDidMount() {
15
+ var _a;
16
+ (_a = super.componentDidMount) === null || _a === void 0 ? void 0 : _a.call(this);
17
+ window.addEventListener('dragend', this.dragReset);
18
+ }
19
+ componentWillUnmount() {
20
+ window.removeEventListener('dragend', this.dragReset);
21
+ }
22
+ processGridItem(child, isDroppingItem) {
23
+ var _a;
24
+ const gridItem = (_a = super.processGridItem) === null || _a === void 0 ? void 0 : _a.call(this, child, isDroppingItem);
25
+ if (isDroppingItem) {
26
+ // Drop item from outside gets 0,0 droppingPosition
27
+ // centering cursor on newly creted grid item
28
+ // And cause grid-layout using it's own GridItem to make it look
29
+ // like overlay adding className
30
+ if (!gridItem)
31
+ return null;
32
+ const { props } = gridItem;
33
+ const { containerWidth, cols, w, h, rowHeight, margin, transformScale } = props;
34
+ const leftOffset = (((containerWidth / cols) * w) / 2 || 0) * transformScale;
35
+ const topOffset = ((h * rowHeight + (h - 1) * margin[1]) / 2 || 0) * transformScale;
36
+ return React.cloneElement(gridItem, {
37
+ className: OVERLAY_CLASS_NAME,
38
+ droppingPosition: Object.assign(Object.assign({}, props.droppingPosition), { left: props.droppingPosition.left - leftOffset, top: props.droppingPosition.top - topOffset }),
39
+ });
40
+ }
41
+ return gridItem;
42
+ }
43
+ }
44
+ // eslint-disable-next-line new-cap
45
+ export const Layout = WidthProvider(DragOverLayout);