svg-layout-designer-react/src/Components/SymbolsSidebar/SymbolsSidebar.tsx
Eric Nguyen 8b8d88f885 Merged PR 162: Implement symbols and other stuff (see desc)
Implement symbols
- Add, Remove, Select Container
- Form
- Link with container
- Symbol behavior application to container (move to x with xpositionreference)

Important changes
- Remove SelectedContainer from HistoryState, meaning that it will be slower for each load but will be faster for each operations* (SetHistory, SelectContainer, DeleteContainer, SymbolOperations)
- ElementsSidebar now opens with isSidebarOpen meaning that both sidebar will open on toggle
- Moved camelize, transformX, restoreX to different modules (stringtools.ts, svg.ts)
2022-08-22 13:58:32 +00:00

137 lines
3.9 KiB
TypeScript

import * as React from 'react';
import { FixedSizeList as List } from 'react-window';
import { Menu } from '../Menu/Menu';
import { MenuItem } from '../Menu/MenuItem';
import { handleLeftClick, handleRightClick } from './MouseEventHandlers';
import { IPoint } from '../../Interfaces/IPoint';
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
import { SymbolProperties } from '../SymbolProperties/SymbolProperties';
interface ISymbolsSidebarProps {
SelectedSymbolId: string
symbols: Map<string, ISymbolModel>
isOpen: boolean
isHistoryOpen: boolean
OnPropertyChange: (key: string, value: string | number | boolean) => void
SelectSymbol: (symbolId: string) => void
DeleteSymbol: (containerid: string) => void
}
export const SymbolsSidebar: React.FC<ISymbolsSidebarProps> = (props: ISymbolsSidebarProps): JSX.Element => {
// States
const [isContextMenuOpen, setIsContextMenuOpen] = React.useState<boolean>(false);
const [onClickSymbolId, setOnClickSymbolId] = 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,
setOnClickSymbolId,
setContextMenuPosition
);
const onLeftClick = (): void => handleLeftClick(
isContextMenuOpen,
setIsContextMenuOpen,
setOnClickSymbolId
);
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 containers = [...props.symbols.values()];
const Row = ({ index, style }: {index: number, style: React.CSSProperties}): JSX.Element => {
const container = containers[index];
const key = container.id.toString();
const text = key;
const selectedClass: string = props.SelectedSymbolId !== '' &&
props.SelectedSymbolId === container.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}
onClick={() => props.SelectSymbol(key)}
>
{ text }
</button>
);
};
return (
<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='h-96 text-gray-800'>
<List
className='List divide-y divide-black'
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'
x={contextMenuPosition.x}
y={contextMenuPosition.y}
isOpen={isContextMenuOpen}
>
<MenuItem className='contextmenu-item' text='Delete' onClick={() => {
setIsContextMenuOpen(false);
props.DeleteSymbol(onClickSymbolId);
}} />
</Menu>
<SymbolProperties
symbol={props.symbols.get(props.SelectedSymbolId)}
symbols={props.symbols}
onChange={props.OnPropertyChange}
/>
</div>
);
};