diff --git a/src/components/ItemControls/NodeControls/NodeControls.tsx b/src/components/ItemControls/NodeControls/NodeControls.tsx index db1f8a2a..eb783e09 100644 --- a/src/components/ItemControls/NodeControls/NodeControls.tsx +++ b/src/components/ItemControls/NodeControls/NodeControls.tsx @@ -28,7 +28,7 @@ type Mode = keyof typeof ModeOptions; export const NodeControls = ({ id }: Props) => { const [mode, setMode] = useState('SETTINGS'); - const { updateModelItem, updateViewItem, deleteViewItem } = useScene(); + const { updateModelItem, updateViewItem, deleteViewItem, duplicateViewItem } = useScene(); const uiStateActions = useUiStateStore((state) => { return state.actions; }); @@ -101,6 +101,9 @@ export const NodeControls = ({ id }: Props) => { uiStateActions.setItemControls(null); deleteViewItem(viewItem.id); }} + onDuplicated={() => { + duplicateViewItem(viewItem); + }} /> )} {mode === 'CHANGE_ICON' && ( diff --git a/src/components/ItemControls/NodeControls/NodeSettings/NodeSettings.tsx b/src/components/ItemControls/NodeControls/NodeSettings/NodeSettings.tsx index 54131192..2350445d 100644 --- a/src/components/ItemControls/NodeControls/NodeSettings/NodeSettings.tsx +++ b/src/components/ItemControls/NodeControls/NodeSettings/NodeSettings.tsx @@ -1,9 +1,10 @@ import React from 'react'; -import { Slider, Box, TextField } from '@mui/material'; +import { Slider, TextField, Grid } from '@mui/material'; import { ModelItem, ViewItem } from 'src/types'; import { MarkdownEditor } from 'src/components/MarkdownEditor/MarkdownEditor'; import { useModelItem } from 'src/hooks/useModelItem'; import { DeleteButton } from '../../components/DeleteButton'; +import { DuplicateButton } from '../../components/DuplicateButton'; import { Section } from '../../components/Section'; export type NodeUpdates = { @@ -16,13 +17,15 @@ interface Props { onModelItemUpdated: (updates: Partial) => void; onViewItemUpdated: (updates: Partial) => void; onDeleted: () => void; + onDuplicated: () => void; } export const NodeSettings = ({ node, onModelItemUpdated, onViewItemUpdated, - onDeleted + onDeleted, + onDuplicated }: Props) => { const modelItem = useModelItem(node.id); @@ -61,11 +64,14 @@ export const NodeSettings = ({ /> )} -
- + + - -
+ + + + + ); }; diff --git a/src/components/ItemControls/components/DuplicateButton.tsx b/src/components/ItemControls/components/DuplicateButton.tsx new file mode 100644 index 00000000..9804d3dc --- /dev/null +++ b/src/components/ItemControls/components/DuplicateButton.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { DeleteOutlined as DeleteIcon } from '@mui/icons-material'; +import { ContentCopy as DuplicateIcon } from '@mui/icons-material'; +import { Button } from '@mui/material'; + +interface Props { + onClick: () => void; +} + +export const DuplicateButton = ({ onClick }: Props) => { + return ( + + ); +}; diff --git a/src/hooks/useScene.ts b/src/hooks/useScene.ts index 85795f1d..9bd3f045 100644 --- a/src/hooks/useScene.ts +++ b/src/hooks/useScene.ts @@ -129,6 +129,18 @@ export const useScene = () => { [getState, setState, currentViewId] ); + const duplicateViewItem = useCallback( + (newViewItem: ViewItem) => { + const newState = reducers.view({ + action: 'CREATE_VIEWITEM', + payload: newViewItem, + ctx: { viewId: currentViewId, state: getState() } + }); + setState(newState); + }, + [getState, setState, currentViewId] + ) + const updateViewItem = useCallback( (id: string, updates: Partial) => { const newState = reducers.view({ @@ -287,6 +299,7 @@ export const useScene = () => { updateViewItem, deleteViewItem, createConnector, + duplicateViewItem, updateConnector, deleteConnector, createTextBox,