From e78930009014df28886ee04605f9ebff47e75980 Mon Sep 17 00:00:00 2001 From: Carl Fuchs Date: Mon, 6 Feb 2023 16:45:34 +0100 Subject: [PATCH] Implemtation in progress, UIX working , replacing in ContainerOperations.ts working but not properly need fix --- src/Components/Bar/Bar.tsx | 3 +- src/Components/Components/Components.tsx | 20 ++- .../Editor/Actions/ContainerOperations.ts | 118 ++++++++---------- .../Editor/Actions/ContextMenuActions.ts | 14 ++- src/Components/Editor/Editor.tsx | 42 +++++-- src/Components/Menu/Menu.tsx | 5 +- src/Components/UI/UI.tsx | 24 ++-- src/Components/Viewer/Viewer.tsx | 6 +- src/Interfaces/IReplaceContainer.ts | 5 + 9 files changed, 139 insertions(+), 98 deletions(-) create mode 100644 src/Interfaces/IReplaceContainer.ts diff --git a/src/Components/Bar/Bar.tsx b/src/Components/Bar/Bar.tsx index 8f28635..a693fc2 100644 --- a/src/Components/Bar/Bar.tsx +++ b/src/Components/Bar/Bar.tsx @@ -17,6 +17,7 @@ import { BarIcon } from './BarIcon'; import { Text } from '../Text/Text'; interface IBarProps { + className: string isComponentsOpen: boolean isSymbolsOpen: boolean isHistoryOpen: boolean @@ -33,7 +34,7 @@ export const BAR_WIDTH = 64; // 4rem export function Bar(props: IBarProps): JSX.Element { return ( -
+
> buttonOnClick: (type: string) => void } @@ -63,7 +66,7 @@ export function Components(props: IComponentsProps): JSX.Element { disabled = config.Blacklist?.find(type => type === componentOption.Type) !== undefined ?? false; } - if (componentOption.Category !== props.replaceableCategoryName) { + if (props.replaceContainer.isReplacing && componentOption.Category !== props.replaceContainer.category) { disabled = true; } @@ -101,6 +104,15 @@ export function Components(props: IComponentsProps): JSX.Element { return (
+ {props.replaceContainer.isReplacing && + }
); -}; +} diff --git a/src/Components/Editor/Actions/ContainerOperations.ts b/src/Components/Editor/Actions/ContainerOperations.ts index dc5bfb5..57a0d72 100644 --- a/src/Components/Editor/Actions/ContainerOperations.ts +++ b/src/Components/Editor/Actions/ContainerOperations.ts @@ -8,7 +8,8 @@ import Swal from 'sweetalert2'; import { PropertyType } from '../../../Enums/PropertyType'; import { TransformX, TransformY } from '../../../utils/svg'; import { Orientation } from '../../../Enums/Orientation'; -import { AddContainers, AddContainerToSelectedContainer } from './AddContainer'; +import { AddContainerToSelectedContainer } from './AddContainer'; +import { IConfiguration } from '../../../Interfaces/IConfiguration'; /** * Select a container @@ -113,64 +114,44 @@ export function DeleteContainer( /** * Replace a container * @param containerId containerId of the container to delete + * @param newContainerId + * @param configuration * @param fullHistory History of the editor * @param historyCurrentStep Current step * @returns New history */ -// export function ReplaceByContainer( -// containerId: string, -// newContainerId: string, -// fullHistory: IHistoryState[], -// historyCurrentStep: number -// ): IHistoryState[] { -// const history = GetCurrentHistory(fullHistory, historyCurrentStep); -// const current = history[history.length - 1]; -// -// -// const containers = structuredClone(current.containers); -// const container = FindContainerById(containers, containerId); -// if (container === undefined) { -// throw new Error(`[ReplaceContainer] Tried to delete a container that is not present in the main container: ${containerId}`); -// } -// /// -// const parent = FindContainerById(containers, container.properties.parentId); -// if (parent === undefined || parent === null) { -// throw new Error('[ReplaceContainer] Cannot replace a container that does not exists'); -// } -// -// const index = parent.children.indexOf(container.properties.id); -// -// const newHistoryAfterDelete = DeleteContainer( -// container.properties.id, -// history, -// historyCurrentStep -// ); -// -// const newContainer = FindContainerById(containers, container.properties.parentId); -// -// AddContainerToSelectedContainer( -// ne, -// selected, -// configuration, -// history, -// historyCurrentStep -// ); -// -// -// /// / -// -// -// history.push({ -// lastAction: `Replace ${containerId} By InsertnewId`, -// mainContainer: current.mainContainer, -// containers, -// newContainerId, -// typeCounters: Object.assign({}, current.typeCounters), -// symbols: newSymbols, -// selectedSymbolId: current.selectedSymbolId -// }); -// return history; -// } +export function ReplaceByContainer( + containerId: string, + newContainerId: string, + configuration: IConfiguration, + fullHistory: IHistoryState[], + historyCurrentStep: number +): IHistoryState[] { + const history = GetCurrentHistory(fullHistory, historyCurrentStep); + const current = history[history.length - 1]; + + const historyDelete = DeleteContainer(containerId, fullHistory, historyCurrentStep); + const currentDelete = historyDelete[historyDelete.length - 1]; + const selectedContainer = FindContainerById(currentDelete.containers, currentDelete.selectedContainerId); + if (selectedContainer != null) { + const historyAdd = AddContainerToSelectedContainer(newContainerId, selectedContainer, configuration, fullHistory, historyCurrentStep); + + const currentAdd = historyAdd[historyAdd.length - 1]; + + fullHistory.push({ + lastAction: `Replace ${containerId} by ${newContainerId}`, + mainContainer: currentAdd.mainContainer, + containers: currentAdd.containers, + selectedContainerId: currentAdd.selectedContainerId, + typeCounters: Object.assign({}, currentAdd.typeCounters), + symbols: current.symbols, + selectedSymbolId: current.selectedSymbolId + }); + + return fullHistory; + } + return history; +} /** * Returns the next container that will be selected @@ -178,7 +159,7 @@ export function DeleteContainer( * If the selected container is removed, select the sibling after, * If there is no sibling, select the parent, * - * @param mainContainerClone Main container + * @param containers * @param selectedContainerId Current selected container * @param parent Parent of the selected/deleted container * @param index Index of the selected/deleted container @@ -190,11 +171,10 @@ function GetSelectedContainerOnDelete( parent: IContainerModel, index: number ): string { - const newSelectedContainerId = FindContainerById(containers, selectedContainerId)?.properties.id ?? - parent.children.at(index) ?? - parent.children.at(index - 1) ?? - parent.properties.id; - return newSelectedContainerId; + return FindContainerById(containers, selectedContainerId)?.properties.id ?? + parent.children.at(index) ?? + parent.children.at(index - 1) ?? + parent.properties.id; } /** @@ -220,11 +200,15 @@ function UnlinkContainerFromSymbols( } /** - * Handled the property change event in the properties form - * @param key Property name - * @param value New value of the property - * @returns void - */ + * Handled the property change event in the properties form + * @param key Property name + * @param value New value of the property + * @param type + * @param selected + * @param fullHistory + * @param historyCurrentStep + * @returns void + */ export function OnPropertyChange( key: string, value: string | number | boolean | number[], @@ -264,6 +248,7 @@ export function OnPropertyChange( /** * Sort the parent children by x + * @param containers * @param parent The clone used for the sort * @returns void */ @@ -328,6 +313,7 @@ export function SortChildren( /** * Set the container with properties and behaviors (mutate) + * @param containers * @param container Container to update * @param key Key of the property to update * @param value Value of the property to update diff --git a/src/Components/Editor/Actions/ContextMenuActions.ts b/src/Components/Editor/Actions/ContextMenuActions.ts index 2781d1e..5b2a546 100644 --- a/src/Components/Editor/Actions/ContextMenuActions.ts +++ b/src/Components/Editor/Actions/ContextMenuActions.ts @@ -16,6 +16,7 @@ import { AddContainers } from './AddContainer'; import { DeleteContainer } from './ContainerOperations'; import { DeleteSymbol } from './SymbolOperations'; import { Text } from '../../Text/Text'; +import { IReplaceContainer } from '../../../Interfaces/IReplaceContainer'; export function InitActions( menuActions: Map, @@ -23,7 +24,8 @@ export function InitActions( history: IHistoryState[], historyCurrentStep: number, setNewHistory: (newHistory: IHistoryState[]) => void, - setHistoryCurrentStep: Dispatch> + setHistoryCurrentStep: Dispatch>, + setIsReplacingContainer: Dispatch> ): void { menuActions.set( '', @@ -60,8 +62,14 @@ export function InitActions( title: Text({ textId: '@ReplaceByContainerTitle' }), shortcut: 'R', action: (target: HTMLElement) => { - const id = target.id; - console.log('replace'); + const targetContainer = FindContainerById(history[historyCurrentStep].containers, target.id); + const targetAvailableContainer = configuration.AvailableContainers.find((availableContainer) => availableContainer.Type === targetContainer?.properties.type); + + if (targetAvailableContainer === undefined) { + return; + } + + setIsReplacingContainer({ isReplacing: true, id: target.id, category: targetAvailableContainer.Category }); } }, { text: Text({ textId: '@DeleteContainer' }), diff --git a/src/Components/Editor/Editor.tsx b/src/Components/Editor/Editor.tsx index 372ab06..6644bed 100644 --- a/src/Components/Editor/Editor.tsx +++ b/src/Components/Editor/Editor.tsx @@ -3,7 +3,7 @@ import './Editor.scss'; import { IConfiguration } from '../../Interfaces/IConfiguration'; import { IHistoryState } from '../../Interfaces/IHistoryState'; import { UI } from '../UI/UI'; -import { SelectContainer, DeleteContainer, OnPropertyChange } from './Actions/ContainerOperations'; +import { SelectContainer, DeleteContainer, OnPropertyChange, ReplaceByContainer } from './Actions/ContainerOperations'; import { SaveEditorAsJSON, SaveEditorAsSVG } from './Actions/Save'; import { OnKey } from './Actions/Shortcuts'; import { UseCustomEvents, UseEditorListener } from '../../Events/EditorEvents'; @@ -13,6 +13,7 @@ import { FindContainerById } from '../../utils/itertools'; import { Menu } from '../Menu/Menu'; import { InitActions } from './Actions/ContextMenuActions'; import { AddContainerToSelectedContainer, AddContainer } from './Actions/AddContainer'; +import { IReplaceContainer } from '../../Interfaces/IReplaceContainer'; interface IEditorProps { root: Element | Document @@ -66,6 +67,8 @@ export function Editor(props: IEditorProps): JSX.Element { // States const [history, setHistory] = React.useState(structuredClone(props.history)); const [historyCurrentStep, setHistoryCurrentStep] = React.useState(props.historyCurrentStep); + const [replaceContainer, setReplaceContainer] = React.useState({ isReplacing: false, id: undefined, category: undefined }); + const editorRef = useRef(null); const setNewHistory = UseNewHistoryState(setHistory, setHistoryCurrentStep); @@ -104,7 +107,8 @@ export function Editor(props: IEditorProps): JSX.Element { history, historyCurrentStep, setNewHistory, - setHistoryCurrentStep + setHistoryCurrentStep, + setReplaceContainer ); // Render @@ -113,7 +117,7 @@ export function Editor(props: IEditorProps): JSX.Element { const selected = FindContainerById(current.containers, current.selectedContainerId); return ( -
+
{ + addOrReplaceContainer={(type) => { if (selected === null || selected === undefined) { return; } - - setNewHistory(AddContainerToSelectedContainer( - type, - selected, - configuration, - history, - historyCurrentStep - )); + if (replaceContainer.isReplacing && replaceContainer.id !== undefined) { + const newHistory = ReplaceByContainer( + replaceContainer.id, + type, + configuration, + history, + historyCurrentStep + ); + setReplaceContainer({ isReplacing: false, id: undefined, category: undefined }); + setNewHistory(newHistory); + } else { + setNewHistory(AddContainerToSelectedContainer( + type, + selected, + configuration, + history, + historyCurrentStep + )); + } }} addContainerAt={(index, type, parent) => setNewHistory( AddContainer( @@ -194,9 +209,10 @@ export function Editor(props: IEditorProps): JSX.Element { )} saveEditorAsSVG={() => SaveEditorAsSVG()} loadState={(move) => setHistoryCurrentStep(move)} - /> + replaceContainer ={replaceContainer} setReplaceContainer={setReplaceContainer}/> editorRef.current} + configuration={configuration} actions={menuActions} className="z-30 transition-opacity rounded bg-slate-200 drop-shadow-xl" /> diff --git a/src/Components/Menu/Menu.tsx b/src/Components/Menu/Menu.tsx index a0fbc03..c8da101 100644 --- a/src/Components/Menu/Menu.tsx +++ b/src/Components/Menu/Menu.tsx @@ -2,11 +2,13 @@ import useSize from '@react-hook/size'; import * as React from 'react'; import { IPoint } from '../../Interfaces/IPoint'; import { MenuItem } from './MenuItem'; +import { IConfiguration } from '../../Interfaces/IConfiguration'; interface IMenuProps { getListener: () => HTMLElement | null actions: Map className?: string + configuration: IConfiguration } export interface IMenuAction { @@ -21,6 +23,7 @@ export interface IMenuAction { /** function to be called on button click */ action: (target: HTMLElement) => void + } function UseMouseEvents( @@ -139,7 +142,7 @@ function AddClassSpecificActions( onClick={() => action.action(target)} />); }); children.push(
); - }; + } return count; } diff --git a/src/Components/UI/UI.tsx b/src/Components/UI/UI.tsx index 5e7a6a4..cab0e17 100644 --- a/src/Components/UI/UI.tsx +++ b/src/Components/UI/UI.tsx @@ -17,13 +17,15 @@ import { FindContainerById } from '../../utils/itertools'; import { IEditorState } from '../../Interfaces/IEditorState'; import { GetCurrentHistoryState } from '../Editor/Editor'; import { Text } from '../Text/Text'; +import { IReplaceContainer } from '../../Interfaces/IReplaceContainer'; +import { Dispatch } from 'react'; export interface IUIProps { editorState: IEditorState selectContainer: (containerId: string) => void deleteContainer: (containerId: string) => void onPropertyChange: (key: string, value: string | number | boolean | number[], type?: PropertyType) => void - addContainer: (type: string) => void + addOrReplaceContainer: (type: string) => void addContainerAt: (index: number, type: string, parent: string) => void addSymbol: (type: string) => void onSymbolPropertyChange: (key: string, value: string | number | boolean) => void @@ -32,6 +34,9 @@ export interface IUIProps { saveEditorAsJSON: () => void saveEditorAsSVG: () => void loadState: (move: number) => void + replaceContainer: IReplaceContainer + setReplaceContainer: Dispatch> + } export enum SidebarType { @@ -57,8 +62,9 @@ function UseSetOrToggleSidebar( }; } -export function UI({ editorState, ...methods }: IUIProps): JSX.Element { +export function UI({ editorState, replaceContainer, setReplaceContainer, ...methods }: IUIProps): JSX.Element { const [selectedSidebar, setSelectedSidebar] = React.useState(SidebarType.Components); + const [messages, setMessages] = React.useState([]); const current = GetCurrentHistoryState(editorState.history, editorState.historyCurrentStep); const configuration = editorState.configuration; @@ -98,15 +104,13 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element { case SidebarType.Components: leftSidebarTitle = Text({ textId: '@Components' }); - const AllowedReplaceCategory = undefined; - leftChildren = ; + buttonOnClick={methods.addOrReplaceContainer} + replaceContainer={replaceContainer} + setReplaceContainer={setReplaceContainer}/>; rightSidebarTitle = Text({ textId: '@Elements' }); rightChildren = { rightChildren } diff --git a/src/Components/Viewer/Viewer.tsx b/src/Components/Viewer/Viewer.tsx index ba0560b..474ac57 100644 --- a/src/Components/Viewer/Viewer.tsx +++ b/src/Components/Viewer/Viewer.tsx @@ -12,6 +12,7 @@ import { SVG } from '../SVG/SVG'; import { RenderSymbol } from '../Canvas/Symbol'; interface IViewerProps { + className: string isLeftSidebarOpen: boolean isRightSidebarOpen: boolean current: IHistoryState @@ -73,6 +74,7 @@ function UseSVGAutoResizerOnSidebar( } export function Viewer({ + className, isLeftSidebarOpen, isRightSidebarOpen, current, selectedContainer, @@ -160,7 +162,7 @@ export function Viewer({ return ( @@ -169,7 +171,7 @@ export function Viewer({ return (