Merged PR 320: #7697 - Remplacer par
Il faut juste que je confirme avec Eric le fonctionnement de l'index (pour l'insertion de l'element dupliqué) au même endroit + Peut être ajouté un guard si le nombre d'element autorisé dans un type n'est pas le même que l'element remplaçant Related work items: #7687
This commit is contained in:
commit
09654a6d50
10 changed files with 246 additions and 95 deletions
|
@ -17,6 +17,7 @@ import { BarIcon } from './BarIcon';
|
||||||
import { Text } from '../Text/Text';
|
import { Text } from '../Text/Text';
|
||||||
|
|
||||||
interface IBarProps {
|
interface IBarProps {
|
||||||
|
className: string
|
||||||
isComponentsOpen: boolean
|
isComponentsOpen: boolean
|
||||||
isSymbolsOpen: boolean
|
isSymbolsOpen: boolean
|
||||||
isHistoryOpen: boolean
|
isHistoryOpen: boolean
|
||||||
|
@ -33,7 +34,7 @@ export const BAR_WIDTH = 64; // 4rem
|
||||||
|
|
||||||
export function Bar(props: IBarProps): JSX.Element {
|
export function Bar(props: IBarProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div className='bar'>
|
<div className={`bar ${props.className}`}>
|
||||||
<BarIcon
|
<BarIcon
|
||||||
isActive={props.isComponentsOpen}
|
isActive={props.isComponentsOpen}
|
||||||
title={Text({ textId: '@Components' })}
|
title={Text({ textId: '@Components' })}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
|
import { EyeIcon, EyeSlashIcon, XCircleIcon } from '@heroicons/react/24/outline';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
||||||
import { ICategory } from '../../Interfaces/ICategory';
|
import { ICategory } from '../../Interfaces/ICategory';
|
||||||
|
@ -6,11 +6,15 @@ import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { TruncateString } from '../../utils/stringtools';
|
import { TruncateString } from '../../utils/stringtools';
|
||||||
import { Category } from '../Category/Category';
|
import { Category } from '../Category/Category';
|
||||||
import { Text } from '../Text/Text';
|
import { Text } from '../Text/Text';
|
||||||
|
import { IReplaceContainer } from '../../Interfaces/IReplaceContainer';
|
||||||
|
import { Dispatch } from 'react';
|
||||||
|
|
||||||
interface IComponentsProps {
|
interface IComponentsProps {
|
||||||
selectedContainer: IContainerModel | undefined
|
selectedContainer: IContainerModel | undefined
|
||||||
componentOptions: IAvailableContainer[]
|
componentOptions: IAvailableContainer[]
|
||||||
categories: ICategory[]
|
categories: ICategory[]
|
||||||
|
replaceContainer: IReplaceContainer
|
||||||
|
setReplaceContainer: Dispatch<React.SetStateAction<IReplaceContainer>>
|
||||||
buttonOnClick: (type: string) => void
|
buttonOnClick: (type: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +66,10 @@ export function Components(props: IComponentsProps): JSX.Element {
|
||||||
disabled = config.Blacklist?.find(type => type === componentOption.Type) !== undefined ?? false;
|
disabled = config.Blacklist?.find(type => type === componentOption.Type) !== undefined ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (props.replaceContainer.isReplacing && componentOption.Category !== props.replaceContainer.category) {
|
||||||
|
disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (disabled && hideDisabled) {
|
if (disabled && hideDisabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -96,6 +104,15 @@ export function Components(props: IComponentsProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div className='h-full'>
|
<div className='h-full'>
|
||||||
<div className='hover:bg-slate-300 h-7 text-right pr-1 pl-1'>
|
<div className='hover:bg-slate-300 h-7 text-right pr-1 pl-1'>
|
||||||
|
{props.replaceContainer.isReplacing && <button
|
||||||
|
onClick={() => {
|
||||||
|
props.setReplaceContainer({ isReplacing: false, id: undefined, category: undefined });
|
||||||
|
}}
|
||||||
|
className='h-full hover:bg-slate-400 rounded-lg p-1'
|
||||||
|
>
|
||||||
|
<XCircleIcon className='heroicon'></XCircleIcon>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
<button
|
<button
|
||||||
onClick={() => { setHideDisabled(!hideDisabled); }}
|
onClick={() => { setHideDisabled(!hideDisabled); }}
|
||||||
className='h-full hover:bg-slate-400 rounded-lg p-1'
|
className='h-full hover:bg-slate-400 rounded-lg p-1'
|
||||||
|
@ -117,4 +134,4 @@ export function Components(props: IComponentsProps): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ import Swal from 'sweetalert2';
|
||||||
import { PropertyType } from '../../../Enums/PropertyType';
|
import { PropertyType } from '../../../Enums/PropertyType';
|
||||||
import { TransformX, TransformY } from '../../../utils/svg';
|
import { TransformX, TransformY } from '../../../utils/svg';
|
||||||
import { Orientation } from '../../../Enums/Orientation';
|
import { Orientation } from '../../../Enums/Orientation';
|
||||||
|
import { AddContainers } from './AddContainer';
|
||||||
|
import { IConfiguration } from '../../../Interfaces/IConfiguration';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select a container
|
* Select a container
|
||||||
|
@ -133,6 +135,58 @@ export function DeleteContainer(
|
||||||
return history;
|
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
|
* Returns the next container that will be selected
|
||||||
* after the selectedContainer is removed.
|
* after the selectedContainer is removed.
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { AddContainers } from './AddContainer';
|
||||||
import { DeleteContainer } from './ContainerOperations';
|
import { DeleteContainer } from './ContainerOperations';
|
||||||
import { DeleteSymbol } from './SymbolOperations';
|
import { DeleteSymbol } from './SymbolOperations';
|
||||||
import { Text } from '../../Text/Text';
|
import { Text } from '../../Text/Text';
|
||||||
|
import { IReplaceContainer } from '../../../Interfaces/IReplaceContainer';
|
||||||
|
|
||||||
export function InitActions(
|
export function InitActions(
|
||||||
menuActions: Map<string, IMenuAction[]>,
|
menuActions: Map<string, IMenuAction[]>,
|
||||||
|
@ -23,7 +24,8 @@ export function InitActions(
|
||||||
history: IHistoryState[],
|
history: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setNewHistory: (newHistory: IHistoryState[]) => void,
|
setNewHistory: (newHistory: IHistoryState[]) => void,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>,
|
||||||
|
setIsReplacingContainer: Dispatch<SetStateAction<IReplaceContainer>>
|
||||||
): void {
|
): void {
|
||||||
menuActions.set(
|
menuActions.set(
|
||||||
'',
|
'',
|
||||||
|
@ -56,9 +58,24 @@ export function InitActions(
|
||||||
menuActions.set(
|
menuActions.set(
|
||||||
'elements-sidebar-row',
|
'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' }),
|
text: Text({ textId: '@DeleteContainer' }),
|
||||||
title: Text({ textId: '@DeleteContainerTitle' }),
|
title: Text({ textId: '@DeleteContainerTitle' }),
|
||||||
shortcut: '<kbd>Suppr</kbd>',
|
shortcut: '<kbd>Suppr</kbd>',
|
||||||
|
|
||||||
action: (target: HTMLElement) => {
|
action: (target: HTMLElement) => {
|
||||||
const id = target.id;
|
const id = target.id;
|
||||||
const newHistory = DeleteContainer(
|
const newHistory = DeleteContainer(
|
||||||
|
|
|
@ -7,7 +7,8 @@ export function OnKey(
|
||||||
history: IHistoryState[],
|
history: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>,
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>,
|
||||||
deleteAction: () => void
|
deleteAction: () => void,
|
||||||
|
resetState: () => void
|
||||||
): void {
|
): void {
|
||||||
if (!ENABLE_SHORTCUTS) {
|
if (!ENABLE_SHORTCUTS) {
|
||||||
return;
|
return;
|
||||||
|
@ -27,5 +28,7 @@ export function OnKey(
|
||||||
setHistoryCurrentStep(historyCurrentStep + 1);
|
setHistoryCurrentStep(historyCurrentStep + 1);
|
||||||
} else if (event.key === 'Delete') {
|
} else if (event.key === 'Delete') {
|
||||||
deleteAction();
|
deleteAction();
|
||||||
|
} else if (event.key === 'Escape') {
|
||||||
|
resetState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 './Editor.scss';
|
||||||
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
import { type IConfiguration } from '../../Interfaces/IConfiguration';
|
||||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
import { type IHistoryState } from '../../Interfaces/IHistoryState';
|
||||||
import { UI } from '../UI/UI';
|
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 { SaveEditorAsJSON, SaveEditorAsSVG } from './Actions/Save';
|
||||||
import { OnKey } from './Actions/Shortcuts';
|
import { OnKey } from './Actions/Shortcuts';
|
||||||
import { UseCustomEvents, UseEditorListener } from '../../Events/EditorEvents';
|
import { UseCustomEvents, UseEditorListener } from '../../Events/EditorEvents';
|
||||||
|
@ -13,6 +13,7 @@ import { FindContainerById } from '../../utils/itertools';
|
||||||
import { Menu } from '../Menu/Menu';
|
import { Menu } from '../Menu/Menu';
|
||||||
import { InitActions } from './Actions/ContextMenuActions';
|
import { InitActions } from './Actions/ContextMenuActions';
|
||||||
import { AddContainerToSelectedContainer, AddContainer } from './Actions/AddContainer';
|
import { AddContainerToSelectedContainer, AddContainer } from './Actions/AddContainer';
|
||||||
|
import { type IReplaceContainer } from '../../Interfaces/IReplaceContainer';
|
||||||
|
|
||||||
interface IEditorProps {
|
interface IEditorProps {
|
||||||
root: Element | Document
|
root: Element | Document
|
||||||
|
@ -25,16 +26,18 @@ function UseShortcuts(
|
||||||
history: IHistoryState[],
|
history: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>,
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>,
|
||||||
deleteAction: () => void
|
deleteAction: () => void,
|
||||||
|
resetState: () => void
|
||||||
): void {
|
): void {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function OnKeyUp(event: KeyboardEvent): void {
|
function OnKeyUp(event: KeyboardEvent): void {
|
||||||
return OnKey(
|
OnKey(
|
||||||
event,
|
event,
|
||||||
history,
|
history,
|
||||||
historyCurrentStep,
|
historyCurrentStep,
|
||||||
setHistoryCurrentStep,
|
setHistoryCurrentStep,
|
||||||
deleteAction
|
deleteAction,
|
||||||
|
resetState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,13 +65,20 @@ function UseNewHistoryState(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function Editor(props: IEditorProps): JSX.Element {
|
export function Editor(props: IEditorProps): JSX.Element {
|
||||||
// States
|
// States
|
||||||
const [history, setHistory] = React.useState<IHistoryState[]>(structuredClone(props.history));
|
const [history, setHistory] = React.useState<IHistoryState[]>(structuredClone(props.history));
|
||||||
const [historyCurrentStep, setHistoryCurrentStep] = React.useState<number>(props.historyCurrentStep);
|
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 editorRef = useRef<HTMLDivElement>(null);
|
||||||
const setNewHistory = UseNewHistoryState(setHistory, setHistoryCurrentStep);
|
const setNewHistory = UseNewHistoryState(setHistory, setHistoryCurrentStep);
|
||||||
|
|
||||||
|
function ResetState(): void {
|
||||||
|
setReplaceContainer({ isReplacing: false, id: undefined, category: undefined });
|
||||||
|
}
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
UseShortcuts(
|
UseShortcuts(
|
||||||
history,
|
history,
|
||||||
|
@ -79,7 +89,8 @@ export function Editor(props: IEditorProps): JSX.Element {
|
||||||
setNewHistory(
|
setNewHistory(
|
||||||
DeleteContainer(current.selectedContainerId, history, historyCurrentStep)
|
DeleteContainer(current.selectedContainerId, history, historyCurrentStep)
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
ResetState
|
||||||
);
|
);
|
||||||
UseCustomEvents(
|
UseCustomEvents(
|
||||||
props.root,
|
props.root,
|
||||||
|
@ -104,7 +115,8 @@ export function Editor(props: IEditorProps): JSX.Element {
|
||||||
history,
|
history,
|
||||||
historyCurrentStep,
|
historyCurrentStep,
|
||||||
setNewHistory,
|
setNewHistory,
|
||||||
setHistoryCurrentStep
|
setHistoryCurrentStep,
|
||||||
|
setReplaceContainer
|
||||||
);
|
);
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
|
@ -113,87 +125,118 @@ export function Editor(props: IEditorProps): JSX.Element {
|
||||||
const selected = FindContainerById(current.containers, current.selectedContainerId);
|
const selected = FindContainerById(current.containers, current.selectedContainerId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={editorRef} className="Editor font-sans h-full">
|
<div ref={editorRef} className="Editor font-sans h-full ">
|
||||||
<UI
|
<UI
|
||||||
editorState={{
|
editorState={{
|
||||||
configuration: props.configuration,
|
configuration: props.configuration,
|
||||||
history,
|
history,
|
||||||
historyCurrentStep
|
historyCurrentStep
|
||||||
}}
|
}}
|
||||||
selectContainer={(container) => setNewHistory(
|
replaceContainer={replaceContainer}
|
||||||
SelectContainer(
|
selectContainer={(container) => {
|
||||||
container,
|
setNewHistory(
|
||||||
history,
|
SelectContainer(
|
||||||
historyCurrentStep
|
container,
|
||||||
))}
|
history,
|
||||||
deleteContainer={(containerId: string) => setNewHistory(
|
historyCurrentStep
|
||||||
DeleteContainer(
|
));
|
||||||
containerId,
|
}}
|
||||||
history,
|
deleteContainer={(containerId: string) => {
|
||||||
historyCurrentStep
|
setNewHistory(
|
||||||
))}
|
DeleteContainer(
|
||||||
onPropertyChange={(key, value, type) => setNewHistory(
|
containerId,
|
||||||
OnPropertyChange(
|
history,
|
||||||
key, value, type,
|
historyCurrentStep
|
||||||
selected,
|
));
|
||||||
history,
|
}}
|
||||||
historyCurrentStep
|
onPropertyChange={(key, value, type) => {
|
||||||
))}
|
setNewHistory(
|
||||||
addContainer={(type) => {
|
OnPropertyChange(
|
||||||
|
key, value, type,
|
||||||
|
selected,
|
||||||
|
history,
|
||||||
|
historyCurrentStep
|
||||||
|
));
|
||||||
|
}}
|
||||||
|
addOrReplaceContainer={(type) => {
|
||||||
if (selected === null || selected === undefined) {
|
if (selected === null || selected === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (replaceContainer.isReplacing && replaceContainer.id !== undefined) {
|
||||||
setNewHistory(AddContainerToSelectedContainer(
|
const newHistory = ReplaceByContainer(
|
||||||
type,
|
replaceContainer.id,
|
||||||
selected,
|
type,
|
||||||
configuration,
|
configuration,
|
||||||
history,
|
history,
|
||||||
historyCurrentStep
|
historyCurrentStep
|
||||||
));
|
);
|
||||||
|
setReplaceContainer({ isReplacing: false, id: undefined, category: undefined });
|
||||||
|
setNewHistory(newHistory);
|
||||||
|
} else {
|
||||||
|
setNewHistory(AddContainerToSelectedContainer(
|
||||||
|
type,
|
||||||
|
selected,
|
||||||
|
configuration,
|
||||||
|
history,
|
||||||
|
historyCurrentStep
|
||||||
|
));
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
addContainerAt={(index, type, parent) => setNewHistory(
|
addContainerAt={(index, type, parent) => {
|
||||||
AddContainer(
|
setNewHistory(
|
||||||
index,
|
AddContainer(
|
||||||
type,
|
index,
|
||||||
parent,
|
type,
|
||||||
configuration,
|
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,
|
history,
|
||||||
historyCurrentStep
|
historyCurrentStep,
|
||||||
)
|
configuration
|
||||||
)}
|
);
|
||||||
addSymbol={(type) => setNewHistory(
|
}}
|
||||||
AddSymbol(
|
saveEditorAsSVG={() => { SaveEditorAsSVG(); }}
|
||||||
type,
|
loadState={(move) => { setHistoryCurrentStep(move); }}
|
||||||
configuration,
|
setReplaceContainer={setReplaceContainer}
|
||||||
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)}
|
|
||||||
/>
|
/>
|
||||||
<Menu
|
<Menu
|
||||||
getListener={() => editorRef.current}
|
getListener={() => editorRef.current}
|
||||||
|
|
|
@ -21,6 +21,7 @@ export interface IMenuAction {
|
||||||
|
|
||||||
/** function to be called on button click */
|
/** function to be called on button click */
|
||||||
action: (target: HTMLElement) => void
|
action: (target: HTMLElement) => void
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function UseMouseEvents(
|
function UseMouseEvents(
|
||||||
|
@ -139,7 +140,7 @@ function AddClassSpecificActions(
|
||||||
onClick={() => action.action(target)} />);
|
onClick={() => action.action(target)} />);
|
||||||
});
|
});
|
||||||
children.push(<hr key={`contextmenu-hr-${count}`} className='border-slate-400' />);
|
children.push(<hr key={`contextmenu-hr-${count}`} className='border-slate-400' />);
|
||||||
};
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,13 +17,16 @@ import { FindContainerById } from '../../utils/itertools';
|
||||||
import { type IEditorState } from '../../Interfaces/IEditorState';
|
import { type IEditorState } from '../../Interfaces/IEditorState';
|
||||||
import { GetCurrentHistoryState } from '../Editor/Editor';
|
import { GetCurrentHistoryState } from '../Editor/Editor';
|
||||||
import { Text } from '../Text/Text';
|
import { Text } from '../Text/Text';
|
||||||
|
import { IReplaceContainer } from '../../Interfaces/IReplaceContainer';
|
||||||
|
import { Dispatch } from 'react';
|
||||||
|
|
||||||
export interface IUIProps {
|
export interface IUIProps {
|
||||||
editorState: IEditorState
|
editorState: IEditorState
|
||||||
|
replaceContainer: IReplaceContainer
|
||||||
selectContainer: (containerId: string) => void
|
selectContainer: (containerId: string) => void
|
||||||
deleteContainer: (containerId: string) => void
|
deleteContainer: (containerId: string) => void
|
||||||
onPropertyChange: (key: string, value: string | number | boolean | number[], type?: PropertyType) => 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
|
addContainerAt: (index: number, type: string, parent: string) => void
|
||||||
addSymbol: (type: string) => void
|
addSymbol: (type: string) => void
|
||||||
onSymbolPropertyChange: (key: string, value: string | number | boolean) => void
|
onSymbolPropertyChange: (key: string, value: string | number | boolean) => void
|
||||||
|
@ -32,6 +35,8 @@ export interface IUIProps {
|
||||||
saveEditorAsJSON: () => void
|
saveEditorAsJSON: () => void
|
||||||
saveEditorAsSVG: () => void
|
saveEditorAsSVG: () => void
|
||||||
loadState: (move: number) => void
|
loadState: (move: number) => void
|
||||||
|
setReplaceContainer: Dispatch<React.SetStateAction<IReplaceContainer>>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SidebarType {
|
export enum SidebarType {
|
||||||
|
@ -59,7 +64,7 @@ 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>(SidebarType.Components);
|
const [selectedSidebar, setSelectedSidebar] = React.useState<SidebarType>(SidebarType.Components);
|
||||||
|
|
||||||
const [messages, setMessages] = React.useState<IMessage[]>([]);
|
const [messages, setMessages] = React.useState<IMessage[]>([]);
|
||||||
|
@ -101,12 +106,14 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
|
||||||
switch (selectedSidebar) {
|
switch (selectedSidebar) {
|
||||||
case SidebarType.Components:
|
case SidebarType.Components:
|
||||||
leftSidebarTitle = Text({ textId: '@Components' });
|
leftSidebarTitle = Text({ textId: '@Components' });
|
||||||
|
|
||||||
leftChildren = <Components
|
leftChildren = <Components
|
||||||
selectedContainer={selectedContainer}
|
selectedContainer={selectedContainer}
|
||||||
componentOptions={configuration.AvailableContainers}
|
componentOptions={configuration.AvailableContainers}
|
||||||
categories={configuration.Categories}
|
categories={configuration.Categories}
|
||||||
buttonOnClick={methods.addContainer}
|
buttonOnClick={methods.addOrReplaceContainer}
|
||||||
/>;
|
replaceContainer={replaceContainer}
|
||||||
|
setReplaceContainer={setReplaceContainer}/>;
|
||||||
rightSidebarTitle = Text({ textId: '@Elements' });
|
rightSidebarTitle = Text({ textId: '@Elements' });
|
||||||
rightChildren = <ElementsSideBar
|
rightChildren = <ElementsSideBar
|
||||||
containers={current.containers}
|
containers={current.containers}
|
||||||
|
@ -126,8 +133,9 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
|
||||||
selectedContainer={selectedContainer}
|
selectedContainer={selectedContainer}
|
||||||
componentOptions={configuration.AvailableContainers}
|
componentOptions={configuration.AvailableContainers}
|
||||||
categories={configuration.Categories}
|
categories={configuration.Categories}
|
||||||
buttonOnClick={methods.addContainer}
|
buttonOnClick={methods.addOrReplaceContainer}
|
||||||
/>;
|
replaceContainer={replaceContainer}
|
||||||
|
setReplaceContainer={setReplaceContainer}/>;
|
||||||
rightSidebarTitle = Text({ textId: '@Elements' });
|
rightSidebarTitle = Text({ textId: '@Elements' });
|
||||||
rightChildren = <ElementsSideBar
|
rightChildren = <ElementsSideBar
|
||||||
containers={current.containers}
|
containers={current.containers}
|
||||||
|
@ -243,12 +251,14 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
|
||||||
isLeftSidebarOpenClasses.add('left-sidebar-single');
|
isLeftSidebarOpenClasses.add('left-sidebar-single');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clickRestrictionsClasses = replaceContainer.isReplacing ? 'pointer-events-none opacity-50' : '';
|
||||||
const isComponentsOpen = selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.ComponentsExpanded;
|
const isComponentsOpen = selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.ComponentsExpanded;
|
||||||
const isSymbolsOpen = selectedSidebar === SidebarType.Symbols || selectedSidebar === SidebarType.SymbolsExpanded;
|
const isSymbolsOpen = selectedSidebar === SidebarType.Symbols || selectedSidebar === SidebarType.SymbolsExpanded;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Bar
|
<Bar
|
||||||
|
className={clickRestrictionsClasses}
|
||||||
isComponentsOpen={isComponentsOpen}
|
isComponentsOpen={isComponentsOpen}
|
||||||
isSymbolsOpen={isSymbolsOpen}
|
isSymbolsOpen={isSymbolsOpen}
|
||||||
isHistoryOpen={selectedSidebar === SidebarType.History}
|
isHistoryOpen={selectedSidebar === SidebarType.History}
|
||||||
|
@ -285,7 +295,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
|
||||||
{ leftChildren }
|
{ leftChildren }
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
<Viewer
|
<Viewer
|
||||||
className={`${[...viewerMarginClasses.values()].join(' ')} w-full h-full`}
|
className={`${clickRestrictionsClasses} ${[...viewerMarginClasses.values()].join(' ')} w-full h-full`}
|
||||||
current={current}
|
current={current}
|
||||||
isComponentsOpen={isComponentsOpen}
|
isComponentsOpen={isComponentsOpen}
|
||||||
isSymbolsOpen={isSymbolsOpen}
|
isSymbolsOpen={isSymbolsOpen}
|
||||||
|
@ -295,7 +305,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
|
||||||
margin={marginSidebar}
|
margin={marginSidebar}
|
||||||
/>
|
/>
|
||||||
<Sidebar
|
<Sidebar
|
||||||
className={`right-sidebar ${isRightSidebarOpenClasses}`}
|
className={`right-sidebar ${isRightSidebarOpenClasses} ${clickRestrictionsClasses}`}
|
||||||
title={rightSidebarTitle}
|
title={rightSidebarTitle}
|
||||||
>
|
>
|
||||||
{ rightChildren }
|
{ rightChildren }
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { useState } from 'react';
|
||||||
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
|
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
|
||||||
|
|
||||||
interface IViewerProps {
|
interface IViewerProps {
|
||||||
className?: string
|
className: string
|
||||||
current: IHistoryState
|
current: IHistoryState
|
||||||
selectedContainer: IContainerModel | undefined
|
selectedContainer: IContainerModel | undefined
|
||||||
selectContainer: (containerId: string) => void
|
selectContainer: (containerId: string) => void
|
||||||
|
@ -133,7 +133,7 @@ export function Viewer({
|
||||||
return (
|
return (
|
||||||
<Canvas
|
<Canvas
|
||||||
draw={Draw}
|
draw={Draw}
|
||||||
className='ml-16'
|
className={`ml-16 ${className}`}
|
||||||
width={window.innerWidth - BAR_WIDTH}
|
width={window.innerWidth - BAR_WIDTH}
|
||||||
height={window.innerHeight}
|
height={window.innerHeight}
|
||||||
/>
|
/>
|
||||||
|
|
5
src/Interfaces/IReplaceContainer.ts
Normal file
5
src/Interfaces/IReplaceContainer.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export interface IReplaceContainer {
|
||||||
|
id: string | undefined
|
||||||
|
isReplacing: boolean
|
||||||
|
category: string | undefined
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue