diff --git a/src/components/ActionPanel/ActionPanel.tsx b/src/components/ActionPanel/ActionPanel.tsx index a908a81..8a1b60b 100644 --- a/src/components/ActionPanel/ActionPanel.tsx +++ b/src/components/ActionPanel/ActionPanel.tsx @@ -5,20 +5,10 @@ import {CSSTransition} from 'react-transition-group'; import {DashKitDnDContext} from '../../context/DashKitContext'; import {cn} from '../../utils/cn'; -import {ActionPanelProps} from './types'; +import {ActionPanelItem, ActionPanelProps} from './types'; import './ActionPanel.scss'; -export type ActionPanelItem = { - id: string; - icon: React.ReactNode; - title: string; - onClick?: () => void; - className?: string; - qa?: string; - pluginType?: string; -}; - type DndProps = | {} | { @@ -35,9 +25,9 @@ export const ActionPanelItemContainer = ({item}: {item: ActionPanelItem}) => { const onDragStart = React.useCallback( (e: React.DragEvent) => { - dragContext?.onDragStart(e, item.pluginType); + dragContext?.onDragStart(e, item.dragProps); }, - [dragContext?.onDragStart, item.pluginType], + [dragContext?.onDragStart, item.dragProps], ); const onDragEnd = React.useCallback>( @@ -49,7 +39,7 @@ export const ActionPanelItemContainer = ({item}: {item: ActionPanelItem}) => { let dndProps: DndProps = {}; - if (item.pluginType) { + if (item.dragProps) { dndProps = { draggable: true, unselectable: 'on', diff --git a/src/components/ActionPanel/types.ts b/src/components/ActionPanel/types.ts index 3447c51..e853cba 100644 --- a/src/components/ActionPanel/types.ts +++ b/src/components/ActionPanel/types.ts @@ -1,5 +1,7 @@ import React from 'react'; +import type {DragProps} from '../../shared'; + export type ActionPanelItem = { id: string; icon: React.ReactNode; @@ -7,7 +9,7 @@ export type ActionPanelItem = { onClick?: () => void; className?: string; qa?: string; - pluginType?: string; + dragProps?: DragProps; }; export type ActionPanelProps = { diff --git a/src/components/DashKit/DashKit.tsx b/src/components/DashKit/DashKit.tsx index 2e09108..0ebeca1 100644 --- a/src/components/DashKit/DashKit.tsx +++ b/src/components/DashKit/DashKit.tsx @@ -7,6 +7,7 @@ import type { Config, ConfigItem, ConfigLayout, + DragProps, GlobalParams, ItemsStateAndParams, } from '../../shared'; @@ -36,7 +37,7 @@ interface DashKitDefaultProps { onChange: (data: {config: Config; itemsStateAndParams: ItemsStateAndParams}) => void; onDrop: (dropProps: { commit: () => void; - pluginType: string; + dragProps: DragProps; itemLayout: ConfigLayout; newLayout: ConfigLayout[]; }) => void; diff --git a/src/components/DashKit/__stories__/DashKitDnDShowcase.tsx b/src/components/DashKit/__stories__/DashKitDnDShowcase.tsx index 7207848..15eb927 100644 --- a/src/components/DashKit/__stories__/DashKitDnDShowcase.tsx +++ b/src/components/DashKit/__stories__/DashKitDnDShowcase.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import {ChartColumn, Heading, Sliders, TextAlignLeft} from '@gravity-ui/icons'; +import {ChartColumn, Copy, Heading, Sliders, TextAlignLeft} from '@gravity-ui/icons'; import {Icon} from '@gravity-ui/uikit'; import {ActionPanel, DashKit, DashKitDnDWrapper, DashKitProps} from '../../..'; @@ -49,7 +49,9 @@ export const DashKitDnDShowcase: React.FC = () => { title: 'Chart', className: 'test', qa: 'chart', - pluginType: 'custom', + dragProps: { + type: 'custom', + }, onClick, }, { @@ -57,21 +59,38 @@ export const DashKitDnDShowcase: React.FC = () => { icon: , title: 'Selector', qa: 'selector', - pluginType: 'custom', + dragProps: { + type: 'custom', + }, onClick, }, { id: 'text', icon: , title: 'Text', - pluginType: 'text', + dragProps: { + type: 'text', + }, onClick, }, { id: 'header', icon: , title: 'Header', - pluginType: 'title', + dragProps: { + type: 'title', + }, + onClick, + }, + { + id: 'custom', + icon: , + title: 'Custom', + dragProps: { + type: 'title', + h: 10, + w: 36, + }, onClick, }, ], @@ -86,13 +105,14 @@ export const DashKitDnDShowcase: React.FC = () => { const onDrop = React.useCallback>( (dropProps) => { let data = null; - if (dropProps.pluginType === 'custom') { + const type = dropProps.dragProps?.type; + if (type === 'custom') { data = {}; } else { const text = prompt('Enter text'); if (text) { data = - dropProps.pluginType === 'title' + type === 'title' ? { size: 'm', text, @@ -106,8 +126,8 @@ export const DashKitDnDShowcase: React.FC = () => { const newConfig = DashKit.setItem({ item: { data, + type, namespace: 'default', - type: dropProps.pluginType, layout: dropProps.itemLayout, }, config, diff --git a/src/components/DashKitDnDWrapper/DashKitDnDWrapper.tsx b/src/components/DashKitDnDWrapper/DashKitDnDWrapper.tsx index a09d280..d4931ef 100644 --- a/src/components/DashKitDnDWrapper/DashKitDnDWrapper.tsx +++ b/src/components/DashKitDnDWrapper/DashKitDnDWrapper.tsx @@ -1,35 +1,36 @@ import React from 'react'; import {DashKitDnDContext} from '../../context/DashKitContext'; +import type {DragProps} from '../../shared'; type DashKitDnDWrapperProps = { children: React.ReactElement; }; export const DashKitDnDWrapper: React.FC = (props) => { - const [dragPluginType, setDragPluginType] = React.useState(null); + const [dragProps, setDragProps] = React.useState(null); const onDragStart = React.useCallback( - (_: React.DragEvent, type: string) => { - setDragPluginType(type); + (_: React.DragEvent, currentProps: DragProps) => { + setDragProps(currentProps); }, - [setDragPluginType], + [setDragProps], ); const onDragEnd = React.useCallback( (_: React.DragEvent) => { - setDragPluginType(null); + setDragProps(null); }, - [setDragPluginType], + [setDragProps], ); const contextValue = React.useMemo(() => { return { - dragPluginType, + dragProps, onDragStart, onDragEnd, }; - }, [dragPluginType, onDragStart, onDragEnd]); + }, [dragProps, onDragStart, onDragEnd]); return ( diff --git a/src/components/GridLayout/GridLayout.js b/src/components/GridLayout/GridLayout.js index f5bea82..e4a67a5 100644 --- a/src/components/GridLayout/GridLayout.js +++ b/src/components/GridLayout/GridLayout.js @@ -25,12 +25,13 @@ class DragOverLayout extends ReactGridLayout { }; processGridItem(child, isDroppingItem) { + const gridItem = super.processGridItem(child, isDroppingItem); + if (isDroppingItem) { // Drop item from outside gets 0,0 droppingPosition // centering cursor on newly creted grid item // And cause grid-layout using it's own GridItem to make it look // like overlay adding className - const gridItem = super.processGridItem(child, isDroppingItem); if (!gridItem) return null; const {props} = gridItem; @@ -49,7 +50,7 @@ class DragOverLayout extends ReactGridLayout { }); } - return super.processGridItem(child, isDroppingItem); + return gridItem; } } diff --git a/src/hocs/withContext.js b/src/hocs/withContext.js index 335424e..eab5a01 100644 --- a/src/hocs/withContext.js +++ b/src/hocs/withContext.js @@ -7,6 +7,9 @@ import {DashKitContext, DashKitDnDContext} from '../context/DashKitContext'; import {getItemsParams, getItemsState} from '../shared'; import {UpdateManager} from '../utils'; +const DEFAULT_HEIGHT = 3; +const DEFAULT_WIDTH = 3; + function useMemoStateContext(props) { // так как мы не хотим хранить параметры виджета с активированной автовысотой в сторе и на сервере, актуальный // (видимый юзером в конкретный момент времени) лэйаут (массив объектов с данными о ширине, высоте, @@ -202,12 +205,12 @@ function useMemoStateContext(props) { pluginsRefs.forEach((ref) => ref && ref.reload && ref.reload(data)); }, []); - const dndPluginType = dndContext?.dragPluginType; + const dragProps = dndContext?.dragProps; const dragOverPlugin = React.useMemo(() => { - const pluginType = dndPluginType; + if (!dragProps && temporaryLayout === null) return null; - if (pluginType === null && temporaryLayout === null) return null; + const pluginType = dragProps.type; if (props.registerManager.check(pluginType)) { return props.registerManager.getItem(pluginType); @@ -216,7 +219,7 @@ function useMemoStateContext(props) { console.error(`Uknown pluginType: ${pluginType}`); return null; } - }, [dndPluginType, props.registerManager, temporaryLayout]); + }, [dragProps, temporaryLayout, props.registerManager]); const onDropDragOver = React.useCallback(() => { if (temporaryLayout) { @@ -224,17 +227,24 @@ function useMemoStateContext(props) { return false; } - if (dragOverPlugin?.defaultLayout) { - const {h = 3, w = 3} = dragOverPlugin.defaultLayout; + if (dragOverPlugin) { + const {defaultLayout} = dragOverPlugin; + const {h = defaultLayout?.h || DEFAULT_HEIGHT, w = defaultLayout?.w || DEFAULT_WIDTH} = + dragProps; + return {h, w}; } return false; - }, [resetTemporaryLayout, temporaryLayout, dragOverPlugin]); + }, [resetTemporaryLayout, temporaryLayout, dragOverPlugin, dragProps]); const onDropProp = props.onDrop; const onDrop = React.useCallback( (newLayout, item) => { + if (!dragProps) { + return; + } + setTemporaryLayout(newLayout); const {i, w, h, x, y} = item; @@ -246,11 +256,11 @@ function useMemoStateContext(props) { return memo; }, []), itemLayout: {w, h, x, y}, - pluginType: dndPluginType, commit: resetTemporaryLayout, + dragProps, }); }, - [dndPluginType, onDropProp, resetTemporaryLayout], + [dragProps, onDropProp, resetTemporaryLayout], ); return React.useMemo( diff --git a/src/shared/types/config.ts b/src/shared/types/config.ts index 6c06985..437253e 100644 --- a/src/shared/types/config.ts +++ b/src/shared/types/config.ts @@ -1,5 +1,11 @@ import {StringParams} from './common'; +export type DragProps = { + type: string; + w?: number; + h?: number; +}; + export interface ConfigLayout { i: string; h: number;