This improve greatly the performance and the code cleaning. It allows us to separate the inseparable class methods into modules functions
149 lines
4.7 KiB
TypeScript
149 lines
4.7 KiB
TypeScript
import * as React from 'react';
|
|
import { motion } from 'framer-motion';
|
|
import { Properties } from '../Properties/Properties';
|
|
import { IContainerModel } from '../../Interfaces/ContainerModel';
|
|
import { getDepth, MakeIterator } from '../../utils/itertools';
|
|
import { Menu } from '../Menu/Menu';
|
|
import { MenuItem } from '../Menu/MenuItem';
|
|
import { handleDragLeave, handleDragOver, handleLeftClick, handleOnDrop, handleRightClick } from './MouseEventHandlers';
|
|
import { Point } from '../../Interfaces/Point';
|
|
|
|
interface IElementsSidebarProps {
|
|
MainContainer: IContainerModel
|
|
isOpen: boolean
|
|
isHistoryOpen: boolean
|
|
SelectedContainer: IContainerModel | null
|
|
onPropertyChange: (key: string, value: string) => void
|
|
SelectContainer: (container: IContainerModel) => void
|
|
DeleteContainer: (containerid: string) => void
|
|
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);
|
|
const [onClickContainerId, setOnClickContainerId] = React.useState<string>('');
|
|
const [contextMenuPosition, setContextMenuPosition] = React.useState<Point>({
|
|
x: 0,
|
|
y: 0
|
|
});
|
|
|
|
const elementRef = React.useRef<HTMLDivElement>(null);
|
|
|
|
// Event listeners
|
|
React.useEffect(() => {
|
|
elementRef.current?.addEventListener(
|
|
'contextmenu',
|
|
(event) => handleRightClick(
|
|
event,
|
|
setIsContextMenuOpen,
|
|
setOnClickContainerId,
|
|
setContextMenuPosition
|
|
));
|
|
|
|
window.addEventListener(
|
|
'click',
|
|
(event) => handleLeftClick(
|
|
isContextMenuOpen,
|
|
setIsContextMenuOpen,
|
|
setOnClickContainerId
|
|
));
|
|
|
|
return () => {
|
|
elementRef.current?.addEventListener(
|
|
'contextmenu',
|
|
(event) => handleRightClick(
|
|
event,
|
|
setIsContextMenuOpen,
|
|
setOnClickContainerId,
|
|
setContextMenuPosition
|
|
));
|
|
|
|
window.removeEventListener(
|
|
'click',
|
|
(event) => handleLeftClick(
|
|
isContextMenuOpen,
|
|
setIsContextMenuOpen,
|
|
setOnClickContainerId
|
|
));
|
|
};
|
|
}, []);
|
|
|
|
// Render
|
|
let isOpenClasses = '-right-64';
|
|
if (props.isOpen) {
|
|
isOpenClasses = props.isHistoryOpen
|
|
? 'right-64'
|
|
: 'right-0';
|
|
}
|
|
|
|
const containerRows: React.ReactNode[] = [];
|
|
|
|
const it = MakeIterator(props.MainContainer);
|
|
for (const container of it) {
|
|
createRows(
|
|
container,
|
|
props,
|
|
containerRows
|
|
);
|
|
}
|
|
|
|
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='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>
|
|
<Menu
|
|
className='transition-opacity rounded bg-slate-200 py-1 drop-shadow-xl'
|
|
x={contextMenuPosition.x}
|
|
y={contextMenuPosition.y}
|
|
isOpen={isContextMenuOpen}
|
|
>
|
|
<MenuItem className='contextmenu-item' text='Delete' onClick={() => props.DeleteContainer(onClickContainerId)} />
|
|
</Menu>
|
|
<Properties properties={props.SelectedContainer?.properties} onChange={props.onPropertyChange}></Properties>
|
|
</div>
|
|
);
|
|
};
|