157 lines
4.8 KiB
TypeScript
157 lines
4.8 KiB
TypeScript
import * as React from 'react';
|
|
import { motion } from 'framer-motion';
|
|
import { Properties } from '../Properties/Properties';
|
|
import ContainerProperties from '../../Interfaces/IProperties';
|
|
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
|
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 { IPoint } from '../../Interfaces/IPoint';
|
|
|
|
interface IElementsSidebarProps {
|
|
MainContainer: IContainerModel
|
|
isOpen: boolean
|
|
isHistoryOpen: boolean
|
|
SelectedContainer: IContainerModel | null
|
|
OnPropertyChange: (key: string, value: string | number | boolean) => void
|
|
OnPropertiesSubmit: (event: React.FormEvent<HTMLFormElement>, properties: ContainerProperties) => 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<IPoint>({
|
|
x: 0,
|
|
y: 0
|
|
});
|
|
|
|
const elementRef = React.useRef<HTMLDivElement>(null);
|
|
|
|
// Event listeners
|
|
React.useEffect(() => {
|
|
const onContextMenu = (event: MouseEvent): void => handleRightClick(
|
|
event,
|
|
setIsContextMenuOpen,
|
|
setOnClickContainerId,
|
|
setContextMenuPosition
|
|
);
|
|
|
|
const onLeftClick = (): void => handleLeftClick(
|
|
isContextMenuOpen,
|
|
setIsContextMenuOpen,
|
|
setOnClickContainerId
|
|
);
|
|
|
|
elementRef.current?.addEventListener(
|
|
'contextmenu',
|
|
onContextMenu
|
|
);
|
|
|
|
window.addEventListener(
|
|
'click',
|
|
onLeftClick
|
|
);
|
|
|
|
return () => {
|
|
elementRef.current?.removeEventListener(
|
|
'contextmenu',
|
|
onContextMenu
|
|
);
|
|
|
|
window.removeEventListener(
|
|
'click',
|
|
onLeftClick
|
|
);
|
|
};
|
|
});
|
|
|
|
// 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={() => {
|
|
setIsContextMenuOpen(false);
|
|
props.DeleteContainer(onClickContainerId);
|
|
}} />
|
|
</Menu>
|
|
<Properties
|
|
properties={props.SelectedContainer?.properties}
|
|
onChange={props.OnPropertyChange}
|
|
onSubmit={props.OnPropertiesSubmit}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|