Merge branch 'master' into master.7306.symbolY

# Conflicts:
#	src/Components/Canvas/Symbol.ts
#	src/Components/SVG/Elements/DimensionLayer.tsx
#	src/Components/SVG/Elements/SelectorSymbol/SelectorSymbol.tsx
This commit is contained in:
Carl Fuchs 2023-02-20 10:21:54 +01:00
commit d72c2266f3
52 changed files with 1967 additions and 913 deletions

View file

@ -8,6 +8,8 @@ import Swal from 'sweetalert2';
import { PropertyType } from '../../../Enums/PropertyType';
import { TransformX, TransformY } from '../../../utils/svg';
import { Orientation } from '../../../Enums/Orientation';
import { AddContainers } from './AddContainer';
import { IConfiguration } from '../../../Interfaces/IConfiguration';
/**
* Select a container
@ -133,6 +135,58 @@ export function DeleteContainer(
return history;
}
/**
* 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,
configuration: IConfiguration,
fullHistory: IHistoryState[],
historyCurrentStep: number
): IHistoryState[] {
const history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1];
const containerToReplace = FindContainerById(current.containers, containerId);
if (containerToReplace === undefined) {
return history;
}
const containerParent = FindContainerById(current.containers, containerToReplace.properties.parentId);
if (containerParent === undefined) {
return history;
}
const historyAdd = AddContainers(
containerParent.children.indexOf(containerId),
[{ Type: newContainerId }],
containerParent.properties.id,
configuration, fullHistory, historyCurrentStep
);
const historyDelete = DeleteContainer(containerId, historyAdd.history, historyCurrentStep + 1);
const currentDelete = historyDelete[historyDelete.length - 1];
fullHistory.push({
lastAction: `Replace ${containerId} by ${newContainerId}`,
mainContainer: currentDelete.mainContainer,
containers: currentDelete.containers,
selectedContainerId: currentDelete.selectedContainerId,
typeCounters: Object.assign({}, currentDelete.typeCounters),
symbols: current.symbols,
selectedSymbolId: current.selectedSymbolId
});
return fullHistory;
}
/**
* Returns the next container that will be selected
* after the selectedContainer is removed.

View file

@ -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<string, IMenuAction[]>,
@ -23,7 +24,8 @@ export function InitActions(
history: IHistoryState[],
historyCurrentStep: number,
setNewHistory: (newHistory: IHistoryState[]) => void,
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
setHistoryCurrentStep: Dispatch<SetStateAction<number>>,
setIsReplacingContainer: Dispatch<SetStateAction<IReplaceContainer>>
): void {
menuActions.set(
'',
@ -56,9 +58,24 @@ export function InitActions(
menuActions.set(
'elements-sidebar-row',
[{
text: Text({ textId: '@ReplaceByContainer' }),
title: Text({ textId: '@ReplaceByContainerTitle' }),
shortcut: '<kbd>R</kbd>',
action: (target: HTMLElement) => {
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' }),
title: Text({ textId: '@DeleteContainerTitle' }),
shortcut: '<kbd>Suppr</kbd>',
action: (target: HTMLElement) => {
const id = target.id;
const newHistory = DeleteContainer(

View file

@ -53,6 +53,7 @@ export function SaveEditorAsSVG(): void {
svg.replaceChildren(...mainSvg);
// remove the selector
// TODO: Fix this with SelectorMode != Nothing or with some html magic
const group = svg.children[svg.children.length - 1];
group.removeChild(group.children[group.children.length - 1]);
if (SHOW_SELECTOR_TEXT) {

View file

@ -7,7 +7,8 @@ export function OnKey(
history: IHistoryState[],
historyCurrentStep: number,
setHistoryCurrentStep: Dispatch<SetStateAction<number>>,
deleteAction: () => void
deleteAction: () => void,
resetState: () => void
): void {
if (!ENABLE_SHORTCUTS) {
return;
@ -27,5 +28,7 @@ export function OnKey(
setHistoryCurrentStep(historyCurrentStep + 1);
} else if (event.key === 'Delete') {
deleteAction();
} else if (event.key === 'Escape') {
resetState();
}
}

View file

@ -1,9 +1,9 @@
import React, { Dispatch, SetStateAction, useEffect, useRef } from 'react';
import React, { type Dispatch, type SetStateAction, useEffect, useRef } from 'react';
import './Editor.scss';
import { IConfiguration } from '../../Interfaces/IConfiguration';
import { IHistoryState } from '../../Interfaces/IHistoryState';
import { type IConfiguration } from '../../Interfaces/IConfiguration';
import { type 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 { type IReplaceContainer } from '../../Interfaces/IReplaceContainer';
interface IEditorProps {
root: Element | Document
@ -25,16 +26,18 @@ function UseShortcuts(
history: IHistoryState[],
historyCurrentStep: number,
setHistoryCurrentStep: Dispatch<SetStateAction<number>>,
deleteAction: () => void
deleteAction: () => void,
resetState: () => void
): void {
useEffect(() => {
function OnKeyUp(event: KeyboardEvent): void {
return OnKey(
OnKey(
event,
history,
historyCurrentStep,
setHistoryCurrentStep,
deleteAction
deleteAction,
resetState
);
}
@ -62,13 +65,20 @@ function UseNewHistoryState(
};
}
export function Editor(props: IEditorProps): JSX.Element {
// States
const [history, setHistory] = React.useState<IHistoryState[]>(structuredClone(props.history));
const [historyCurrentStep, setHistoryCurrentStep] = React.useState<number>(props.historyCurrentStep);
const [replaceContainer, setReplaceContainer] = React.useState<IReplaceContainer>({ isReplacing: false, id: undefined, category: undefined });
const editorRef = useRef<HTMLDivElement>(null);
const setNewHistory = UseNewHistoryState(setHistory, setHistoryCurrentStep);
function ResetState(): void {
setReplaceContainer({ isReplacing: false, id: undefined, category: undefined });
}
// Events
UseShortcuts(
history,
@ -79,7 +89,8 @@ export function Editor(props: IEditorProps): JSX.Element {
setNewHistory(
DeleteContainer(current.selectedContainerId, history, historyCurrentStep)
);
}
},
ResetState
);
UseCustomEvents(
props.root,
@ -104,7 +115,8 @@ export function Editor(props: IEditorProps): JSX.Element {
history,
historyCurrentStep,
setNewHistory,
setHistoryCurrentStep
setHistoryCurrentStep,
setReplaceContainer
);
// Render
@ -113,87 +125,118 @@ export function Editor(props: IEditorProps): JSX.Element {
const selected = FindContainerById(current.containers, current.selectedContainerId);
return (
<div ref={editorRef} className="Editor font-sans h-full">
<div ref={editorRef} className="Editor font-sans h-full ">
<UI
editorState={{
configuration: props.configuration,
history,
historyCurrentStep
}}
selectContainer={(container) => setNewHistory(
SelectContainer(
container,
history,
historyCurrentStep
))}
deleteContainer={(containerId: string) => setNewHistory(
DeleteContainer(
containerId,
history,
historyCurrentStep
))}
onPropertyChange={(key, value, type) => setNewHistory(
OnPropertyChange(
key, value, type,
selected,
history,
historyCurrentStep
))}
addContainer={(type) => {
replaceContainer={replaceContainer}
selectContainer={(container) => {
setNewHistory(
SelectContainer(
container,
history,
historyCurrentStep
));
}}
deleteContainer={(containerId: string) => {
setNewHistory(
DeleteContainer(
containerId,
history,
historyCurrentStep
));
}}
onPropertyChange={(key, value, type) => {
setNewHistory(
OnPropertyChange(
key, value, type,
selected,
history,
historyCurrentStep
));
}}
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(
index,
type,
parent,
configuration,
addContainerAt={(index, type, parent) => {
setNewHistory(
AddContainer(
index,
type,
parent,
configuration,
history,
historyCurrentStep
)
);
}}
addSymbol={(type) => {
setNewHistory(
AddSymbol(
type,
configuration,
history,
historyCurrentStep
));
}}
onSymbolPropertyChange={(key, value) => {
setNewHistory(
OnSymbolPropertyChange(
key, value,
history,
historyCurrentStep
));
}}
selectSymbol={(symbolId) => {
setNewHistory(
SelectSymbol(
symbolId,
history,
historyCurrentStep
));
}}
deleteSymbol={(symbolId) => {
setNewHistory(
DeleteSymbol(
symbolId,
history,
historyCurrentStep
));
}}
saveEditorAsJSON={() => {
SaveEditorAsJSON(
history,
historyCurrentStep
)
)}
addSymbol={(type) => setNewHistory(
AddSymbol(
type,
configuration,
history,
historyCurrentStep
))}
onSymbolPropertyChange={(key, value) => setNewHistory(
OnSymbolPropertyChange(
key, value,
history,
historyCurrentStep
))}
selectSymbol={(symbolId) => setNewHistory(
SelectSymbol(
symbolId,
history,
historyCurrentStep
))}
deleteSymbol={(symbolId) => setNewHistory(
DeleteSymbol(
symbolId,
history,
historyCurrentStep
))}
saveEditorAsJSON={() => SaveEditorAsJSON(
history,
historyCurrentStep,
configuration
)}
saveEditorAsSVG={() => SaveEditorAsSVG()}
loadState={(move) => setHistoryCurrentStep(move)}
historyCurrentStep,
configuration
);
}}
saveEditorAsSVG={() => { SaveEditorAsSVG(); }}
loadState={(move) => { setHistoryCurrentStep(move); }}
setReplaceContainer={setReplaceContainer}
/>
<Menu
getListener={() => editorRef.current}