@gravity-ui/dashkit 8.5.0 → 8.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -4
- package/build/cjs/components/DashKit/DashKit.d.ts +3 -2
- package/build/cjs/components/DashKitView/DashKitView.js +2 -2
- package/build/cjs/components/GridItem/GridItem.js +2 -4
- package/build/cjs/components/GridLayout/GridLayout.d.ts +16 -1
- package/build/cjs/components/GridLayout/GridLayout.js +81 -10
- package/build/cjs/components/Item/Item.d.ts +4 -0
- package/build/cjs/components/OverlayControls/OverlayControls.d.ts +8 -6
- package/build/cjs/components/OverlayControls/OverlayControls.js +58 -42
- package/build/cjs/constants/common.d.ts +1 -0
- package/build/cjs/constants/common.js +2 -1
- package/build/cjs/context/DashKitContext.d.ts +1 -0
- package/build/cjs/context/DashKitContext.js +3 -1
- package/build/cjs/hocs/prepareItem.d.ts +4 -0
- package/build/cjs/hocs/prepareItem.js +33 -22
- package/build/cjs/hocs/withContext.js +129 -21
- package/build/cjs/hooks/useDeepEqualMemo.d.ts +2 -0
- package/build/cjs/hooks/useDeepEqualMemo.js +17 -0
- package/build/cjs/typings/config.d.ts +3 -1
- package/build/esm/components/DashKit/DashKit.d.ts +3 -2
- package/build/esm/components/DashKitView/DashKitView.js +2 -2
- package/build/esm/components/GridItem/GridItem.js +2 -4
- package/build/esm/components/GridLayout/GridLayout.d.ts +16 -1
- package/build/esm/components/GridLayout/GridLayout.js +82 -11
- package/build/esm/components/Item/Item.d.ts +4 -0
- package/build/esm/components/OverlayControls/OverlayControls.d.ts +8 -6
- package/build/esm/components/OverlayControls/OverlayControls.js +59 -43
- package/build/esm/constants/common.d.ts +1 -0
- package/build/esm/constants/common.js +1 -0
- package/build/esm/context/DashKitContext.d.ts +1 -0
- package/build/esm/context/DashKitContext.js +2 -1
- package/build/esm/hocs/prepareItem.d.ts +4 -0
- package/build/esm/hocs/prepareItem.js +33 -22
- package/build/esm/hocs/withContext.js +132 -23
- package/build/esm/hooks/useDeepEqualMemo.d.ts +2 -0
- package/build/esm/hooks/useDeepEqualMemo.js +12 -0
- package/build/esm/typings/config.d.ts +3 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ButtonSize, ButtonView, IconProps, MenuItemProps } from '@gravity-ui/uikit';
|
|
3
|
-
import type {
|
|
4
|
-
import
|
|
3
|
+
import type { ConfigItem, ConfigLayout, ItemState, PluginBase, StringParams } from '../../shared';
|
|
4
|
+
import { MenuItem } from '../../typings';
|
|
5
5
|
import './OverlayControls.css';
|
|
6
6
|
export declare enum OverlayControlsPosition {
|
|
7
7
|
TopRight = "top_right",
|
|
@@ -14,6 +14,7 @@ export interface OverlayControlItem {
|
|
|
14
14
|
icon?: IconProps['data'];
|
|
15
15
|
iconSize?: number | string;
|
|
16
16
|
handler?: (item: ConfigItem) => void;
|
|
17
|
+
visible?: (item: ConfigItem) => boolean;
|
|
17
18
|
allWidgetsControls?: boolean;
|
|
18
19
|
excludeWidgetsTypes?: Array<PluginBase['type']>;
|
|
19
20
|
id?: string;
|
|
@@ -36,8 +37,6 @@ interface OverlayControlsDefaultProps {
|
|
|
36
37
|
}
|
|
37
38
|
interface OverlayControlsProps extends OverlayControlsDefaultProps {
|
|
38
39
|
configItem: ConfigItem;
|
|
39
|
-
items?: OverlayControlItem[];
|
|
40
|
-
overlayControls?: Record<string, OverlayControlItem[]>;
|
|
41
40
|
}
|
|
42
41
|
type PreparedCopyItemOptionsArg = Pick<ConfigItem, 'data' | 'type' | 'defaults' | 'namespace'> & {
|
|
43
42
|
timestamp: number;
|
|
@@ -50,21 +49,24 @@ export type PreparedCopyItemOptions<C extends object = {}> = PreparedCopyItemOpt
|
|
|
50
49
|
copyContext?: C;
|
|
51
50
|
};
|
|
52
51
|
type DashKitCtx = React.Context<{
|
|
52
|
+
overlayControls?: Record<string, OverlayControlItem[]>;
|
|
53
53
|
context: Record<string, any>;
|
|
54
|
-
|
|
54
|
+
menu: MenuItem[];
|
|
55
55
|
itemsParams: Record<string, StringParams>;
|
|
56
56
|
itemsState: Record<string, ItemState>;
|
|
57
57
|
editItem: (item: ConfigItem) => void;
|
|
58
58
|
removeItem: (id: string) => void;
|
|
59
|
-
|
|
59
|
+
getLayoutItem: (id: string) => ConfigLayout | void;
|
|
60
60
|
}>;
|
|
61
61
|
declare class OverlayControls extends React.Component<OverlayControlsProps> {
|
|
62
62
|
static contextType: React.Context<any>;
|
|
63
63
|
static defaultProps: OverlayControlsDefaultProps;
|
|
64
64
|
context: React.ContextType<DashKitCtx>;
|
|
65
65
|
render(): JSX.Element;
|
|
66
|
+
private getItems;
|
|
66
67
|
private renderControlsItem;
|
|
67
68
|
private getDropDownMenuItemConfig;
|
|
69
|
+
private getDefaultControls;
|
|
68
70
|
private renderControls;
|
|
69
71
|
private renderMenu;
|
|
70
72
|
private isDefaultMenu;
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { Button, DropdownMenu, Icon, } from '@gravity-ui/uikit';
|
|
3
3
|
import noop from 'lodash/noop';
|
|
4
4
|
import { COPIED_WIDGET_STORE_KEY, MenuItems, OVERLAY_CONTROLS_CLASS_NAME } from '../../constants';
|
|
5
|
-
import {
|
|
5
|
+
import { DashkitOvelayControlsContext } from '../../context/DashKitContext';
|
|
6
6
|
import { i18n } from '../../i18n';
|
|
7
7
|
import { CloseIcon } from '../../icons/CloseIcon';
|
|
8
8
|
import { CogIcon } from '../../icons/CogIcon';
|
|
@@ -21,6 +21,11 @@ const DEFAULT_DROPDOWN_MENU = [MenuItems.Copy, MenuItems.Delete];
|
|
|
21
21
|
class OverlayControls extends React.Component {
|
|
22
22
|
constructor() {
|
|
23
23
|
super(...arguments);
|
|
24
|
+
this.getItems = () => {
|
|
25
|
+
const { overlayControls } = this.context;
|
|
26
|
+
const { configItem } = this.props;
|
|
27
|
+
return (overlayControls && overlayControls[configItem.type]) || [];
|
|
28
|
+
};
|
|
24
29
|
this.renderControlsItem = (item, index, length) => {
|
|
25
30
|
const { view, size } = this.props;
|
|
26
31
|
const { title, handler, icon, iconSize, qa } = item;
|
|
@@ -28,18 +33,28 @@ class OverlayControls extends React.Component {
|
|
|
28
33
|
return (React.createElement(Button, { key: index, view: view, size: size, title: title, pin: this.getControlItemPinStyle(index, length), onClick: () => onItemClickHandler(this.props.configItem), qa: qa },
|
|
29
34
|
React.createElement(Icon, { data: icon || CogIcon, size: icon ? iconSize : 24 })));
|
|
30
35
|
};
|
|
36
|
+
this.getDefaultControls = () => {
|
|
37
|
+
const { view, size } = this.props;
|
|
38
|
+
if (this.context.overlayControls === null) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return (React.createElement(Button, { view: view, size: size, title: i18n('label_settings'), pin: "round-brick", onClick: this.onEditItem, qa: "dashkit-overlay-control-settings" },
|
|
42
|
+
React.createElement(Icon, { data: CogIcon, size: "24" })));
|
|
43
|
+
};
|
|
31
44
|
this.getCustomLeftOverlayControls = () => {
|
|
32
45
|
// выбираем только items-ы у которых проставлено поле `allWidgetsControls:true`
|
|
33
46
|
// те контролы, которые будут показываться слева от меню
|
|
34
47
|
let controls = [];
|
|
35
|
-
for (const controlItem of Object.values(this.
|
|
48
|
+
for (const controlItem of Object.values(this.context.overlayControls || {})) {
|
|
36
49
|
controls = controls.concat((controlItem.filter((item) => {
|
|
37
50
|
var _a;
|
|
38
51
|
// если тип виджета-плагина в списке исключаемых, то не показываем такой контрол
|
|
39
52
|
if ((_a = item.excludeWidgetsTypes) === null || _a === void 0 ? void 0 : _a.includes(this.props.configItem.type)) {
|
|
40
53
|
return false;
|
|
41
54
|
}
|
|
42
|
-
return item.
|
|
55
|
+
return item.visible
|
|
56
|
+
? item.visible(this.props.configItem)
|
|
57
|
+
: item.allWidgetsControls;
|
|
43
58
|
}) || []).map((item) => {
|
|
44
59
|
var _a;
|
|
45
60
|
if (!(item === null || item === void 0 ? void 0 : item.id)) {
|
|
@@ -54,15 +69,14 @@ class OverlayControls extends React.Component {
|
|
|
54
69
|
};
|
|
55
70
|
this.onCopyItem = () => {
|
|
56
71
|
var _a;
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
});
|
|
72
|
+
const { configItem } = this.props;
|
|
73
|
+
const correspondedItemLayout = this.context.getLayoutItem(configItem.id);
|
|
60
74
|
let options = {
|
|
61
75
|
timestamp: Date.now(),
|
|
62
|
-
data:
|
|
63
|
-
type:
|
|
64
|
-
defaults:
|
|
65
|
-
namespace:
|
|
76
|
+
data: configItem.data,
|
|
77
|
+
type: configItem.type,
|
|
78
|
+
defaults: configItem.defaults,
|
|
79
|
+
namespace: configItem.namespace,
|
|
66
80
|
layout: {
|
|
67
81
|
w: correspondedItemLayout.w,
|
|
68
82
|
h: correspondedItemLayout.h,
|
|
@@ -84,7 +98,8 @@ class OverlayControls extends React.Component {
|
|
|
84
98
|
};
|
|
85
99
|
}
|
|
86
100
|
render() {
|
|
87
|
-
const {
|
|
101
|
+
const { position } = this.props;
|
|
102
|
+
const items = this.getItems();
|
|
88
103
|
const hasCustomControlsWithWidgets = items.length > 0;
|
|
89
104
|
const controls = hasCustomControlsWithWidgets
|
|
90
105
|
? this.getCustomControlsWithWidgets()
|
|
@@ -119,11 +134,9 @@ class OverlayControls extends React.Component {
|
|
|
119
134
|
return null;
|
|
120
135
|
}
|
|
121
136
|
renderControls() {
|
|
122
|
-
const { view, size } = this.props;
|
|
123
137
|
const customLeftControls = this.getCustomLeftOverlayControls();
|
|
124
138
|
const hasCustomOverlayLeftControls = Boolean(customLeftControls.length);
|
|
125
|
-
const defaultControl =
|
|
126
|
-
React.createElement(Icon, { data: CogIcon, size: "24" })));
|
|
139
|
+
const defaultControl = this.getDefaultControls();
|
|
127
140
|
const controls = hasCustomOverlayLeftControls
|
|
128
141
|
? customLeftControls.map((item, index, items) => this.renderControlsItem(item, index, items.length + 1))
|
|
129
142
|
: defaultControl;
|
|
@@ -136,50 +149,58 @@ class OverlayControls extends React.Component {
|
|
|
136
149
|
}
|
|
137
150
|
renderMenu() {
|
|
138
151
|
const { view, size } = this.props;
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
152
|
+
const dropdown = this.renderDropdownMenu();
|
|
153
|
+
if (dropdown) {
|
|
154
|
+
return dropdown;
|
|
155
|
+
}
|
|
156
|
+
return (React.createElement(Button, { key: 'delete-control', view: view, size: size, title: i18n('label_delete'), pin: "brick-round", onClick: this.onRemoveItem, qa: "dashkit-overlay-control-menu" },
|
|
157
|
+
React.createElement(Icon, { data: CloseIcon, size: "12" })));
|
|
143
158
|
}
|
|
144
159
|
isDefaultMenu(menu) {
|
|
145
160
|
return menu === null || menu === void 0 ? void 0 : menu.every((item) => Object.values(MenuItems).includes(String(item)));
|
|
146
161
|
}
|
|
147
162
|
renderDropdownMenu() {
|
|
148
163
|
const { view, size } = this.props;
|
|
149
|
-
const {
|
|
164
|
+
const { menu: contextMenu, itemsParams, itemsState } = this.context;
|
|
150
165
|
const configItem = this.props.configItem;
|
|
151
166
|
const itemParams = itemsParams[configItem.id];
|
|
152
167
|
const itemState = itemsState[configItem.id];
|
|
153
|
-
|
|
154
|
-
if (!menu.length) {
|
|
155
|
-
menu = DEFAULT_DROPDOWN_MENU;
|
|
156
|
-
}
|
|
168
|
+
const menu = (contextMenu === null || contextMenu === void 0 ? void 0 : contextMenu.length) > 0 ? contextMenu : DEFAULT_DROPDOWN_MENU;
|
|
157
169
|
const isDefaultMenu = this.isDefaultMenu(menu);
|
|
158
|
-
|
|
159
|
-
? (menu || []).
|
|
160
|
-
|
|
170
|
+
const items = isDefaultMenu
|
|
171
|
+
? (menu || []).reduce((memo, name) => {
|
|
172
|
+
const item = this.getDropDownMenuItemConfig(name, true);
|
|
173
|
+
if (item) {
|
|
174
|
+
memo.push(item);
|
|
175
|
+
}
|
|
176
|
+
return memo;
|
|
177
|
+
}, [])
|
|
178
|
+
: menu.reduce((memo, item) => {
|
|
161
179
|
var _a;
|
|
162
180
|
if (typeof item === 'string') {
|
|
163
|
-
return
|
|
181
|
+
return memo;
|
|
164
182
|
}
|
|
165
183
|
// custom menu dropdown item filter
|
|
166
184
|
if (item.visible && !item.visible(configItem)) {
|
|
167
|
-
return
|
|
185
|
+
return memo;
|
|
168
186
|
}
|
|
169
187
|
const itemHandler = item.handler;
|
|
170
188
|
const itemAction = typeof itemHandler === 'function'
|
|
171
189
|
? () => itemHandler(configItem, itemParams, itemState)
|
|
172
190
|
: ((_a = this.getDropDownMenuItemConfig(item.id)) === null || _a === void 0 ? void 0 : _a.action) || (() => { });
|
|
173
|
-
|
|
191
|
+
memo.push({
|
|
174
192
|
// @ts-expect-error
|
|
175
193
|
text: item.title || i18n(item.id),
|
|
176
194
|
icon: item.icon,
|
|
177
195
|
action: itemAction,
|
|
178
196
|
className: item.className,
|
|
179
197
|
qa: item.qa,
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
|
|
198
|
+
});
|
|
199
|
+
return memo;
|
|
200
|
+
}, []);
|
|
201
|
+
if (items.length === 0) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
183
204
|
return (React.createElement(DropdownMenu, { items: items, renderSwitcher: (props) => (React.createElement(Button, Object.assign({}, props, { view: view, size: size, pin: "brick-round", qa: "dashkit-overlay-control-menu" }),
|
|
184
205
|
React.createElement(Icon, { data: DotsIcon, size: "16" }))) }));
|
|
185
206
|
}
|
|
@@ -199,19 +220,14 @@ class OverlayControls extends React.Component {
|
|
|
199
220
|
return 'brick-brick';
|
|
200
221
|
}
|
|
201
222
|
getCustomControlsWithWidgets() {
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
handler: this.onRemoveItem,
|
|
208
|
-
};
|
|
209
|
-
const { items = [] } = this.props;
|
|
210
|
-
const customOverlayControls = [...items, deleteControl];
|
|
211
|
-
return customOverlayControls.map((item, index, controlItems) => this.renderControlsItem(item, index, controlItems.length));
|
|
223
|
+
const items = this.getItems();
|
|
224
|
+
const result = items.map((item, index, controlItems) => this.renderControlsItem(item, index, controlItems.length));
|
|
225
|
+
// Добавляем контрол удаления или меню виджета по умолчанию
|
|
226
|
+
result.push(this.renderMenu());
|
|
227
|
+
return result;
|
|
212
228
|
}
|
|
213
229
|
}
|
|
214
|
-
OverlayControls.contextType =
|
|
230
|
+
OverlayControls.contextType = DashkitOvelayControlsContext;
|
|
215
231
|
OverlayControls.defaultProps = {
|
|
216
232
|
position: OverlayControlsPosition.TopRight,
|
|
217
233
|
view: 'flat',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
const DashKitContext = React.createContext();
|
|
3
|
+
const DashkitOvelayControlsContext = React.createContext();
|
|
3
4
|
const DashKitDnDContext = React.createContext();
|
|
4
|
-
export { DashKitContext, DashKitDnDContext };
|
|
5
|
+
export { DashKitContext, DashkitOvelayControlsContext, DashKitDnDContext };
|
|
@@ -2,6 +2,8 @@ export function prepareItem(Component: any): {
|
|
|
2
2
|
new (props: any): {
|
|
3
3
|
shouldComponentUpdate(nextProps: any): boolean;
|
|
4
4
|
_onStateAndParamsChange: (stateAndParams: any, options: any) => void;
|
|
5
|
+
_currentRenderProps: {};
|
|
6
|
+
getRenderProps: () => {};
|
|
5
7
|
render(): JSX.Element;
|
|
6
8
|
context: unknown;
|
|
7
9
|
setState<K extends string | number | symbol>(state: any, callback?: (() => void) | undefined): void;
|
|
@@ -26,6 +28,8 @@ export function prepareItem(Component: any): {
|
|
|
26
28
|
new (props: any, context: any): {
|
|
27
29
|
shouldComponentUpdate(nextProps: any): boolean;
|
|
28
30
|
_onStateAndParamsChange: (stateAndParams: any, options: any) => void;
|
|
31
|
+
_currentRenderProps: {};
|
|
32
|
+
getRenderProps: () => {};
|
|
29
33
|
render(): JSX.Element;
|
|
30
34
|
context: unknown;
|
|
31
35
|
setState<K extends string | number | symbol>(state: any, callback?: (() => void) | undefined): void;
|
|
@@ -9,6 +9,35 @@ export function prepareItem(Component) {
|
|
|
9
9
|
this._onStateAndParamsChange = (stateAndParams, options) => {
|
|
10
10
|
this.context.onItemStateAndParamsChange(this.props.id, stateAndParams, options);
|
|
11
11
|
};
|
|
12
|
+
this._currentRenderProps = {};
|
|
13
|
+
this.getRenderProps = () => {
|
|
14
|
+
const { id, width, height, item, adjustWidgetLayout, layout, isPlaceholder } = this.props;
|
|
15
|
+
const { itemsState, itemsParams, registerManager, settings, context, editMode } = this.context;
|
|
16
|
+
const { data, defaults, namespace } = item;
|
|
17
|
+
const rendererProps = {
|
|
18
|
+
data,
|
|
19
|
+
editMode,
|
|
20
|
+
params: itemsParams[id],
|
|
21
|
+
state: itemsState[id],
|
|
22
|
+
onStateAndParamsChange: this._onStateAndParamsChange,
|
|
23
|
+
width,
|
|
24
|
+
height,
|
|
25
|
+
id,
|
|
26
|
+
defaults,
|
|
27
|
+
namespace,
|
|
28
|
+
settings,
|
|
29
|
+
context,
|
|
30
|
+
layout,
|
|
31
|
+
gridLayout: registerManager.gridLayout,
|
|
32
|
+
adjustWidgetLayout,
|
|
33
|
+
isPlaceholder,
|
|
34
|
+
};
|
|
35
|
+
const changedProp = Object.entries(rendererProps).find(([key, value]) => this._currentRenderProps[key] !== value);
|
|
36
|
+
if (changedProp) {
|
|
37
|
+
this._currentRenderProps = rendererProps;
|
|
38
|
+
}
|
|
39
|
+
return this._currentRenderProps;
|
|
40
|
+
};
|
|
12
41
|
}
|
|
13
42
|
shouldComponentUpdate(nextProps) {
|
|
14
43
|
const { width, height, transform } = this.props;
|
|
@@ -22,28 +51,10 @@ export function prepareItem(Component) {
|
|
|
22
51
|
return true;
|
|
23
52
|
}
|
|
24
53
|
render() {
|
|
25
|
-
const {
|
|
26
|
-
const {
|
|
27
|
-
const { type
|
|
28
|
-
|
|
29
|
-
data,
|
|
30
|
-
editMode,
|
|
31
|
-
params: itemsParams[id],
|
|
32
|
-
state: itemsState[id],
|
|
33
|
-
onStateAndParamsChange: this._onStateAndParamsChange,
|
|
34
|
-
width,
|
|
35
|
-
height,
|
|
36
|
-
id,
|
|
37
|
-
defaults,
|
|
38
|
-
namespace,
|
|
39
|
-
settings,
|
|
40
|
-
context,
|
|
41
|
-
layout,
|
|
42
|
-
gridLayout: registerManager.gridLayout,
|
|
43
|
-
adjustWidgetLayout,
|
|
44
|
-
isPlaceholder,
|
|
45
|
-
};
|
|
46
|
-
return (React.createElement(Component, { forwardedPluginRef: this.props.forwardedPluginRef, rendererProps: rendererProps, registerManager: registerManager, type: type, isPlaceholder: isPlaceholder }));
|
|
54
|
+
const { item, isPlaceholder } = this.props;
|
|
55
|
+
const { registerManager } = this.context;
|
|
56
|
+
const { type } = item;
|
|
57
|
+
return (React.createElement(Component, { forwardedPluginRef: this.props.forwardedPluginRef, rendererProps: this.getRenderProps(), registerManager: registerManager, type: type, isPlaceholder: isPlaceholder }));
|
|
47
58
|
}
|
|
48
59
|
},
|
|
49
60
|
_a.propTypes = {
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import { __rest } from "tslib";
|
|
1
2
|
import React from 'react';
|
|
2
3
|
import isEqual from 'lodash/isEqual';
|
|
3
4
|
import pick from 'lodash/pick';
|
|
4
|
-
import { DEFAULT_WIDGET_HEIGHT, DEFAULT_WIDGET_WIDTH, TEMPORARY_ITEM_ID } from '../constants/common';
|
|
5
|
-
import { DashKitContext, DashKitDnDContext } from '../context/DashKitContext';
|
|
5
|
+
import { COMPACT_TYPE_HORIZONTAL_NOWRAP, DEFAULT_GROUP, DEFAULT_WIDGET_HEIGHT, DEFAULT_WIDGET_WIDTH, TEMPORARY_ITEM_ID, } from '../constants/common';
|
|
6
|
+
import { DashKitContext, DashKitDnDContext, DashkitOvelayControlsContext, } from '../context/DashKitContext';
|
|
7
|
+
import { useDeepEqualMemo } from '../hooks/useDeepEqualMemo';
|
|
6
8
|
import { getItemsParams, getItemsState } from '../shared';
|
|
7
9
|
import { UpdateManager } from '../utils';
|
|
10
|
+
const ITEM_PROPS = ['i', 'h', 'w', 'x', 'y', 'parent'];
|
|
8
11
|
function useMemoStateContext(props) {
|
|
9
12
|
// так как мы не хотим хранить параметры виджета с активированной автовысотой в сторе и на сервере, актуальный
|
|
10
13
|
// (видимый юзером в конкретный момент времени) лэйаут (массив объектов с данными о ширине, высоте,
|
|
@@ -13,6 +16,7 @@ function useMemoStateContext(props) {
|
|
|
13
16
|
// (высота) из конфига будет запоминаться в originalLayouts, новое значение высоты в adjustedLayouts
|
|
14
17
|
const originalLayouts = React.useRef({});
|
|
15
18
|
const adjustedLayouts = React.useRef({});
|
|
19
|
+
const nowrapAdjustedLayouts = React.useRef({});
|
|
16
20
|
const [temporaryLayout, setTemporaryLayout] = React.useState(null);
|
|
17
21
|
const resetTemporaryLayout = React.useCallback(() => setTemporaryLayout(null), [setTemporaryLayout]);
|
|
18
22
|
const dndContext = React.useContext(DashKitDnDContext);
|
|
@@ -47,7 +51,13 @@ function useMemoStateContext(props) {
|
|
|
47
51
|
onChange({ config: newConfig });
|
|
48
52
|
}
|
|
49
53
|
}, [props.config, onChange]);
|
|
54
|
+
const getLayoutItem = React.useCallback((id) => {
|
|
55
|
+
return props.config.layout.find(({ i }) => i === id);
|
|
56
|
+
}, [props.config.layout]);
|
|
50
57
|
const onItemRemove = React.useCallback((id) => {
|
|
58
|
+
delete nowrapAdjustedLayouts.current[id];
|
|
59
|
+
delete adjustedLayouts.current[id];
|
|
60
|
+
delete originalLayouts.current[id];
|
|
51
61
|
if (id === TEMPORARY_ITEM_ID) {
|
|
52
62
|
resetTemporaryLayout();
|
|
53
63
|
}
|
|
@@ -102,7 +112,55 @@ function useMemoStateContext(props) {
|
|
|
102
112
|
forceUpdateLayout((prev) => prev + 1);
|
|
103
113
|
}
|
|
104
114
|
}, []);
|
|
105
|
-
|
|
115
|
+
React.useMemo(() => {
|
|
116
|
+
const groups = props.groups;
|
|
117
|
+
const layout = props.layout;
|
|
118
|
+
const defaultProps = props.registerManager._gridLayout || {};
|
|
119
|
+
const nowrapGroups = {};
|
|
120
|
+
let hasNowrapGroups = false;
|
|
121
|
+
if (defaultProps.compactType === COMPACT_TYPE_HORIZONTAL_NOWRAP) {
|
|
122
|
+
nowrapGroups[DEFAULT_GROUP] = {
|
|
123
|
+
items: [],
|
|
124
|
+
leftSpace: defaultProps.cols,
|
|
125
|
+
};
|
|
126
|
+
hasNowrapGroups = true;
|
|
127
|
+
}
|
|
128
|
+
if (groups) {
|
|
129
|
+
groups.forEach((group) => {
|
|
130
|
+
var _a;
|
|
131
|
+
const resultProps = ((_a = group.gridProperties) === null || _a === void 0 ? void 0 : _a.call(group, defaultProps)) || {};
|
|
132
|
+
if (resultProps.compactType === COMPACT_TYPE_HORIZONTAL_NOWRAP) {
|
|
133
|
+
nowrapGroups[group.id] = {
|
|
134
|
+
items: [],
|
|
135
|
+
leftSpace: resultProps.cols,
|
|
136
|
+
};
|
|
137
|
+
hasNowrapGroups = true;
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
if (hasNowrapGroups) {
|
|
142
|
+
layout.forEach((item) => {
|
|
143
|
+
const widgetId = item.i;
|
|
144
|
+
const parentId = item.parent || DEFAULT_GROUP;
|
|
145
|
+
if (nowrapGroups[parentId]) {
|
|
146
|
+
// Collecting nowrap elements
|
|
147
|
+
nowrapGroups[parentId].items.push(item);
|
|
148
|
+
nowrapGroups[parentId].leftSpace -= item.w;
|
|
149
|
+
}
|
|
150
|
+
else if (nowrapAdjustedLayouts.current[widgetId]) {
|
|
151
|
+
// If element is not in horizontal-nowrap cleaning up and reverting adjustLayout values
|
|
152
|
+
delete nowrapAdjustedLayouts.current[widgetId];
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
Object.entries(nowrapGroups).forEach(([, { items, leftSpace }]) => {
|
|
156
|
+
items.forEach((item) => {
|
|
157
|
+
// setting maxW with adjustLayout fields and saving previous
|
|
158
|
+
nowrapAdjustedLayouts.current[item.i] = item.w + leftSpace;
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}, [props.registerManager, props.groups, props.layout]);
|
|
163
|
+
const itemsParams = useDeepEqualMemo(() => getItemsParams({
|
|
106
164
|
defaultGlobalParams: props.defaultGlobalParams,
|
|
107
165
|
globalParams: props.globalParams,
|
|
108
166
|
config: props.config,
|
|
@@ -115,7 +173,7 @@ function useMemoStateContext(props) {
|
|
|
115
173
|
props.itemsStateAndParams,
|
|
116
174
|
props.registerManager,
|
|
117
175
|
]);
|
|
118
|
-
const itemsState =
|
|
176
|
+
const itemsState = useDeepEqualMemo(() => getItemsState({
|
|
119
177
|
config: props.config,
|
|
120
178
|
itemsStateAndParams: props.itemsStateAndParams,
|
|
121
179
|
}), [props.config, props.itemsStateAndParams]);
|
|
@@ -130,12 +188,31 @@ function useMemoStateContext(props) {
|
|
|
130
188
|
.filter(Boolean);
|
|
131
189
|
}, []);
|
|
132
190
|
const resultLayout = React.useMemo(() => {
|
|
191
|
+
const adjusted = adjustedLayouts.current;
|
|
192
|
+
const original = originalLayouts.current;
|
|
193
|
+
const nowrapAdjust = nowrapAdjustedLayouts.current;
|
|
133
194
|
return props.layout.map((item) => {
|
|
134
|
-
|
|
135
|
-
|
|
195
|
+
const widgetId = item.i;
|
|
196
|
+
if (widgetId in adjusted || widgetId in nowrapAdjust) {
|
|
197
|
+
original[widgetId] = item;
|
|
198
|
+
// eslint-disable-next-line no-unused-vars
|
|
199
|
+
const _a = adjusted[widgetId] || item, { parent } = _a, adjustedItem = __rest(_a, ["parent"]);
|
|
200
|
+
adjustedItem.w = item.w;
|
|
201
|
+
adjustedItem.x = item.x;
|
|
202
|
+
adjustedItem.y = item.y;
|
|
203
|
+
if (item.parent) {
|
|
204
|
+
adjustedItem.parent = item.parent;
|
|
205
|
+
}
|
|
206
|
+
if (widgetId in nowrapAdjust) {
|
|
207
|
+
adjustedItem.maxW = nowrapAdjust[widgetId];
|
|
208
|
+
}
|
|
209
|
+
return adjustedItem;
|
|
136
210
|
}
|
|
137
211
|
else {
|
|
138
|
-
|
|
212
|
+
if (widgetId in original) {
|
|
213
|
+
delete original[widgetId];
|
|
214
|
+
}
|
|
215
|
+
return item;
|
|
139
216
|
}
|
|
140
217
|
});
|
|
141
218
|
}, [props.layout, layoutUpdateCounter]);
|
|
@@ -157,17 +234,31 @@ function useMemoStateContext(props) {
|
|
|
157
234
|
return null;
|
|
158
235
|
}
|
|
159
236
|
}, [dragProps, props.registerManager]);
|
|
160
|
-
const onDropDragOver = React.useCallback(() => {
|
|
237
|
+
const onDropDragOver = React.useCallback((_e, gridProps, groupLayout) => {
|
|
161
238
|
if (temporaryLayout) {
|
|
162
239
|
resetTemporaryLayout();
|
|
163
240
|
return false;
|
|
164
241
|
}
|
|
165
|
-
if (dragOverPlugin) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
242
|
+
if (!dragOverPlugin) {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
let maxW = gridProps.cols;
|
|
246
|
+
const { defaultLayout } = dragOverPlugin;
|
|
247
|
+
const maxH = Math.min(gridProps.maxRows || Infinity, defaultLayout.maxH || Infinity);
|
|
248
|
+
if (gridProps.compactType === COMPACT_TYPE_HORIZONTAL_NOWRAP) {
|
|
249
|
+
maxW = groupLayout.reduce((memo, item) => memo - item.w, gridProps.cols);
|
|
250
|
+
}
|
|
251
|
+
if (maxW === 0 ||
|
|
252
|
+
maxH === 0 ||
|
|
253
|
+
maxW < defaultLayout.minW ||
|
|
254
|
+
maxH < defaultLayout.minH) {
|
|
255
|
+
return false;
|
|
169
256
|
}
|
|
170
|
-
|
|
257
|
+
const { h = (defaultLayout === null || defaultLayout === void 0 ? void 0 : defaultLayout.h) || DEFAULT_WIDGET_HEIGHT, w = (defaultLayout === null || defaultLayout === void 0 ? void 0 : defaultLayout.w) || DEFAULT_WIDGET_WIDTH, } = dragProps.layout || {};
|
|
258
|
+
return {
|
|
259
|
+
h: maxH ? Math.min(h, maxH) : h,
|
|
260
|
+
w: maxW ? Math.min(w, maxW) : w,
|
|
261
|
+
};
|
|
171
262
|
}, [resetTemporaryLayout, temporaryLayout, dragOverPlugin, dragProps]);
|
|
172
263
|
const onDropProp = props.onDrop;
|
|
173
264
|
const onDrop = React.useCallback((newLayout, item) => {
|
|
@@ -181,16 +272,16 @@ function useMemoStateContext(props) {
|
|
|
181
272
|
onDropProp({
|
|
182
273
|
newLayout: newLayout.reduce((memo, l) => {
|
|
183
274
|
if (l.i !== item.i) {
|
|
184
|
-
memo.push(pick(l,
|
|
275
|
+
memo.push(pick(l, ITEM_PROPS));
|
|
185
276
|
}
|
|
186
277
|
return memo;
|
|
187
278
|
}, []),
|
|
188
|
-
itemLayout: pick(item,
|
|
279
|
+
itemLayout: pick(item, ITEM_PROPS),
|
|
189
280
|
commit: resetTemporaryLayout,
|
|
190
281
|
dragProps,
|
|
191
282
|
});
|
|
192
283
|
}, [dragProps, onDropProp, setTemporaryLayout, resetTemporaryLayout]);
|
|
193
|
-
|
|
284
|
+
const dashkitContextValue = React.useMemo(() => ({
|
|
194
285
|
layout: resultLayout,
|
|
195
286
|
temporaryLayout,
|
|
196
287
|
config: props.config,
|
|
@@ -206,10 +297,8 @@ function useMemoStateContext(props) {
|
|
|
206
297
|
itemsParams,
|
|
207
298
|
registerManager: props.registerManager,
|
|
208
299
|
onItemStateAndParamsChange,
|
|
209
|
-
removeItem: onItemRemove,
|
|
210
300
|
onDrop,
|
|
211
301
|
onDropDragOver,
|
|
212
|
-
editItem: props.onItemEdit,
|
|
213
302
|
layoutChange: onLayoutChange,
|
|
214
303
|
getItemsMeta,
|
|
215
304
|
reloadItems,
|
|
@@ -234,10 +323,8 @@ function useMemoStateContext(props) {
|
|
|
234
323
|
itemsParams,
|
|
235
324
|
props.registerManager,
|
|
236
325
|
onItemStateAndParamsChange,
|
|
237
|
-
onItemRemove,
|
|
238
326
|
onDrop,
|
|
239
327
|
onDropDragOver,
|
|
240
|
-
props.onItemEdit,
|
|
241
328
|
onLayoutChange,
|
|
242
329
|
getItemsMeta,
|
|
243
330
|
reloadItems,
|
|
@@ -248,12 +335,34 @@ function useMemoStateContext(props) {
|
|
|
248
335
|
outerDnDEnable,
|
|
249
336
|
dragOverPlugin,
|
|
250
337
|
]);
|
|
338
|
+
const overlayMenuItems = props.overlayMenuItems || props.registerManager.settings.menu;
|
|
339
|
+
const controlsContextValue = React.useMemo(() => ({
|
|
340
|
+
overlayControls: props.overlayControls,
|
|
341
|
+
context: props.context,
|
|
342
|
+
menu: overlayMenuItems,
|
|
343
|
+
itemsParams: itemsParams,
|
|
344
|
+
itemsState: itemsState,
|
|
345
|
+
editItem: props.onItemEdit,
|
|
346
|
+
removeItem: onItemRemove,
|
|
347
|
+
getLayoutItem: getLayoutItem,
|
|
348
|
+
}), [
|
|
349
|
+
itemsParams,
|
|
350
|
+
itemsState,
|
|
351
|
+
props.context,
|
|
352
|
+
props.onItemEdit,
|
|
353
|
+
onItemRemove,
|
|
354
|
+
props.overlayControls,
|
|
355
|
+
overlayMenuItems,
|
|
356
|
+
getLayoutItem,
|
|
357
|
+
]);
|
|
358
|
+
return { controlsContextValue, dashkitContextValue };
|
|
251
359
|
}
|
|
252
360
|
export function withContext(Component) {
|
|
253
361
|
const WithContext = (props) => {
|
|
254
|
-
const
|
|
255
|
-
return (React.createElement(DashKitContext.Provider, { value:
|
|
256
|
-
React.createElement(
|
|
362
|
+
const { dashkitContextValue, controlsContextValue } = useMemoStateContext(props);
|
|
363
|
+
return (React.createElement(DashKitContext.Provider, { value: dashkitContextValue },
|
|
364
|
+
React.createElement(DashkitOvelayControlsContext.Provider, { value: controlsContextValue },
|
|
365
|
+
React.createElement(Component, null))));
|
|
257
366
|
};
|
|
258
367
|
WithContext.displayName = `withContext(${Component.displayName || Component.name || 'Component'}`;
|
|
259
368
|
return WithContext;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import isEqual from 'lodash/isEqual';
|
|
3
|
+
export const useDeepEqualMemo = (predicate, deps) => {
|
|
4
|
+
const previousValueRef = React.useRef({});
|
|
5
|
+
return React.useMemo(() => {
|
|
6
|
+
const value = predicate();
|
|
7
|
+
if (!isEqual(previousValueRef.current, value)) {
|
|
8
|
+
previousValueRef.current = value;
|
|
9
|
+
}
|
|
10
|
+
return previousValueRef.current;
|
|
11
|
+
}, [previousValueRef, ...deps]);
|
|
12
|
+
};
|
|
@@ -19,7 +19,9 @@ export interface DashkitGroupRenderProps {
|
|
|
19
19
|
items: ConfigItem[];
|
|
20
20
|
layout: ConfigLayout[];
|
|
21
21
|
}
|
|
22
|
-
export type ReactGridLayoutProps = Omit<ReactGridLayout.ReactGridLayoutProps, 'children' | 'innerRef' | 'key' | 'layout' | 'isDraggable' | 'isResizable' | 'onDragStart' | 'onDragStop' | 'onResizeStart' | 'onResizeStop' | 'draggableHandle' | 'isDroppable' | 'onDropDragOver' | 'onDrop' | 'draggableCancel'
|
|
22
|
+
export type ReactGridLayoutProps = Omit<ReactGridLayout.ReactGridLayoutProps, 'children' | 'compactType' | 'innerRef' | 'key' | 'layout' | 'isDraggable' | 'isResizable' | 'onDragStart' | 'onDragStop' | 'onResizeStart' | 'onResizeStop' | 'draggableHandle' | 'isDroppable' | 'onDropDragOver' | 'onDrop' | 'draggableCancel'> & {
|
|
23
|
+
compactType?: ReactGridLayout.ReactGridLayoutProps['compactType'] | 'horizontal-nowrap';
|
|
24
|
+
};
|
|
23
25
|
export interface DashKitGroup {
|
|
24
26
|
id?: string;
|
|
25
27
|
render?: (id: string, children: React.ReactNode, props: DashkitGroupRenderProps) => React.ReactNode;
|