@gravity-ui/dashkit 8.0.2-beta.3 → 8.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/components/ActionPanel/ActionPanel.d.ts +13 -1
- package/build/cjs/components/ActionPanel/ActionPanel.js +25 -6
- package/build/cjs/components/DashKit/DashKit.d.ts +9 -3
- package/build/cjs/components/DashKit/DashKit.js +1 -0
- package/build/cjs/components/DashKitDnDWrapper/DashKitDnDWrapper.d.ts +6 -0
- package/build/cjs/components/DashKitDnDWrapper/DashKitDnDWrapper.js +24 -0
- package/build/cjs/components/GridItem/GridItem.d.ts +2 -2
- package/build/cjs/components/GridItem/GridItem.js +3 -1
- package/build/cjs/components/GridLayout/GridLayout.d.ts +2 -0
- package/build/cjs/components/GridLayout/GridLayout.js +75 -6
- package/build/cjs/components/Item/Item.css +1 -1
- package/build/cjs/components/index.d.ts +1 -0
- package/build/cjs/components/index.js +1 -0
- package/build/cjs/constants/common.d.ts +2 -0
- package/build/cjs/constants/common.js +3 -1
- package/build/cjs/context/DashKitContext.d.ts +1 -0
- package/build/cjs/context/DashKitContext.js +3 -1
- package/build/cjs/hocs/withContext.js +85 -6
- package/build/cjs/plugins/Text/Text.js +3 -0
- package/build/cjs/shared/constants/common.d.ts +1 -0
- package/build/cjs/shared/constants/common.js +2 -1
- package/build/cjs/typings/config.d.ts +3 -0
- package/build/cjs/typings/plugin.d.ts +1 -0
- package/build/cjs/utils/update-manager.d.ts +2 -2
- package/build/cjs/utils/update-manager.js +25 -7
- package/build/esm/components/ActionPanel/ActionPanel.d.ts +13 -1
- package/build/esm/components/ActionPanel/ActionPanel.js +23 -5
- package/build/esm/components/DashKit/DashKit.d.ts +9 -3
- package/build/esm/components/DashKit/DashKit.js +1 -0
- package/build/esm/components/DashKitDnDWrapper/DashKitDnDWrapper.d.ts +6 -0
- package/build/esm/components/DashKitDnDWrapper/DashKitDnDWrapper.js +19 -0
- package/build/esm/components/GridItem/GridItem.d.ts +2 -2
- package/build/esm/components/GridItem/GridItem.js +3 -1
- package/build/esm/components/GridLayout/GridLayout.d.ts +2 -0
- package/build/esm/components/GridLayout/GridLayout.js +76 -7
- package/build/esm/components/Item/Item.css +1 -1
- package/build/esm/components/index.d.ts +1 -0
- package/build/esm/components/index.js +1 -0
- package/build/esm/constants/common.d.ts +2 -0
- package/build/esm/constants/common.js +2 -0
- package/build/esm/context/DashKitContext.d.ts +1 -0
- package/build/esm/context/DashKitContext.js +2 -1
- package/build/esm/hocs/withContext.js +86 -7
- package/build/esm/plugins/Text/Text.js +3 -0
- package/build/esm/shared/constants/common.d.ts +1 -0
- package/build/esm/shared/constants/common.js +1 -0
- package/build/esm/typings/config.d.ts +3 -0
- package/build/esm/typings/plugin.d.ts +1 -0
- package/build/esm/utils/update-manager.d.ts +2 -2
- package/build/esm/utils/update-manager.js +25 -7
- package/package.json +1 -1
|
@@ -1,3 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import { ActionPanelProps } from './types';
|
|
3
|
+
export type ActionPanelItem = {
|
|
4
|
+
id: string;
|
|
5
|
+
icon: React.ReactNode;
|
|
6
|
+
title: string;
|
|
7
|
+
onClick?: () => void;
|
|
8
|
+
className?: string;
|
|
9
|
+
qa?: string;
|
|
10
|
+
pluginType?: string;
|
|
11
|
+
};
|
|
12
|
+
export declare const ActionPanelItemContainer: ({ item }: {
|
|
13
|
+
item: ActionPanelItem;
|
|
14
|
+
}) => JSX.Element;
|
|
3
15
|
export declare const ActionPanel: (props: ActionPanelProps) => JSX.Element | null;
|
|
@@ -1,21 +1,40 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ActionPanel = void 0;
|
|
3
|
+
exports.ActionPanel = exports.ActionPanelItemContainer = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const react_1 = tslib_1.__importDefault(require("react"));
|
|
6
6
|
const react_transition_group_1 = require("react-transition-group");
|
|
7
|
+
const DashKitContext_1 = require("../../context/DashKitContext");
|
|
7
8
|
const cn_1 = require("../../utils/cn");
|
|
8
9
|
const b = (0, cn_1.cn)('dashkit-action-panel');
|
|
10
|
+
const ActionPanelItemContainer = ({ item }) => {
|
|
11
|
+
const dragContext = react_1.default.useContext(DashKitContext_1.DashKitDnDContext);
|
|
12
|
+
const onDragStart = react_1.default.useCallback((e) => {
|
|
13
|
+
dragContext.onDragStart(e, item.pluginType);
|
|
14
|
+
}, [dragContext === null || dragContext === void 0 ? void 0 : dragContext.onDragStart, item.pluginType]);
|
|
15
|
+
const onDragEnd = react_1.default.useCallback((e) => {
|
|
16
|
+
dragContext.onDragEnd(e);
|
|
17
|
+
}, [dragContext === null || dragContext === void 0 ? void 0 : dragContext.onDragEnd]);
|
|
18
|
+
let dndProps = {};
|
|
19
|
+
if (item.pluginType) {
|
|
20
|
+
dndProps = {
|
|
21
|
+
draggable: true,
|
|
22
|
+
unselectable: 'on',
|
|
23
|
+
onDragStart,
|
|
24
|
+
onDragEnd,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return (react_1.default.createElement("div", Object.assign({ role: "button", className: b('item', item.className), key: `dk-action-panel-${item.id}`, onClick: item.onClick, "data-qa": item.qa }, dndProps),
|
|
28
|
+
react_1.default.createElement("div", { className: b('icon') }, item.icon),
|
|
29
|
+
react_1.default.createElement("div", { className: b('title'), title: item.title }, item.title)));
|
|
30
|
+
};
|
|
31
|
+
exports.ActionPanelItemContainer = ActionPanelItemContainer;
|
|
9
32
|
const ActionPanel = (props) => {
|
|
10
33
|
var _a, _b;
|
|
11
34
|
const isDisabled = (_a = props.disable) !== null && _a !== void 0 ? _a : false;
|
|
12
35
|
const isAnimated = (_b = props.toggleAnimation) !== null && _b !== void 0 ? _b : false;
|
|
13
36
|
const nodeRef = react_1.default.useRef(null);
|
|
14
|
-
const content = (react_1.default.createElement("div", { ref: nodeRef, className: b(null, props.className) }, props.items.map((item) => {
|
|
15
|
-
return (react_1.default.createElement("div", { role: "button", className: b('item', item.className), key: `dk-action-panel-${item.id}`, onClick: item.onClick, "data-qa": item.qa },
|
|
16
|
-
react_1.default.createElement("div", { className: b('icon') }, item.icon),
|
|
17
|
-
react_1.default.createElement("div", { className: b('title'), title: item.title }, item.title)));
|
|
18
|
-
})));
|
|
37
|
+
const content = (react_1.default.createElement("div", { ref: nodeRef, className: b(null, props.className) }, props.items.map((item, i) => (react_1.default.createElement(exports.ActionPanelItemContainer, { key: `item_${i}`, item: item })))));
|
|
19
38
|
if (isAnimated) {
|
|
20
39
|
return (react_1.default.createElement(react_transition_group_1.CSSTransition, { in: !isDisabled, nodeRef: nodeRef, classNames: b(null), timeout: 300, unmountOnExit: true }, content));
|
|
21
40
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type { Config, ConfigItem, GlobalParams, ItemsStateAndParams } from '../../shared';
|
|
3
|
-
import { ContextProps, Plugin, SetConfigItem,
|
|
2
|
+
import type { Config, ConfigItem, ConfigLayout, GlobalParams, 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,12 @@ interface DashKitDefaultProps {
|
|
|
15
15
|
config: Config;
|
|
16
16
|
itemsStateAndParams: ItemsStateAndParams;
|
|
17
17
|
}) => void;
|
|
18
|
+
onDrop: (dropProps: {
|
|
19
|
+
commit: () => void;
|
|
20
|
+
pluginType: string;
|
|
21
|
+
itemLayout: ConfigLayout;
|
|
22
|
+
newLayout: ConfigLayout[];
|
|
23
|
+
}) => void;
|
|
18
24
|
defaultGlobalParams: GlobalParams;
|
|
19
25
|
globalParams: GlobalParams;
|
|
20
26
|
itemsStateAndParams: ItemsStateAndParams;
|
|
@@ -34,7 +40,7 @@ export declare class DashKit extends React.PureComponent<DashKitInnerProps> {
|
|
|
34
40
|
item: SetConfigItem;
|
|
35
41
|
namespace?: string;
|
|
36
42
|
config: Config;
|
|
37
|
-
options?:
|
|
43
|
+
options?: SetNewItemOptions;
|
|
38
44
|
}): Config;
|
|
39
45
|
static removeItem({ id, config, itemsStateAndParams, }: {
|
|
40
46
|
id: string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DashKitDnDWrapper = 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 DashKitDnDWrapper = (props) => {
|
|
8
|
+
const [dragPluginType, setDragPluginType] = react_1.default.useState(null);
|
|
9
|
+
const onDragStart = react_1.default.useCallback((_, type) => {
|
|
10
|
+
setDragPluginType(type);
|
|
11
|
+
}, [setDragPluginType]);
|
|
12
|
+
const onDragEnd = react_1.default.useCallback((_) => {
|
|
13
|
+
setDragPluginType(null);
|
|
14
|
+
}, [setDragPluginType]);
|
|
15
|
+
const contextValue = react_1.default.useMemo(() => {
|
|
16
|
+
return {
|
|
17
|
+
dragPluginType,
|
|
18
|
+
onDragStart,
|
|
19
|
+
onDragEnd,
|
|
20
|
+
};
|
|
21
|
+
}, [dragPluginType, onDragStart, onDragEnd]);
|
|
22
|
+
return (react_1.default.createElement(DashKitContext_1.DashKitDnDContext.Provider, { value: contextValue }, props.children));
|
|
23
|
+
};
|
|
24
|
+
exports.DashKitDnDWrapper = DashKitDnDWrapper;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export default GridItemForwarderRef;
|
|
2
|
+
declare const GridItemForwarderRef: React.ForwardRefExoticComponent<React.RefAttributes<any>>;
|
|
3
3
|
import React from 'react';
|
|
@@ -124,6 +124,8 @@ GridItem.propTypes = {
|
|
|
124
124
|
onTouchStart: prop_types_1.default.func,
|
|
125
125
|
};
|
|
126
126
|
GridItem.contextType = DashKitContext_1.DashKitContext;
|
|
127
|
-
|
|
127
|
+
const GridItemForwarderRef = react_1.default.forwardRef((props, ref) => {
|
|
128
128
|
return react_1.default.createElement(GridItem, Object.assign({}, props, { forwardedRef: ref }));
|
|
129
129
|
});
|
|
130
|
+
GridItemForwarderRef.displayName = 'forwardRef(GridItem)';
|
|
131
|
+
exports.default = GridItemForwarderRef;
|
|
@@ -21,6 +21,8 @@ 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;
|
|
24
26
|
render(): JSX.Element;
|
|
25
27
|
}
|
|
26
28
|
import React from 'react';
|
|
@@ -6,7 +6,45 @@ const react_grid_layout_1 = tslib_1.__importStar(require("react-grid-layout"));
|
|
|
6
6
|
const constants_1 = require("../../constants");
|
|
7
7
|
const DashKitContext_1 = require("../../context/DashKitContext");
|
|
8
8
|
const GridItem_1 = tslib_1.__importDefault(require("../GridItem/GridItem"));
|
|
9
|
-
|
|
9
|
+
class DragOverLayout extends react_grid_layout_1.default {
|
|
10
|
+
constructor() {
|
|
11
|
+
super(...arguments);
|
|
12
|
+
this.dragReset = () => {
|
|
13
|
+
if (this.dragEnterCounter) {
|
|
14
|
+
this.dragEnterCounter = 0;
|
|
15
|
+
this.removeDroppingPlaceholder();
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
componentDidMount() {
|
|
20
|
+
super.componentDidMount();
|
|
21
|
+
window.addEventListener('dragend', this.dragReset);
|
|
22
|
+
}
|
|
23
|
+
componentWillUnmount() {
|
|
24
|
+
window.removeEventListener('dragend', this.dragReset);
|
|
25
|
+
}
|
|
26
|
+
processGridItem(child, isDroppingItem) {
|
|
27
|
+
if (isDroppingItem) {
|
|
28
|
+
// Drop item from outside gets 0,0 droppingPosition
|
|
29
|
+
// centering cursor on newly creted grid item
|
|
30
|
+
// And cause grid-layout using it's own GridItem to make it look
|
|
31
|
+
// like overlay adding className
|
|
32
|
+
const gridItem = super.processGridItem(child, isDroppingItem);
|
|
33
|
+
if (!gridItem)
|
|
34
|
+
return null;
|
|
35
|
+
const { props } = gridItem;
|
|
36
|
+
const { containerWidth, cols, w, h, rowHeight, margin, transformScale } = props;
|
|
37
|
+
const leftOffset = (((containerWidth / cols) * w) / 2 || 0) * transformScale;
|
|
38
|
+
const topOffset = ((h * rowHeight + (h - 1) * margin[1]) / 2 || 0) * transformScale;
|
|
39
|
+
return react_1.default.cloneElement(gridItem, {
|
|
40
|
+
className: constants_1.OVERLAY_CLASS_NAME,
|
|
41
|
+
droppingPosition: Object.assign(Object.assign({}, props.droppingPosition), { left: props.droppingPosition.left - leftOffset, top: props.droppingPosition.top - topOffset }),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return super.processGridItem(child, isDroppingItem);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const Layout = (0, react_grid_layout_1.WidthProvider)(DragOverLayout); // eslint-disable-line new-cap
|
|
10
48
|
class GridLayout extends react_1.default.PureComponent {
|
|
11
49
|
constructor(props, context) {
|
|
12
50
|
super(props, context);
|
|
@@ -32,13 +70,38 @@ class GridLayout extends react_1.default.PureComponent {
|
|
|
32
70
|
return getItemsMeta(this.pluginsRefs);
|
|
33
71
|
};
|
|
34
72
|
this._onStart = () => {
|
|
73
|
+
if (this.temporaryLayout)
|
|
74
|
+
return;
|
|
35
75
|
this.setState({ isDragging: true });
|
|
36
76
|
};
|
|
37
77
|
this._onStop = (newLayout) => {
|
|
38
|
-
const { layoutChange } = this.context;
|
|
39
|
-
|
|
78
|
+
const { layoutChange, onDrop, temporaryLayout } = this.context;
|
|
79
|
+
if (temporaryLayout) {
|
|
80
|
+
onDrop === null || onDrop === void 0 ? void 0 : onDrop(newLayout, newLayout.find(({ i }) => i === constants_1.TEMPORARY_ITEM_ID));
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
layoutChange(newLayout);
|
|
84
|
+
}
|
|
40
85
|
this.setState({ isDragging: false });
|
|
41
86
|
};
|
|
87
|
+
this._onDropDragOver = (e) => {
|
|
88
|
+
const { editMode, onDropDragOver, temporaryLayout } = this.context;
|
|
89
|
+
if (!editMode) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
const result = onDropDragOver(e);
|
|
93
|
+
if (temporaryLayout) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
};
|
|
98
|
+
this._onDrop = (layout, item, e) => {
|
|
99
|
+
const { editMode, temporaryLayout, onDrop } = this.context;
|
|
100
|
+
if (!editMode && temporaryLayout) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
onDrop === null || onDrop === void 0 ? void 0 : onDrop(layout, item, e);
|
|
104
|
+
};
|
|
42
105
|
this.pluginsRefs = [];
|
|
43
106
|
this.state = {
|
|
44
107
|
isDragging: false,
|
|
@@ -72,14 +135,20 @@ class GridLayout extends react_1.default.PureComponent {
|
|
|
72
135
|
}
|
|
73
136
|
}
|
|
74
137
|
render() {
|
|
75
|
-
const { layout, config, registerManager, editMode, noOverlay, focusable, draggableHandleClassName, } = this.context;
|
|
138
|
+
const { layout, temporaryLayout, config, registerManager, editMode, noOverlay, focusable, draggableHandleClassName, outerDnDEnable, } = this.context;
|
|
76
139
|
this.pluginsRefs.length = config.items.length;
|
|
77
|
-
return (react_1.default.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
|
|
140
|
+
return (react_1.default.createElement(Layout, Object.assign({}, registerManager.gridLayout, { layout: temporaryLayout || layout, isDraggable: editMode, isResizable: editMode, onDragStart: this._onStart, onDragStop: this._onStop, onResizeStart: this._onStart, onResizeStop: this._onStop }, (draggableHandleClassName
|
|
78
141
|
? { draggableHandle: `.${draggableHandleClassName}` }
|
|
142
|
+
: null), (outerDnDEnable
|
|
143
|
+
? {
|
|
144
|
+
isDroppable: true,
|
|
145
|
+
onDropDragOver: this._onDropDragOver,
|
|
146
|
+
onDrop: this._onDrop,
|
|
147
|
+
}
|
|
79
148
|
: null), { draggableCancel: `.${constants_1.OVERLAY_CONTROLS_CLASS_NAME}` }), config.items.map((item, i) => {
|
|
80
149
|
return (react_1.default.createElement(GridItem_1.default, { forwardedPluginRef: (pluginRef) => {
|
|
81
150
|
this.pluginsRefs[i] = pluginRef;
|
|
82
|
-
}, 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 }));
|
|
151
|
+
}, key: item.id, id: item.id, item: item, layout: temporaryLayout || layout, adjustWidgetLayout: this.adjustWidgetLayout, isDragging: this.state.isDragging, noOverlay: noOverlay, focusable: focusable, withCustomHandle: Boolean(draggableHandleClassName), overlayControls: this.props.overlayControls }));
|
|
83
152
|
})));
|
|
84
153
|
}
|
|
85
154
|
}
|
|
@@ -2,3 +2,4 @@ export * from './DashKit';
|
|
|
2
2
|
export type { OverlayControlItem, PreparedCopyItemOptions } from './OverlayControls/OverlayControls';
|
|
3
3
|
export * from './ActionPanel/ActionPanel';
|
|
4
4
|
export type { ActionPanelItem, ActionPanelProps } from './ActionPanel/types';
|
|
5
|
+
export * from './DashKitDnDWrapper/DashKitDnDWrapper';
|
|
@@ -3,3 +3,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
tslib_1.__exportStar(require("./DashKit"), exports);
|
|
5
5
|
tslib_1.__exportStar(require("./ActionPanel/ActionPanel"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./DashKitDnDWrapper/DashKitDnDWrapper"), exports);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export declare const COPIED_WIDGET_STORE_KEY = "dashCopiedItem";
|
|
2
2
|
export declare const DEFAULT_NAMESPACE = "default";
|
|
3
3
|
export declare const OVERLAY_CONTROLS_CLASS_NAME = "dashkit-overlay-controls";
|
|
4
|
+
export declare const OVERLAY_CLASS_NAME = "dashkit-grid-item__overlay";
|
|
5
|
+
export declare const TEMPORARY_ITEM_ID = "__dropping-elem__";
|
|
4
6
|
export declare const MenuItems: {
|
|
5
7
|
readonly Copy: "copy";
|
|
6
8
|
readonly Delete: "delete";
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MenuItems = exports.OVERLAY_CONTROLS_CLASS_NAME = exports.DEFAULT_NAMESPACE = exports.COPIED_WIDGET_STORE_KEY = void 0;
|
|
3
|
+
exports.MenuItems = exports.TEMPORARY_ITEM_ID = exports.OVERLAY_CLASS_NAME = exports.OVERLAY_CONTROLS_CLASS_NAME = exports.DEFAULT_NAMESPACE = exports.COPIED_WIDGET_STORE_KEY = void 0;
|
|
4
4
|
exports.COPIED_WIDGET_STORE_KEY = 'dashCopiedItem';
|
|
5
5
|
exports.DEFAULT_NAMESPACE = 'default';
|
|
6
6
|
exports.OVERLAY_CONTROLS_CLASS_NAME = 'dashkit-overlay-controls';
|
|
7
|
+
exports.OVERLAY_CLASS_NAME = 'dashkit-grid-item__overlay';
|
|
8
|
+
exports.TEMPORARY_ITEM_ID = '__dropping-elem__';
|
|
7
9
|
exports.MenuItems = {
|
|
8
10
|
Copy: 'copy',
|
|
9
11
|
Delete: 'delete',
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DashKitContext = void 0;
|
|
3
|
+
exports.DashKitDnDContext = exports.DashKitContext = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const react_1 = tslib_1.__importDefault(require("react"));
|
|
6
6
|
const DashKitContext = react_1.default.createContext();
|
|
7
7
|
exports.DashKitContext = DashKitContext;
|
|
8
|
+
const DashKitDnDContext = react_1.default.createContext();
|
|
9
|
+
exports.DashKitDnDContext = DashKitDnDContext;
|
|
@@ -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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
if (id === common_1.TEMPORARY_ITEM_ID) {
|
|
54
|
+
resetTemporaryLayout();
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
if (temporaryLayout) {
|
|
58
|
+
setTemporaryLayout(temporaryLayout.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,58 @@ 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 draggedPluginRef = react_1.default.useRef(dndContext === null || dndContext === void 0 ? void 0 : dndContext.dragPluginType);
|
|
148
|
+
react_1.default.useEffect(() => {
|
|
149
|
+
if (!(dndContext === null || dndContext === void 0 ? void 0 : dndContext.dragPluginType) && !temporaryLayout) {
|
|
150
|
+
draggedPluginRef.current = null;
|
|
151
|
+
}
|
|
152
|
+
else if (dndContext === null || dndContext === void 0 ? void 0 : dndContext.dragPluginType) {
|
|
153
|
+
draggedPluginRef.current = dndContext.dragPluginType;
|
|
154
|
+
}
|
|
155
|
+
}, [draggedPluginRef, dndContext === null || dndContext === void 0 ? void 0 : dndContext.dragPluginType, temporaryLayout]);
|
|
156
|
+
const dndPluginType = draggedPluginRef.current || (dndContext === null || dndContext === void 0 ? void 0 : dndContext.dragPluginType);
|
|
157
|
+
const dragOverPlugin = react_1.default.useMemo(() => {
|
|
158
|
+
const pluginType = dndPluginType;
|
|
159
|
+
if (pluginType === null && temporaryLayout === null)
|
|
160
|
+
return null;
|
|
161
|
+
if (props.registerManager.check(pluginType)) {
|
|
162
|
+
return props.registerManager.getItem(pluginType);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
// eslint-disable-next-line no-console
|
|
166
|
+
console.error(`Uknown pluginType: ${pluginType}`);
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}, [dndPluginType, props.registerManager, temporaryLayout]);
|
|
170
|
+
const onDropDragOver = react_1.default.useCallback(() => {
|
|
171
|
+
if (temporaryLayout) {
|
|
172
|
+
resetTemporaryLayout();
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
if (dragOverPlugin === null || dragOverPlugin === void 0 ? void 0 : dragOverPlugin.defaultLayout) {
|
|
176
|
+
const { h = 3, w = 3 } = dragOverPlugin.defaultLayout;
|
|
177
|
+
return { h, w };
|
|
178
|
+
}
|
|
179
|
+
return false;
|
|
180
|
+
}, [resetTemporaryLayout, temporaryLayout, dragOverPlugin]);
|
|
181
|
+
const onDrop = react_1.default.useCallback((newLayout, item) => {
|
|
182
|
+
setTemporaryLayout(newLayout);
|
|
183
|
+
const { i, w, h, x, y } = item;
|
|
184
|
+
props.onDrop({
|
|
185
|
+
newLayout: newLayout.reduce((memo, l) => {
|
|
186
|
+
if (l.i !== i) {
|
|
187
|
+
memo.push({ i: l.i, w: l.w, h: l.h, x: l.x, y: l.y });
|
|
188
|
+
}
|
|
189
|
+
return memo;
|
|
190
|
+
}, []),
|
|
191
|
+
itemLayout: { w, h, x, y },
|
|
192
|
+
pluginType: dndPluginType,
|
|
193
|
+
commit: resetTemporaryLayout,
|
|
194
|
+
});
|
|
195
|
+
}, [dndPluginType, props.onDrop]);
|
|
127
196
|
return react_1.default.useMemo(() => ({
|
|
128
197
|
layout: resultLayout,
|
|
198
|
+
temporaryLayout,
|
|
129
199
|
config: props.config,
|
|
130
200
|
context: props.context,
|
|
131
201
|
noOverlay: props.noOverlay,
|
|
@@ -139,6 +209,8 @@ function useMemoStateContext(props) {
|
|
|
139
209
|
registerManager: props.registerManager,
|
|
140
210
|
onItemStateAndParamsChange,
|
|
141
211
|
removeItem: onItemRemove,
|
|
212
|
+
onDrop,
|
|
213
|
+
onDropDragOver,
|
|
142
214
|
editItem: props.onItemEdit,
|
|
143
215
|
layoutChange: onLayoutChange,
|
|
144
216
|
getItemsMeta,
|
|
@@ -147,8 +219,11 @@ function useMemoStateContext(props) {
|
|
|
147
219
|
revertToOriginalLayout,
|
|
148
220
|
forwardedMetaRef: props.forwardedMetaRef,
|
|
149
221
|
draggableHandleClassName: props.draggableHandleClassName,
|
|
222
|
+
outerDnDEnable,
|
|
223
|
+
dragOverPlugin,
|
|
150
224
|
}), [
|
|
151
225
|
resultLayout,
|
|
226
|
+
temporaryLayout,
|
|
152
227
|
props.config,
|
|
153
228
|
props.context,
|
|
154
229
|
props.noOverlay,
|
|
@@ -161,6 +236,8 @@ function useMemoStateContext(props) {
|
|
|
161
236
|
props.registerManager,
|
|
162
237
|
onItemStateAndParamsChange,
|
|
163
238
|
onItemRemove,
|
|
239
|
+
onDrop,
|
|
240
|
+
onDropDragOver,
|
|
164
241
|
props.onItemEdit,
|
|
165
242
|
onLayoutChange,
|
|
166
243
|
getItemsMeta,
|
|
@@ -169,6 +246,8 @@ function useMemoStateContext(props) {
|
|
|
169
246
|
revertToOriginalLayout,
|
|
170
247
|
props.forwardedMetaRef,
|
|
171
248
|
props.draggableHandleClassName,
|
|
249
|
+
outerDnDEnable,
|
|
250
|
+
dragOverPlugin,
|
|
172
251
|
]);
|
|
173
252
|
}
|
|
174
253
|
function withContext(Component) {
|
|
@@ -117,5 +117,8 @@ const plugin = {
|
|
|
117
117
|
renderer(props, forwardedRef) {
|
|
118
118
|
return react_1.default.createElement(PluginText, Object.assign({}, props, { apiHandler: plugin._apiHandler, ref: forwardedRef }));
|
|
119
119
|
},
|
|
120
|
+
placeholderRenderer(_, forwardedRef) {
|
|
121
|
+
return react_1.default.createElement("div", { ref: forwardedRef }, "Text placeholder");
|
|
122
|
+
},
|
|
120
123
|
};
|
|
121
124
|
exports.default = plugin;
|
|
@@ -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__';
|
|
@@ -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:
|
|
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
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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,16 @@
|
|
|
1
|
-
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import { ActionPanelProps } from './types';
|
|
3
3
|
import './ActionPanel.css';
|
|
4
|
+
export type ActionPanelItem = {
|
|
5
|
+
id: string;
|
|
6
|
+
icon: React.ReactNode;
|
|
7
|
+
title: string;
|
|
8
|
+
onClick?: () => void;
|
|
9
|
+
className?: string;
|
|
10
|
+
qa?: string;
|
|
11
|
+
pluginType?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare const ActionPanelItemContainer: ({ item }: {
|
|
14
|
+
item: ActionPanelItem;
|
|
15
|
+
}) => JSX.Element;
|
|
4
16
|
export declare const ActionPanel: (props: ActionPanelProps) => JSX.Element | null;
|
|
@@ -1,18 +1,36 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { CSSTransition } from 'react-transition-group';
|
|
3
|
+
import { DashKitDnDContext } from '../../context/DashKitContext';
|
|
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 dragContext = React.useContext(DashKitDnDContext);
|
|
9
|
+
const onDragStart = React.useCallback((e) => {
|
|
10
|
+
dragContext.onDragStart(e, item.pluginType);
|
|
11
|
+
}, [dragContext === null || dragContext === void 0 ? void 0 : dragContext.onDragStart, item.pluginType]);
|
|
12
|
+
const onDragEnd = React.useCallback((e) => {
|
|
13
|
+
dragContext.onDragEnd(e);
|
|
14
|
+
}, [dragContext === null || dragContext === void 0 ? void 0 : dragContext.onDragEnd]);
|
|
15
|
+
let dndProps = {};
|
|
16
|
+
if (item.pluginType) {
|
|
17
|
+
dndProps = {
|
|
18
|
+
draggable: true,
|
|
19
|
+
unselectable: 'on',
|
|
20
|
+
onDragStart,
|
|
21
|
+
onDragEnd,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return (React.createElement("div", Object.assign({ role: "button", className: b('item', item.className), key: `dk-action-panel-${item.id}`, onClick: item.onClick, "data-qa": item.qa }, dndProps),
|
|
25
|
+
React.createElement("div", { className: b('icon') }, item.icon),
|
|
26
|
+
React.createElement("div", { className: b('title'), title: item.title }, item.title)));
|
|
27
|
+
};
|
|
6
28
|
export const ActionPanel = (props) => {
|
|
7
29
|
var _a, _b;
|
|
8
30
|
const isDisabled = (_a = props.disable) !== null && _a !== void 0 ? _a : false;
|
|
9
31
|
const isAnimated = (_b = props.toggleAnimation) !== null && _b !== void 0 ? _b : false;
|
|
10
32
|
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
|
-
})));
|
|
33
|
+
const content = (React.createElement("div", { ref: nodeRef, className: b(null, props.className) }, props.items.map((item, i) => (React.createElement(ActionPanelItemContainer, { key: `item_${i}`, item: item })))));
|
|
16
34
|
if (isAnimated) {
|
|
17
35
|
return (React.createElement(CSSTransition, { in: !isDisabled, nodeRef: nodeRef, classNames: b(null), timeout: 300, unmountOnExit: true }, content));
|
|
18
36
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type { Config, ConfigItem, GlobalParams, ItemsStateAndParams } from '../../shared';
|
|
3
|
-
import { ContextProps, Plugin, SetConfigItem,
|
|
2
|
+
import type { Config, ConfigItem, ConfigLayout, GlobalParams, 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,12 @@ interface DashKitDefaultProps {
|
|
|
15
15
|
config: Config;
|
|
16
16
|
itemsStateAndParams: ItemsStateAndParams;
|
|
17
17
|
}) => void;
|
|
18
|
+
onDrop: (dropProps: {
|
|
19
|
+
commit: () => void;
|
|
20
|
+
pluginType: string;
|
|
21
|
+
itemLayout: ConfigLayout;
|
|
22
|
+
newLayout: ConfigLayout[];
|
|
23
|
+
}) => void;
|
|
18
24
|
defaultGlobalParams: GlobalParams;
|
|
19
25
|
globalParams: GlobalParams;
|
|
20
26
|
itemsStateAndParams: ItemsStateAndParams;
|
|
@@ -34,7 +40,7 @@ export declare class DashKit extends React.PureComponent<DashKitInnerProps> {
|
|
|
34
40
|
item: SetConfigItem;
|
|
35
41
|
namespace?: string;
|
|
36
42
|
config: Config;
|
|
37
|
-
options?:
|
|
43
|
+
options?: SetNewItemOptions;
|
|
38
44
|
}): Config;
|
|
39
45
|
static removeItem({ id, config, itemsStateAndParams, }: {
|
|
40
46
|
id: string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { DashKitDnDContext } from '../../context/DashKitContext';
|
|
3
|
+
export const DashKitDnDWrapper = (props) => {
|
|
4
|
+
const [dragPluginType, setDragPluginType] = React.useState(null);
|
|
5
|
+
const onDragStart = React.useCallback((_, type) => {
|
|
6
|
+
setDragPluginType(type);
|
|
7
|
+
}, [setDragPluginType]);
|
|
8
|
+
const onDragEnd = React.useCallback((_) => {
|
|
9
|
+
setDragPluginType(null);
|
|
10
|
+
}, [setDragPluginType]);
|
|
11
|
+
const contextValue = React.useMemo(() => {
|
|
12
|
+
return {
|
|
13
|
+
dragPluginType,
|
|
14
|
+
onDragStart,
|
|
15
|
+
onDragEnd,
|
|
16
|
+
};
|
|
17
|
+
}, [dragPluginType, onDragStart, onDragEnd]);
|
|
18
|
+
return (React.createElement(DashKitDnDContext.Provider, { value: contextValue }, props.children));
|
|
19
|
+
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export default GridItemForwarderRef;
|
|
2
|
+
declare const GridItemForwarderRef: React.ForwardRefExoticComponent<React.RefAttributes<any>>;
|
|
3
3
|
import React from 'react';
|
|
@@ -122,6 +122,8 @@ GridItem.propTypes = {
|
|
|
122
122
|
onTouchStart: PropTypes.func,
|
|
123
123
|
};
|
|
124
124
|
GridItem.contextType = DashKitContext;
|
|
125
|
-
|
|
125
|
+
const GridItemForwarderRef = React.forwardRef((props, ref) => {
|
|
126
126
|
return React.createElement(GridItem, Object.assign({}, props, { forwardedRef: ref }));
|
|
127
127
|
});
|
|
128
|
+
GridItemForwarderRef.displayName = 'forwardRef(GridItem)';
|
|
129
|
+
export default GridItemForwarderRef;
|
|
@@ -21,6 +21,8 @@ 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;
|
|
24
26
|
render(): JSX.Element;
|
|
25
27
|
}
|
|
26
28
|
import React from 'react';
|
|
@@ -1,9 +1,47 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import ReactGridLayout, { WidthProvider } from 'react-grid-layout';
|
|
3
|
-
import { OVERLAY_CONTROLS_CLASS_NAME } from '../../constants';
|
|
3
|
+
import { OVERLAY_CLASS_NAME, OVERLAY_CONTROLS_CLASS_NAME, TEMPORARY_ITEM_ID } from '../../constants';
|
|
4
4
|
import { DashKitContext } from '../../context/DashKitContext';
|
|
5
5
|
import GridItem from '../GridItem/GridItem';
|
|
6
|
-
|
|
6
|
+
class DragOverLayout extends ReactGridLayout {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(...arguments);
|
|
9
|
+
this.dragReset = () => {
|
|
10
|
+
if (this.dragEnterCounter) {
|
|
11
|
+
this.dragEnterCounter = 0;
|
|
12
|
+
this.removeDroppingPlaceholder();
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
componentDidMount() {
|
|
17
|
+
super.componentDidMount();
|
|
18
|
+
window.addEventListener('dragend', this.dragReset);
|
|
19
|
+
}
|
|
20
|
+
componentWillUnmount() {
|
|
21
|
+
window.removeEventListener('dragend', this.dragReset);
|
|
22
|
+
}
|
|
23
|
+
processGridItem(child, isDroppingItem) {
|
|
24
|
+
if (isDroppingItem) {
|
|
25
|
+
// Drop item from outside gets 0,0 droppingPosition
|
|
26
|
+
// centering cursor on newly creted grid item
|
|
27
|
+
// And cause grid-layout using it's own GridItem to make it look
|
|
28
|
+
// like overlay adding className
|
|
29
|
+
const gridItem = super.processGridItem(child, isDroppingItem);
|
|
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 super.processGridItem(child, isDroppingItem);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const Layout = WidthProvider(DragOverLayout); // eslint-disable-line new-cap
|
|
7
45
|
class GridLayout extends React.PureComponent {
|
|
8
46
|
constructor(props, context) {
|
|
9
47
|
super(props, context);
|
|
@@ -29,13 +67,38 @@ class GridLayout extends React.PureComponent {
|
|
|
29
67
|
return getItemsMeta(this.pluginsRefs);
|
|
30
68
|
};
|
|
31
69
|
this._onStart = () => {
|
|
70
|
+
if (this.temporaryLayout)
|
|
71
|
+
return;
|
|
32
72
|
this.setState({ isDragging: true });
|
|
33
73
|
};
|
|
34
74
|
this._onStop = (newLayout) => {
|
|
35
|
-
const { layoutChange } = this.context;
|
|
36
|
-
|
|
75
|
+
const { layoutChange, onDrop, temporaryLayout } = this.context;
|
|
76
|
+
if (temporaryLayout) {
|
|
77
|
+
onDrop === null || onDrop === void 0 ? void 0 : onDrop(newLayout, newLayout.find(({ i }) => i === TEMPORARY_ITEM_ID));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
layoutChange(newLayout);
|
|
81
|
+
}
|
|
37
82
|
this.setState({ isDragging: false });
|
|
38
83
|
};
|
|
84
|
+
this._onDropDragOver = (e) => {
|
|
85
|
+
const { editMode, onDropDragOver, temporaryLayout } = this.context;
|
|
86
|
+
if (!editMode) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const result = onDropDragOver(e);
|
|
90
|
+
if (temporaryLayout) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
};
|
|
95
|
+
this._onDrop = (layout, item, e) => {
|
|
96
|
+
const { editMode, temporaryLayout, onDrop } = this.context;
|
|
97
|
+
if (!editMode && temporaryLayout) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
onDrop === null || onDrop === void 0 ? void 0 : onDrop(layout, item, e);
|
|
101
|
+
};
|
|
39
102
|
this.pluginsRefs = [];
|
|
40
103
|
this.state = {
|
|
41
104
|
isDragging: false,
|
|
@@ -69,14 +132,20 @@ class GridLayout extends React.PureComponent {
|
|
|
69
132
|
}
|
|
70
133
|
}
|
|
71
134
|
render() {
|
|
72
|
-
const { layout, config, registerManager, editMode, noOverlay, focusable, draggableHandleClassName, } = this.context;
|
|
135
|
+
const { layout, temporaryLayout, config, registerManager, editMode, noOverlay, focusable, draggableHandleClassName, outerDnDEnable, } = this.context;
|
|
73
136
|
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
|
|
137
|
+
return (React.createElement(Layout, Object.assign({}, registerManager.gridLayout, { layout: temporaryLayout || layout, isDraggable: editMode, isResizable: editMode, onDragStart: this._onStart, onDragStop: this._onStop, onResizeStart: this._onStart, onResizeStop: this._onStop }, (draggableHandleClassName
|
|
75
138
|
? { draggableHandle: `.${draggableHandleClassName}` }
|
|
139
|
+
: null), (outerDnDEnable
|
|
140
|
+
? {
|
|
141
|
+
isDroppable: true,
|
|
142
|
+
onDropDragOver: this._onDropDragOver,
|
|
143
|
+
onDrop: this._onDrop,
|
|
144
|
+
}
|
|
76
145
|
: null), { draggableCancel: `.${OVERLAY_CONTROLS_CLASS_NAME}` }), config.items.map((item, i) => {
|
|
77
146
|
return (React.createElement(GridItem, { forwardedPluginRef: (pluginRef) => {
|
|
78
147
|
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 }));
|
|
148
|
+
}, key: item.id, id: item.id, item: item, layout: temporaryLayout || layout, adjustWidgetLayout: this.adjustWidgetLayout, isDragging: this.state.isDragging, noOverlay: noOverlay, focusable: focusable, withCustomHandle: Boolean(draggableHandleClassName), overlayControls: this.props.overlayControls }));
|
|
80
149
|
})));
|
|
81
150
|
}
|
|
82
151
|
}
|
|
@@ -2,3 +2,4 @@ export * from './DashKit';
|
|
|
2
2
|
export type { OverlayControlItem, PreparedCopyItemOptions } from './OverlayControls/OverlayControls';
|
|
3
3
|
export * from './ActionPanel/ActionPanel';
|
|
4
4
|
export type { ActionPanelItem, ActionPanelProps } from './ActionPanel/types';
|
|
5
|
+
export * from './DashKitDnDWrapper/DashKitDnDWrapper';
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export declare const COPIED_WIDGET_STORE_KEY = "dashCopiedItem";
|
|
2
2
|
export declare const DEFAULT_NAMESPACE = "default";
|
|
3
3
|
export declare const OVERLAY_CONTROLS_CLASS_NAME = "dashkit-overlay-controls";
|
|
4
|
+
export declare const OVERLAY_CLASS_NAME = "dashkit-grid-item__overlay";
|
|
5
|
+
export declare const TEMPORARY_ITEM_ID = "__dropping-elem__";
|
|
4
6
|
export declare const MenuItems: {
|
|
5
7
|
readonly Copy: "copy";
|
|
6
8
|
readonly Delete: "delete";
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export const COPIED_WIDGET_STORE_KEY = 'dashCopiedItem';
|
|
2
2
|
export const DEFAULT_NAMESPACE = 'default';
|
|
3
3
|
export const OVERLAY_CONTROLS_CLASS_NAME = 'dashkit-overlay-controls';
|
|
4
|
+
export const OVERLAY_CLASS_NAME = 'dashkit-grid-item__overlay';
|
|
5
|
+
export const TEMPORARY_ITEM_ID = '__dropping-elem__';
|
|
4
6
|
export const MenuItems = {
|
|
5
7
|
Copy: 'copy',
|
|
6
8
|
Delete: 'delete',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import isEqual from 'lodash/isEqual';
|
|
3
|
-
import {
|
|
3
|
+
import { TEMPORARY_ITEM_ID } from '../constants/common';
|
|
4
|
+
import { DashKitContext, DashKitDnDContext } from '../context/DashKitContext';
|
|
4
5
|
import { getItemsParams, getItemsState } from '../shared';
|
|
5
6
|
import { UpdateManager } from '../utils';
|
|
6
7
|
function useMemoStateContext(props) {
|
|
@@ -11,6 +12,10 @@ function useMemoStateContext(props) {
|
|
|
11
12
|
// (высота) из конфига будет запоминаться в originalLayouts, новое значение высоты в adjustedLayouts
|
|
12
13
|
const originalLayouts = React.useRef({});
|
|
13
14
|
const adjustedLayouts = React.useRef({});
|
|
15
|
+
const [temporaryLayout, setTemporaryLayout] = React.useState(null);
|
|
16
|
+
const resetTemporaryLayout = React.useCallback(() => setTemporaryLayout(null), [setTemporaryLayout]);
|
|
17
|
+
const dndContext = React.useContext(DashKitDnDContext);
|
|
18
|
+
const outerDnDEnable = Boolean(dndContext);
|
|
14
19
|
// TODO: need move originalLayouts, adjustedLayouts to state
|
|
15
20
|
const [layoutUpdateCounter, forceUpdateLayout] = React.useState(0);
|
|
16
21
|
const onChange = React.useCallback(({ config = props.config, itemsStateAndParams = props.itemsStateAndParams }) => {
|
|
@@ -41,12 +46,27 @@ function useMemoStateContext(props) {
|
|
|
41
46
|
}
|
|
42
47
|
}, [props.config, onChange]);
|
|
43
48
|
const onItemRemove = React.useCallback((id) => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
if (id === TEMPORARY_ITEM_ID) {
|
|
50
|
+
resetTemporaryLayout();
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
if (temporaryLayout) {
|
|
54
|
+
setTemporaryLayout(temporaryLayout.filter(({ i }) => i !== id));
|
|
55
|
+
}
|
|
56
|
+
onChange(UpdateManager.removeItem({
|
|
57
|
+
id,
|
|
58
|
+
config: props.config,
|
|
59
|
+
itemsStateAndParams: props.itemsStateAndParams,
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
}, [
|
|
63
|
+
props.config,
|
|
64
|
+
props.itemsStateAndParams,
|
|
65
|
+
temporaryLayout,
|
|
66
|
+
onChange,
|
|
67
|
+
setTemporaryLayout,
|
|
68
|
+
resetTemporaryLayout,
|
|
69
|
+
]);
|
|
50
70
|
const onItemStateAndParamsChange = React.useCallback((id, stateAndParams, options) => {
|
|
51
71
|
onChange({
|
|
52
72
|
itemsStateAndParams: UpdateManager.changeStateAndParams({
|
|
@@ -120,8 +140,58 @@ function useMemoStateContext(props) {
|
|
|
120
140
|
const reloadItems = React.useCallback((pluginsRefs, data) => {
|
|
121
141
|
pluginsRefs.forEach((ref) => ref && ref.reload && ref.reload(data));
|
|
122
142
|
}, []);
|
|
143
|
+
const draggedPluginRef = React.useRef(dndContext === null || dndContext === void 0 ? void 0 : dndContext.dragPluginType);
|
|
144
|
+
React.useEffect(() => {
|
|
145
|
+
if (!(dndContext === null || dndContext === void 0 ? void 0 : dndContext.dragPluginType) && !temporaryLayout) {
|
|
146
|
+
draggedPluginRef.current = null;
|
|
147
|
+
}
|
|
148
|
+
else if (dndContext === null || dndContext === void 0 ? void 0 : dndContext.dragPluginType) {
|
|
149
|
+
draggedPluginRef.current = dndContext.dragPluginType;
|
|
150
|
+
}
|
|
151
|
+
}, [draggedPluginRef, dndContext === null || dndContext === void 0 ? void 0 : dndContext.dragPluginType, temporaryLayout]);
|
|
152
|
+
const dndPluginType = draggedPluginRef.current || (dndContext === null || dndContext === void 0 ? void 0 : dndContext.dragPluginType);
|
|
153
|
+
const dragOverPlugin = React.useMemo(() => {
|
|
154
|
+
const pluginType = dndPluginType;
|
|
155
|
+
if (pluginType === null && temporaryLayout === null)
|
|
156
|
+
return null;
|
|
157
|
+
if (props.registerManager.check(pluginType)) {
|
|
158
|
+
return props.registerManager.getItem(pluginType);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
// eslint-disable-next-line no-console
|
|
162
|
+
console.error(`Uknown pluginType: ${pluginType}`);
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}, [dndPluginType, props.registerManager, temporaryLayout]);
|
|
166
|
+
const onDropDragOver = React.useCallback(() => {
|
|
167
|
+
if (temporaryLayout) {
|
|
168
|
+
resetTemporaryLayout();
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
if (dragOverPlugin === null || dragOverPlugin === void 0 ? void 0 : dragOverPlugin.defaultLayout) {
|
|
172
|
+
const { h = 3, w = 3 } = dragOverPlugin.defaultLayout;
|
|
173
|
+
return { h, w };
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
176
|
+
}, [resetTemporaryLayout, temporaryLayout, dragOverPlugin]);
|
|
177
|
+
const onDrop = React.useCallback((newLayout, item) => {
|
|
178
|
+
setTemporaryLayout(newLayout);
|
|
179
|
+
const { i, w, h, x, y } = item;
|
|
180
|
+
props.onDrop({
|
|
181
|
+
newLayout: newLayout.reduce((memo, l) => {
|
|
182
|
+
if (l.i !== i) {
|
|
183
|
+
memo.push({ i: l.i, w: l.w, h: l.h, x: l.x, y: l.y });
|
|
184
|
+
}
|
|
185
|
+
return memo;
|
|
186
|
+
}, []),
|
|
187
|
+
itemLayout: { w, h, x, y },
|
|
188
|
+
pluginType: dndPluginType,
|
|
189
|
+
commit: resetTemporaryLayout,
|
|
190
|
+
});
|
|
191
|
+
}, [dndPluginType, props.onDrop]);
|
|
123
192
|
return React.useMemo(() => ({
|
|
124
193
|
layout: resultLayout,
|
|
194
|
+
temporaryLayout,
|
|
125
195
|
config: props.config,
|
|
126
196
|
context: props.context,
|
|
127
197
|
noOverlay: props.noOverlay,
|
|
@@ -135,6 +205,8 @@ function useMemoStateContext(props) {
|
|
|
135
205
|
registerManager: props.registerManager,
|
|
136
206
|
onItemStateAndParamsChange,
|
|
137
207
|
removeItem: onItemRemove,
|
|
208
|
+
onDrop,
|
|
209
|
+
onDropDragOver,
|
|
138
210
|
editItem: props.onItemEdit,
|
|
139
211
|
layoutChange: onLayoutChange,
|
|
140
212
|
getItemsMeta,
|
|
@@ -143,8 +215,11 @@ function useMemoStateContext(props) {
|
|
|
143
215
|
revertToOriginalLayout,
|
|
144
216
|
forwardedMetaRef: props.forwardedMetaRef,
|
|
145
217
|
draggableHandleClassName: props.draggableHandleClassName,
|
|
218
|
+
outerDnDEnable,
|
|
219
|
+
dragOverPlugin,
|
|
146
220
|
}), [
|
|
147
221
|
resultLayout,
|
|
222
|
+
temporaryLayout,
|
|
148
223
|
props.config,
|
|
149
224
|
props.context,
|
|
150
225
|
props.noOverlay,
|
|
@@ -157,6 +232,8 @@ function useMemoStateContext(props) {
|
|
|
157
232
|
props.registerManager,
|
|
158
233
|
onItemStateAndParamsChange,
|
|
159
234
|
onItemRemove,
|
|
235
|
+
onDrop,
|
|
236
|
+
onDropDragOver,
|
|
160
237
|
props.onItemEdit,
|
|
161
238
|
onLayoutChange,
|
|
162
239
|
getItemsMeta,
|
|
@@ -165,6 +242,8 @@ function useMemoStateContext(props) {
|
|
|
165
242
|
revertToOriginalLayout,
|
|
166
243
|
props.forwardedMetaRef,
|
|
167
244
|
props.draggableHandleClassName,
|
|
245
|
+
outerDnDEnable,
|
|
246
|
+
dragOverPlugin,
|
|
168
247
|
]);
|
|
169
248
|
}
|
|
170
249
|
export function withContext(Component) {
|
|
@@ -113,5 +113,8 @@ const plugin = {
|
|
|
113
113
|
renderer(props, forwardedRef) {
|
|
114
114
|
return React.createElement(PluginText, Object.assign({}, props, { apiHandler: plugin._apiHandler, ref: forwardedRef }));
|
|
115
115
|
},
|
|
116
|
+
placeholderRenderer(_, forwardedRef) {
|
|
117
|
+
return React.createElement("div", { ref: forwardedRef }, "Text placeholder");
|
|
118
|
+
},
|
|
116
119
|
};
|
|
117
120
|
export default plugin;
|
|
@@ -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:
|
|
22
|
+
options: SetNewItemOptions;
|
|
23
23
|
}): Config;
|
|
24
24
|
static editItem({ item, namespace, config, options, }: {
|
|
25
25
|
item: ConfigItem;
|
|
@@ -212,19 +212,37 @@ function changeGroupParams({ groupItemIds, initiatorId, initiatorItem, itemsStat
|
|
|
212
212
|
}
|
|
213
213
|
export class UpdateManager {
|
|
214
214
|
static addItem({ item, namespace = DEFAULT_NAMESPACE, layout, config, options, }) {
|
|
215
|
-
const layoutY = Math.max(0, ...config.layout.map(({ h, y }) => h + y));
|
|
216
|
-
const saveDefaultLayout = pick(layout, ['h', 'w', 'x', 'y']);
|
|
217
215
|
const salt = config.salt;
|
|
218
216
|
const newItemData = getNewItemData({ item, config, salt, counter: config.counter, options });
|
|
219
217
|
let counter = newItemData.counter;
|
|
220
218
|
const newIdData = getNewId({ config, salt, counter, excludeIds: newItemData.excludeIds });
|
|
221
219
|
counter = newIdData.counter;
|
|
222
220
|
const newItem = Object.assign(Object.assign({}, item), { id: newIdData.id, data: newItemData.data, namespace });
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
221
|
+
const saveDefaultLayout = pick(layout, ['h', 'w', 'x', 'y']);
|
|
222
|
+
if (options.updateLayout) {
|
|
223
|
+
const byId = options.updateLayout.reduce((memo, t) => {
|
|
224
|
+
memo[t.i] = t;
|
|
225
|
+
return memo;
|
|
226
|
+
}, {});
|
|
227
|
+
const newLayout = [
|
|
228
|
+
...config.layout.map((t) => (Object.assign(Object.assign({}, t), (byId[t.i] || {})))),
|
|
229
|
+
Object.assign(Object.assign({}, saveDefaultLayout), { i: newItem.id }),
|
|
230
|
+
];
|
|
231
|
+
return update(config, {
|
|
232
|
+
items: { $push: [newItem] },
|
|
233
|
+
layout: { $set: newLayout },
|
|
234
|
+
counter: { $set: counter },
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
const layoutY = Math.max(0, ...config.layout.map(({ h, y }) => h + y));
|
|
239
|
+
const newLayoutItem = Object.assign(Object.assign({}, saveDefaultLayout), { y: layoutY, i: newItem.id });
|
|
240
|
+
return update(config, {
|
|
241
|
+
items: { $push: [newItem] },
|
|
242
|
+
layout: { $push: [newLayoutItem] },
|
|
243
|
+
counter: { $set: counter },
|
|
244
|
+
});
|
|
245
|
+
}
|
|
228
246
|
}
|
|
229
247
|
static editItem({ item, namespace = DEFAULT_NAMESPACE, config, options, }) {
|
|
230
248
|
const itemIndex = config.items.findIndex(({ id }) => item.id === id);
|