Optimize history and fix nodes pollution + fix css + removes motion.framer (#28)
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: https://git.siklos-chaneru.duckdns.org/Siklos/svg-layout-designer-react/pulls/28
This commit is contained in:
parent
704dab7307
commit
d6eb9ea364
11 changed files with 196 additions and 575 deletions
|
@ -15,7 +15,7 @@ export const BAR_WIDTH = 64; // 4rem
|
|||
|
||||
export const Bar: React.FC<IBarProps> = (props) => {
|
||||
return (
|
||||
<div className='fixed z-20 flex flex-col top-0 left-0 h-screen w-16 bg-slate-100'>
|
||||
<div className='fixed z-20 flex flex-col top-0 left-0 h-full w-16 bg-slate-100'>
|
||||
<BarIcon
|
||||
isActive={props.isSidebarOpen}
|
||||
title='Components'
|
||||
|
|
|
@ -27,14 +27,15 @@ export function SelectContainer(
|
|||
throw new Error('[SelectContainer] Cannot find container among children of main container!');
|
||||
}
|
||||
|
||||
setHistory(history.concat([{
|
||||
LastAction: `Select container ${selectedContainer.properties.id}`,
|
||||
history.push({
|
||||
LastAction: `Select ${selectedContainer.properties.id}`,
|
||||
MainContainer: mainContainerClone,
|
||||
SelectedContainer: selectedContainer,
|
||||
SelectedContainerId: selectedContainer.properties.id,
|
||||
TypeCounters: Object.assign({}, current.TypeCounters)
|
||||
}]));
|
||||
setHistoryCurrentStep(history.length);
|
||||
});
|
||||
setHistory(history);
|
||||
setHistoryCurrentStep(history.length - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -87,14 +88,15 @@ export function DeleteContainer(
|
|||
container.parent;
|
||||
const SelectedContainerId = SelectedContainer.properties.id;
|
||||
|
||||
setHistory(history.concat([{
|
||||
LastAction: `Delete container ${containerId}`,
|
||||
history.push({
|
||||
LastAction: `Delete ${containerId}`,
|
||||
MainContainer: mainContainerClone,
|
||||
SelectedContainer,
|
||||
SelectedContainerId,
|
||||
TypeCounters: Object.assign({}, current.TypeCounters)
|
||||
}]));
|
||||
setHistoryCurrentStep(history.length);
|
||||
});
|
||||
setHistory(history);
|
||||
setHistoryCurrentStep(history.length - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -235,12 +237,13 @@ export function AddContainer(
|
|||
}
|
||||
|
||||
// Update the state
|
||||
setHistory(history.concat([{
|
||||
history.push({
|
||||
LastAction: 'Add container',
|
||||
MainContainer: clone,
|
||||
SelectedContainer: parentClone,
|
||||
SelectedContainerId: parentClone.properties.id,
|
||||
TypeCounters: newCounters
|
||||
}]));
|
||||
setHistoryCurrentStep(history.length);
|
||||
});
|
||||
setHistory(history);
|
||||
setHistoryCurrentStep(history.length - 1);
|
||||
}
|
||||
|
|
|
@ -42,12 +42,23 @@ const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
|||
configuration: props.configuration
|
||||
};
|
||||
|
||||
const funcs = new Map<string, () => void>();
|
||||
for (const event of events) {
|
||||
editorRef.current?.addEventListener(event.name, () => event.func(editorState));
|
||||
const func = (): void => event.func(editorState);
|
||||
editorRef.current?.addEventListener(event.name, func);
|
||||
funcs.set(event.name, func);
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keyup', onKeyUp);
|
||||
|
||||
for (const event of events) {
|
||||
const func = funcs.get(event.name);
|
||||
if (func === undefined) {
|
||||
continue;
|
||||
}
|
||||
editorRef.current?.removeEventListener(event.name, func);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -51,14 +51,15 @@ export function OnPropertyChange(
|
|||
RecalculatePhysics(container);
|
||||
}
|
||||
|
||||
setHistory(history.concat([{
|
||||
LastAction: `Change property of container ${container.properties.id}`,
|
||||
history.push({
|
||||
LastAction: `Change ${key} of ${container.properties.id}`,
|
||||
MainContainer: mainContainerClone,
|
||||
SelectedContainer: container,
|
||||
SelectedContainerId: container.properties.id,
|
||||
TypeCounters: Object.assign({}, current.TypeCounters)
|
||||
}]));
|
||||
setHistoryCurrentStep(history.length);
|
||||
});
|
||||
setHistory(history);
|
||||
setHistoryCurrentStep(history.length - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,12 +108,13 @@ export function OnPropertiesSubmit(
|
|||
RecalculatePhysics(container);
|
||||
}
|
||||
|
||||
setHistory(history.concat([{
|
||||
LastAction: `Change property of container ${container.properties.id}`,
|
||||
history.push({
|
||||
LastAction: `Change properties of ${container.properties.id}`,
|
||||
MainContainer: mainContainerClone,
|
||||
SelectedContainer: container,
|
||||
SelectedContainerId: container.properties.id,
|
||||
TypeCounters: Object.assign({}, current.TypeCounters)
|
||||
}]));
|
||||
setHistoryCurrentStep(history.length);
|
||||
});
|
||||
setHistory(history);
|
||||
setHistoryCurrentStep(history.length - 1);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { FixedSizeList as List } from 'react-window';
|
||||
import { Properties } from '../Properties/Properties';
|
||||
import ContainerProperties from '../../Interfaces/IProperties';
|
||||
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||
|
@ -21,45 +21,6 @@ interface IElementsSidebarProps {
|
|||
AddContainer: (index: number, type: string, parent: string) => void
|
||||
}
|
||||
|
||||
function createRows(
|
||||
container: IContainerModel,
|
||||
props: IElementsSidebarProps,
|
||||
containerRows: React.ReactNode[]
|
||||
): void {
|
||||
const depth: number = getDepth(container);
|
||||
const key = container.properties.id.toString();
|
||||
const text = '|\t'.repeat(depth) + key;
|
||||
const selectedClass: string = props.SelectedContainer !== undefined &&
|
||||
props.SelectedContainer !== null &&
|
||||
props.SelectedContainer.properties.id === container.properties.id
|
||||
? 'border-l-4 bg-slate-400/60 hover:bg-slate-400'
|
||||
: 'bg-slate-300/60 hover:bg-slate-300';
|
||||
|
||||
containerRows.push(
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 1.2 }}
|
||||
initial={{ opacity: 0, scale: 0 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{
|
||||
duration: 0.150
|
||||
}}
|
||||
className={
|
||||
`w-full border-blue-500 elements-sidebar-row whitespace-pre
|
||||
text-left text-sm font-medium transition-all ${selectedClass}`
|
||||
}
|
||||
id={key}
|
||||
key={key}
|
||||
onDrop={(event) => handleOnDrop(event, props.MainContainer, props.AddContainer)}
|
||||
onDragOver={(event) => handleDragOver(event, props.MainContainer)}
|
||||
onDragLeave={(event) => handleDragLeave(event)}
|
||||
onClick={() => props.SelectContainer(container)}
|
||||
>
|
||||
{ text }
|
||||
</motion.button>
|
||||
);
|
||||
};
|
||||
|
||||
export const ElementsSidebar: React.FC<IElementsSidebarProps> = (props: IElementsSidebarProps): JSX.Element => {
|
||||
// States
|
||||
const [isContextMenuOpen, setIsContextMenuOpen] = React.useState<boolean>(false);
|
||||
|
@ -117,24 +78,55 @@ export const ElementsSidebar: React.FC<IElementsSidebarProps> = (props: IElement
|
|||
: 'right-0';
|
||||
}
|
||||
|
||||
const containerRows: React.ReactNode[] = [];
|
||||
|
||||
const it = MakeIterator(props.MainContainer);
|
||||
for (const container of it) {
|
||||
createRows(
|
||||
container,
|
||||
props,
|
||||
containerRows
|
||||
);
|
||||
}
|
||||
const containers = [...it];
|
||||
const Row = ({ index, style }: {index: number, style: React.CSSProperties}): JSX.Element => {
|
||||
const container = containers[index];
|
||||
const depth: number = getDepth(container);
|
||||
const key = container.properties.id.toString();
|
||||
const text = '|\t'.repeat(depth) + key;
|
||||
const selectedClass: string = props.SelectedContainer !== undefined &&
|
||||
props.SelectedContainer !== null &&
|
||||
props.SelectedContainer.properties.id === container.properties.id
|
||||
? 'border-l-4 bg-slate-400/60 hover:bg-slate-400'
|
||||
: 'bg-slate-300/60 hover:bg-slate-300';
|
||||
|
||||
return (
|
||||
<button
|
||||
className={
|
||||
`w-full border-blue-500 elements-sidebar-row whitespace-pre
|
||||
text-left text-sm font-medium transition-all ${selectedClass}`
|
||||
}
|
||||
id={key}
|
||||
key={key}
|
||||
style={style}
|
||||
onDrop={(event) => handleOnDrop(event, props.MainContainer, props.AddContainer)}
|
||||
onDragOver={(event) => handleDragOver(event, props.MainContainer)}
|
||||
onDragLeave={(event) => handleDragLeave(event)}
|
||||
onClick={() => props.SelectContainer(container)}
|
||||
>
|
||||
{ text }
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
const ROW_HEIGHT = 35;
|
||||
const NUMBERS_OF_ROWS = 10;
|
||||
return (
|
||||
<div className={`fixed flex flex-col bg-slate-100 text-gray-800 transition-all h-screen w-64 overflow-y-auto z-20 ${isOpenClasses}`}>
|
||||
<div className={`fixed flex flex-col bg-slate-100 text-gray-800 transition-all h-full w-64 overflow-y-auto z-20 ${isOpenClasses}`}>
|
||||
<div className='bg-slate-100 font-bold sidebar-title'>
|
||||
Elements
|
||||
</div>
|
||||
<div ref={elementRef} className='overflow-y-auto overflow-x-hidden text-gray-800 flex-grow'>
|
||||
{ containerRows }
|
||||
<div ref={elementRef} className='h-96 text-gray-800'>
|
||||
<List
|
||||
className='List'
|
||||
itemCount={containers.length}
|
||||
itemSize={35}
|
||||
height={384}
|
||||
width={256}
|
||||
>
|
||||
{ Row }
|
||||
</List>
|
||||
</div>
|
||||
<Menu
|
||||
className='transition-opacity rounded bg-slate-200 py-1 drop-shadow-xl'
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import * as React from 'react';
|
||||
import { IHistoryState } from "../../Interfaces/IHistoryState";
|
||||
import { FixedSizeList as List } from 'react-window';
|
||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||
|
||||
interface IHistoryProps {
|
||||
history: IHistoryState[]
|
||||
|
@ -10,47 +11,47 @@ interface IHistoryProps {
|
|||
|
||||
export const History: React.FC<IHistoryProps> = (props: IHistoryProps) => {
|
||||
const isOpenClasses = props.isOpen ? 'right-0' : '-right-64';
|
||||
|
||||
const states = props.history.map((step, move) => {
|
||||
const desc = move > 0
|
||||
? `Go to modification n°${move}`
|
||||
const Row = ({ index, style }: {index: number, style: React.CSSProperties}): JSX.Element => {
|
||||
const reversedIndex = (props.history.length - 1) - index;
|
||||
const step = props.history[reversedIndex];
|
||||
const desc = reversedIndex > 0
|
||||
? `${reversedIndex}: ${step.LastAction}`
|
||||
: 'Go to the beginning';
|
||||
|
||||
const isCurrent = move === props.historyCurrentStep;
|
||||
|
||||
const selectedClass = isCurrent
|
||||
const selectedClass = reversedIndex === props.historyCurrentStep
|
||||
? 'bg-blue-500 hover:bg-blue-600'
|
||||
: 'bg-slate-500 hover:bg-slate-700';
|
||||
|
||||
const isCurrentText = isCurrent
|
||||
? ' (current)'
|
||||
: '';
|
||||
return (
|
||||
|
||||
<button
|
||||
key={move}
|
||||
onClick={() => props.jumpTo(move)}
|
||||
key={reversedIndex}
|
||||
style={style}
|
||||
onClick={() => props.jumpTo(reversedIndex)}
|
||||
title={step.LastAction}
|
||||
className={
|
||||
`w-full elements-sidebar-row whitespace-pre
|
||||
`w-full elements-sidebar-row whitespace-pre overflow-hidden
|
||||
text-left text-sm font-medium transition-all ${selectedClass}`
|
||||
}
|
||||
>
|
||||
{desc}{isCurrentText}
|
||||
{desc}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
||||
// recent first
|
||||
states.reverse();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`fixed flex flex-col bg-slate-300 text-white transition-all h-screen w-64 overflow-y-auto z-20 ${isOpenClasses}`}>
|
||||
<div className={`fixed flex flex-col bg-slate-300 text-white transition-all h-full w-64 overflow-y-auto z-20 ${isOpenClasses}`}>
|
||||
<div className='bg-slate-600 font-bold sidebar-title'>
|
||||
Timeline
|
||||
</div>
|
||||
<div className='overflow-y-auto overflow-x-hidden text-slate-300 flex-grow divide-y divide-solid divide-slate-600'>
|
||||
{ states }
|
||||
</div>
|
||||
<List
|
||||
className='List overflow-x-hidden'
|
||||
itemCount={props.history.length}
|
||||
itemSize={35}
|
||||
height={window.innerHeight}
|
||||
width={256}
|
||||
>
|
||||
{ Row }
|
||||
</List>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -30,7 +30,7 @@ function resizeViewBox(
|
|||
|
||||
export const SVG: React.FC<ISVGProps> = (props: ISVGProps) => {
|
||||
const [viewer, setViewer] = React.useState<Viewer>({
|
||||
viewerWidth: window.innerWidth,
|
||||
viewerWidth: window.innerWidth - BAR_WIDTH,
|
||||
viewerHeight: window.innerHeight
|
||||
});
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ export const Sidebar: React.FC<ISidebarProps> = (props: ISidebarProps) => {
|
|||
const isOpenClasses = props.isOpen ? 'left-16' : '-left-64';
|
||||
return (
|
||||
<div className={`fixed z-10 bg-slate-200
|
||||
text-gray-700 transition-all h-screen w-64
|
||||
text-gray-700 transition-all h-full w-64
|
||||
overflow-y-auto ${isOpenClasses}`}>
|
||||
<div className='bg-slate-100 sidebar-title'>
|
||||
Components
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue