Merged PR 171: Refactor the multiple context menus into a single component + Fix eslint
Refactor the multiple context menus into a single component + Fix eslint
This commit is contained in:
parent
ad126c6c28
commit
87c4ea1fe5
8 changed files with 173 additions and 269 deletions
|
@ -4,7 +4,6 @@ module.exports = {
|
||||||
es2021: true
|
es2021: true
|
||||||
},
|
},
|
||||||
extends: [
|
extends: [
|
||||||
'only-warn',
|
|
||||||
'plugin:react/recommended',
|
'plugin:react/recommended',
|
||||||
'standard-with-typescript'
|
'standard-with-typescript'
|
||||||
],
|
],
|
||||||
|
@ -18,6 +17,7 @@ module.exports = {
|
||||||
project: './tsconfig.json'
|
project: './tsconfig.json'
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
'only-warn',
|
||||||
'react',
|
'react',
|
||||||
'react-hooks',
|
'react-hooks',
|
||||||
'@typescript-eslint'
|
'@typescript-eslint'
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { IEditorState } from '../../Interfaces/IEditorState';
|
||||||
import { MAX_HISTORY } from '../../utils/default';
|
import { MAX_HISTORY } from '../../utils/default';
|
||||||
import { AddSymbol, OnPropertyChange as OnSymbolPropertyChange, DeleteSymbol, SelectSymbol } from './Actions/SymbolOperations';
|
import { AddSymbol, OnPropertyChange as OnSymbolPropertyChange, DeleteSymbol, SelectSymbol } from './Actions/SymbolOperations';
|
||||||
import { FindContainerById } from '../../utils/itertools';
|
import { FindContainerById } from '../../utils/itertools';
|
||||||
|
import { Menu } from '../Menu/Menu';
|
||||||
|
|
||||||
interface IEditorProps {
|
interface IEditorProps {
|
||||||
root: Element | Document
|
root: Element | Document
|
||||||
|
@ -20,24 +21,45 @@ interface IEditorProps {
|
||||||
historyCurrentStep: number
|
historyCurrentStep: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UpdateCounters(counters: Record<string, number>, type: string): void {
|
function InitActions(
|
||||||
if (counters[type] === null ||
|
menuActions: Map<any, any>,
|
||||||
counters[type] === undefined) {
|
history: IHistoryState[],
|
||||||
counters[type] = 0;
|
historyCurrentStep: number,
|
||||||
} else {
|
setHistory: React.Dispatch<React.SetStateAction<IHistoryState[]>>,
|
||||||
counters[type]++;
|
setHistoryCurrentStep: React.Dispatch<React.SetStateAction<number>>
|
||||||
}
|
): void {
|
||||||
}
|
menuActions.set(
|
||||||
|
'elements-sidebar-row',
|
||||||
export function GetCurrentHistory(history: IHistoryState[], historyCurrentStep: number): IHistoryState[] {
|
[{
|
||||||
return history.slice(
|
text: 'Delete',
|
||||||
Math.max(0, history.length - MAX_HISTORY),
|
action: (target: HTMLElement) => {
|
||||||
historyCurrentStep + 1
|
const id = target.id;
|
||||||
|
DeleteContainer(
|
||||||
|
id,
|
||||||
|
history,
|
||||||
|
historyCurrentStep,
|
||||||
|
setHistory,
|
||||||
|
setHistoryCurrentStep
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}]
|
||||||
export function GetCurrentHistoryState(history: IHistoryState[], historyCurrentStep: number): IHistoryState {
|
);
|
||||||
return history[historyCurrentStep];
|
menuActions.set(
|
||||||
|
'symbols-sidebar-row',
|
||||||
|
[{
|
||||||
|
text: 'Delete',
|
||||||
|
action: (target: HTMLElement) => {
|
||||||
|
const id = target.id;
|
||||||
|
DeleteSymbol(
|
||||||
|
id,
|
||||||
|
history,
|
||||||
|
historyCurrentStep,
|
||||||
|
setHistory,
|
||||||
|
setHistoryCurrentStep
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function UseShortcuts(
|
function UseShortcuts(
|
||||||
|
@ -106,10 +128,12 @@ function UseWindowEvents(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Editor(props: IEditorProps): JSX.Element {
|
export function Editor(props: IEditorProps): JSX.Element {
|
||||||
|
// 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 editorRef = useRef<HTMLDivElement>(null);
|
const editorRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
// Events
|
||||||
UseShortcuts(history, historyCurrentStep, setHistoryCurrentStep);
|
UseShortcuts(history, historyCurrentStep, setHistoryCurrentStep);
|
||||||
UseWindowEvents(
|
UseWindowEvents(
|
||||||
props.root,
|
props.root,
|
||||||
|
@ -121,6 +145,11 @@ export function Editor(props: IEditorProps): JSX.Element {
|
||||||
setHistoryCurrentStep
|
setHistoryCurrentStep
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Context Menu
|
||||||
|
const menuActions = new Map();
|
||||||
|
InitActions(menuActions, history, historyCurrentStep, setHistory, setHistoryCurrentStep);
|
||||||
|
|
||||||
|
// Render
|
||||||
const configuration = props.configuration;
|
const configuration = props.configuration;
|
||||||
const current = GetCurrentHistoryState(history, historyCurrentStep);
|
const current = GetCurrentHistoryState(history, historyCurrentStep);
|
||||||
const selected = FindContainerById(current.mainContainer, current.selectedContainerId);
|
const selected = FindContainerById(current.mainContainer, current.selectedContainerId);
|
||||||
|
@ -208,6 +237,31 @@ export function Editor(props: IEditorProps): JSX.Element {
|
||||||
>
|
>
|
||||||
{current.mainContainer}
|
{current.mainContainer}
|
||||||
</SVG>
|
</SVG>
|
||||||
|
<Menu
|
||||||
|
getListener={() => editorRef.current}
|
||||||
|
actions={menuActions}
|
||||||
|
className="z-30 transition-opacity rounded bg-slate-200 py-1 drop-shadow-xl"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function UpdateCounters(counters: Record<string, number>, type: string): void {
|
||||||
|
if (counters[type] === null ||
|
||||||
|
counters[type] === undefined) {
|
||||||
|
counters[type] = 0;
|
||||||
|
} else {
|
||||||
|
counters[type]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GetCurrentHistory(history: IHistoryState[], historyCurrentStep: number): IHistoryState[] {
|
||||||
|
return history.slice(
|
||||||
|
Math.max(0, history.length - MAX_HISTORY),
|
||||||
|
historyCurrentStep + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GetCurrentHistoryState(history: IHistoryState[], historyCurrentStep: number): IHistoryState {
|
||||||
|
return history[historyCurrentStep];
|
||||||
|
}
|
||||||
|
|
|
@ -3,10 +3,6 @@ import { FixedSizeList as List } from 'react-window';
|
||||||
import { Properties } from '../ContainerProperties/ContainerProperties';
|
import { Properties } from '../ContainerProperties/ContainerProperties';
|
||||||
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { GetDepth, MakeIterator } from '../../utils/itertools';
|
import { GetDepth, MakeIterator } from '../../utils/itertools';
|
||||||
import { Menu } from '../Menu/Menu';
|
|
||||||
import { MenuItem } from '../Menu/MenuItem';
|
|
||||||
import { UseMouseEvents } from './MouseEventHandlers';
|
|
||||||
import { IPoint } from '../../Interfaces/IPoint';
|
|
||||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
||||||
import { PropertyType } from '../../Enums/PropertyType';
|
import { PropertyType } from '../../Enums/PropertyType';
|
||||||
|
|
||||||
|
@ -22,29 +18,9 @@ interface IElementsSidebarProps {
|
||||||
type?: PropertyType
|
type?: PropertyType
|
||||||
) => void
|
) => void
|
||||||
selectContainer: (containerId: string) => void
|
selectContainer: (containerId: string) => void
|
||||||
deleteContainer: (containerid: string) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ElementsSidebar(props: IElementsSidebarProps): JSX.Element {
|
export function ElementsSidebar(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
|
|
||||||
UseMouseEvents(
|
|
||||||
isContextMenuOpen,
|
|
||||||
elementRef,
|
|
||||||
setIsContextMenuOpen,
|
|
||||||
setOnClickContainerId,
|
|
||||||
setContextMenuPosition
|
|
||||||
);
|
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
let isOpenClasses = '-right-64';
|
let isOpenClasses = '-right-64';
|
||||||
if (props.isOpen) {
|
if (props.isOpen) {
|
||||||
|
@ -90,7 +66,7 @@ export function ElementsSidebar(props: IElementsSidebarProps): JSX.Element {
|
||||||
className={`fixed flex flex-col bg-slate-100 text-gray-800 transition-all h-full w-64 overflow-y-auto z-20 ${isOpenClasses}`}
|
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 className="bg-slate-100 font-bold sidebar-title">Elements</div>
|
||||||
<div ref={elementRef} className="h-96 text-gray-800">
|
<div className="h-96 text-gray-800">
|
||||||
<List
|
<List
|
||||||
className="List divide-y divide-black"
|
className="List divide-y divide-black"
|
||||||
itemCount={containers.length}
|
itemCount={containers.length}
|
||||||
|
@ -101,20 +77,6 @@ export function ElementsSidebar(props: IElementsSidebarProps): JSX.Element {
|
||||||
{Row}
|
{Row}
|
||||||
</List>
|
</List>
|
||||||
</div>
|
</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
|
||||||
properties={props.selectedContainer?.properties}
|
properties={props.selectedContainer?.properties}
|
||||||
symbols={props.symbols}
|
symbols={props.symbols}
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
import React, { RefObject, Dispatch, SetStateAction, useEffect } from 'react';
|
|
||||||
import { IPoint } from '../../Interfaces/IPoint';
|
|
||||||
|
|
||||||
export function UseMouseEvents(
|
|
||||||
isContextMenuOpen: boolean,
|
|
||||||
elementRef: RefObject<HTMLDivElement>,
|
|
||||||
setIsContextMenuOpen: Dispatch<SetStateAction<boolean>>,
|
|
||||||
setOnClickContainerId: Dispatch<SetStateAction<string>>,
|
|
||||||
setContextMenuPosition: Dispatch<SetStateAction<IPoint>>
|
|
||||||
): void {
|
|
||||||
useEffect(() => {
|
|
||||||
function OnContextMenu(event: MouseEvent): void {
|
|
||||||
return HandleRightClick(
|
|
||||||
event,
|
|
||||||
setIsContextMenuOpen,
|
|
||||||
setOnClickContainerId,
|
|
||||||
setContextMenuPosition
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function OnLeftClick(): void {
|
|
||||||
return HandleLeftClick(
|
|
||||||
isContextMenuOpen,
|
|
||||||
setIsContextMenuOpen,
|
|
||||||
setOnClickContainerId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
elementRef.current?.addEventListener(
|
|
||||||
'contextmenu',
|
|
||||||
OnContextMenu
|
|
||||||
);
|
|
||||||
|
|
||||||
window.addEventListener(
|
|
||||||
'click',
|
|
||||||
OnLeftClick
|
|
||||||
);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
elementRef.current?.removeEventListener(
|
|
||||||
'contextmenu',
|
|
||||||
OnContextMenu
|
|
||||||
);
|
|
||||||
|
|
||||||
window.removeEventListener(
|
|
||||||
'click',
|
|
||||||
OnLeftClick
|
|
||||||
);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function HandleRightClick(
|
|
||||||
event: MouseEvent,
|
|
||||||
setIsContextMenuOpen: React.Dispatch<React.SetStateAction<boolean>>,
|
|
||||||
setOnClickContainerId: React.Dispatch<React.SetStateAction<string>>,
|
|
||||||
setContextMenuPosition: React.Dispatch<React.SetStateAction<IPoint>>
|
|
||||||
): void {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
if (!(event.target instanceof HTMLButtonElement)) {
|
|
||||||
setIsContextMenuOpen(false);
|
|
||||||
setOnClickContainerId('');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const contextMenuPosition: IPoint = { x: event.pageX, y: event.pageY };
|
|
||||||
setIsContextMenuOpen(true);
|
|
||||||
setOnClickContainerId(event.target.id);
|
|
||||||
setContextMenuPosition(contextMenuPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function HandleLeftClick(
|
|
||||||
isContextMenuOpen: boolean,
|
|
||||||
setIsContextMenuOpen: React.Dispatch<React.SetStateAction<boolean>>,
|
|
||||||
setOnClickContainerId: React.Dispatch<React.SetStateAction<string>>
|
|
||||||
): void {
|
|
||||||
if (!isContextMenuOpen) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsContextMenuOpen(false);
|
|
||||||
setOnClickContainerId('');
|
|
||||||
}
|
|
|
@ -1,23 +1,109 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { IPoint } from '../../Interfaces/IPoint';
|
||||||
|
import { MenuItem } from './MenuItem';
|
||||||
|
|
||||||
interface IMenuProps {
|
interface IMenuProps {
|
||||||
|
getListener: () => HTMLElement | null
|
||||||
|
actions: Map<string, IAction[]>
|
||||||
className?: string
|
className?: string
|
||||||
x: number
|
}
|
||||||
y: number
|
|
||||||
isOpen: boolean
|
export interface IAction {
|
||||||
children: React.ReactNode[] | React.ReactNode
|
/** displayed */
|
||||||
|
text: string
|
||||||
|
|
||||||
|
/** function to be called on button click */
|
||||||
|
action: (target: HTMLElement) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function UseMouseEvents(
|
||||||
|
getListener: () => HTMLElement | null,
|
||||||
|
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>,
|
||||||
|
setContextMenuPosition: React.Dispatch<React.SetStateAction<IPoint>>,
|
||||||
|
setTarget: React.Dispatch<React.SetStateAction<HTMLElement | undefined>>
|
||||||
|
): void {
|
||||||
|
React.useEffect(() => {
|
||||||
|
function OnContextMenu(event: MouseEvent): void {
|
||||||
|
event.preventDefault();
|
||||||
|
const contextMenuPosition: IPoint = { x: event.pageX, y: event.pageY };
|
||||||
|
|
||||||
|
setIsOpen(true);
|
||||||
|
setContextMenuPosition(contextMenuPosition);
|
||||||
|
setTarget(event.target as HTMLElement); // this is wrong since target can be null and not undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
function OnLeftClick(): void {
|
||||||
|
setIsOpen(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
getListener()?.addEventListener(
|
||||||
|
'contextmenu',
|
||||||
|
OnContextMenu
|
||||||
|
);
|
||||||
|
|
||||||
|
window.addEventListener(
|
||||||
|
'click',
|
||||||
|
OnLeftClick
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
getListener()?.removeEventListener(
|
||||||
|
'contextmenu',
|
||||||
|
OnContextMenu
|
||||||
|
);
|
||||||
|
|
||||||
|
window.removeEventListener(
|
||||||
|
'click',
|
||||||
|
OnLeftClick
|
||||||
|
);
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Menu(props: IMenuProps): JSX.Element {
|
export function Menu(props: IMenuProps): JSX.Element {
|
||||||
const visible = props.isOpen ? 'visible opacity-1' : 'invisible opacity-0';
|
const [isOpen, setIsOpen] = React.useState(false);
|
||||||
|
const [contextMenuPosition, setContextMenuPosition] = React.useState<IPoint>({
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
});
|
||||||
|
const [target, setTarget] = React.useState<HTMLElement>();
|
||||||
|
|
||||||
|
UseMouseEvents(
|
||||||
|
props.getListener,
|
||||||
|
setIsOpen,
|
||||||
|
setContextMenuPosition,
|
||||||
|
setTarget
|
||||||
|
);
|
||||||
|
|
||||||
|
let children;
|
||||||
|
|
||||||
|
if (target !== undefined) {
|
||||||
|
for (const className of target.classList) {
|
||||||
|
const actions = props.actions.get(className);
|
||||||
|
if (actions === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
children = actions.map((action, index) =>
|
||||||
|
<MenuItem
|
||||||
|
key={`contextmenu-item-${index}`}
|
||||||
|
className="contextmenu-item"
|
||||||
|
text={action.text}
|
||||||
|
onClick={() => action.action(target)} />
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const visible = isOpen ? 'visible opacity-1' : 'invisible opacity-0';
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`fixed ${props.className ?? ''} ${visible}`}
|
className={`fixed ${props.className ?? ''} ${visible}`}
|
||||||
style={{
|
style={{
|
||||||
left: props.x,
|
left: contextMenuPosition.x,
|
||||||
top: props.y
|
top: contextMenuPosition.y
|
||||||
}}>
|
}}>
|
||||||
{props.children}
|
{ children }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
import { RefObject, Dispatch, SetStateAction, useEffect } from 'react';
|
|
||||||
import { IPoint } from '../../Interfaces/IPoint';
|
|
||||||
|
|
||||||
export function UseMouseEvents(
|
|
||||||
isContextMenuOpen: boolean,
|
|
||||||
elementRef: RefObject<HTMLDivElement>,
|
|
||||||
setIsContextMenuOpen: Dispatch<SetStateAction<boolean>>,
|
|
||||||
setOnClickSymbolId: Dispatch<SetStateAction<string>>,
|
|
||||||
setContextMenuPosition: Dispatch<SetStateAction<IPoint>>
|
|
||||||
): void {
|
|
||||||
useEffect(() => {
|
|
||||||
function OnContextMenu(event: MouseEvent): void {
|
|
||||||
return HandleRightClick(
|
|
||||||
event,
|
|
||||||
setIsContextMenuOpen,
|
|
||||||
setOnClickSymbolId,
|
|
||||||
setContextMenuPosition
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function OnLeftClick(): void {
|
|
||||||
return HandleLeftClick(
|
|
||||||
isContextMenuOpen,
|
|
||||||
setIsContextMenuOpen,
|
|
||||||
setOnClickSymbolId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
elementRef.current?.addEventListener(
|
|
||||||
'contextmenu',
|
|
||||||
OnContextMenu
|
|
||||||
);
|
|
||||||
|
|
||||||
window.addEventListener(
|
|
||||||
'click',
|
|
||||||
OnLeftClick
|
|
||||||
);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
elementRef.current?.removeEventListener(
|
|
||||||
'contextmenu',
|
|
||||||
OnContextMenu
|
|
||||||
);
|
|
||||||
|
|
||||||
window.removeEventListener(
|
|
||||||
'click',
|
|
||||||
OnLeftClick
|
|
||||||
);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function HandleRightClick(
|
|
||||||
event: MouseEvent,
|
|
||||||
setIsContextMenuOpen: React.Dispatch<React.SetStateAction<boolean>>,
|
|
||||||
setOnClickSymbolId: React.Dispatch<React.SetStateAction<string>>,
|
|
||||||
setContextMenuPosition: React.Dispatch<React.SetStateAction<IPoint>>
|
|
||||||
): void {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
if (!(event.target instanceof HTMLButtonElement)) {
|
|
||||||
setIsContextMenuOpen(false);
|
|
||||||
setOnClickSymbolId('');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const contextMenuPosition: IPoint = { x: event.pageX, y: event.pageY };
|
|
||||||
setIsContextMenuOpen(true);
|
|
||||||
setOnClickSymbolId(event.target.id);
|
|
||||||
setContextMenuPosition(contextMenuPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function HandleLeftClick(
|
|
||||||
isContextMenuOpen: boolean,
|
|
||||||
setIsContextMenuOpen: React.Dispatch<React.SetStateAction<boolean>>,
|
|
||||||
setOnClickContainerId: React.Dispatch<React.SetStateAction<string>>
|
|
||||||
): void {
|
|
||||||
if (!isContextMenuOpen) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsContextMenuOpen(false);
|
|
||||||
setOnClickContainerId('');
|
|
||||||
}
|
|
|
@ -1,9 +1,5 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { FixedSizeList as List } from 'react-window';
|
import { FixedSizeList as List } from 'react-window';
|
||||||
import { Menu } from '../Menu/Menu';
|
|
||||||
import { MenuItem } from '../Menu/MenuItem';
|
|
||||||
import { UseMouseEvents } from './MouseEventHandlers';
|
|
||||||
import { IPoint } from '../../Interfaces/IPoint';
|
|
||||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
||||||
import { SymbolProperties } from '../SymbolProperties/SymbolProperties';
|
import { SymbolProperties } from '../SymbolProperties/SymbolProperties';
|
||||||
|
|
||||||
|
@ -18,25 +14,6 @@ interface ISymbolsSidebarProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
|
export function SymbolsSidebar(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
|
|
||||||
UseMouseEvents(
|
|
||||||
isContextMenuOpen,
|
|
||||||
elementRef,
|
|
||||||
setIsContextMenuOpen,
|
|
||||||
setOnClickSymbolId,
|
|
||||||
setContextMenuPosition
|
|
||||||
);
|
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
let isOpenClasses = '-right-64';
|
let isOpenClasses = '-right-64';
|
||||||
if (props.isOpen) {
|
if (props.isOpen) {
|
||||||
|
@ -57,7 +34,7 @@ export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button type="button"
|
<button type="button"
|
||||||
className={`w-full border-blue-500 elements-sidebar-row whitespace-pre
|
className={`w-full border-blue-500 symbols-sidebar-row whitespace-pre
|
||||||
text-left text-sm font-medium transition-all ${selectedClass}`}
|
text-left text-sm font-medium transition-all ${selectedClass}`}
|
||||||
id={key}
|
id={key}
|
||||||
key={key}
|
key={key}
|
||||||
|
@ -74,7 +51,7 @@ export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
|
||||||
<div className='bg-slate-100 font-bold sidebar-title'>
|
<div className='bg-slate-100 font-bold sidebar-title'>
|
||||||
Elements
|
Elements
|
||||||
</div>
|
</div>
|
||||||
<div ref={elementRef} className='h-96 text-gray-800'>
|
<div className='h-96 text-gray-800'>
|
||||||
<List
|
<List
|
||||||
className='List divide-y divide-black'
|
className='List divide-y divide-black'
|
||||||
itemCount={containers.length}
|
itemCount={containers.length}
|
||||||
|
@ -85,17 +62,6 @@ export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
|
||||||
{Row}
|
{Row}
|
||||||
</List>
|
</List>
|
||||||
</div>
|
</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
|
<SymbolProperties
|
||||||
symbol={props.symbols.get(props.selectedSymbolId)}
|
symbol={props.symbols.get(props.selectedSymbolId)}
|
||||||
symbols={props.symbols}
|
symbols={props.symbols}
|
||||||
|
|
|
@ -19,6 +19,10 @@
|
||||||
@apply pl-6 pr-6 pt-2 pb-2 w-full
|
@apply pl-6 pr-6 pt-2 pb-2 w-full
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.symbols-sidebar-row {
|
||||||
|
@apply elements-sidebar-row
|
||||||
|
}
|
||||||
|
|
||||||
.close-button {
|
.close-button {
|
||||||
@apply transition-all w-full h-auto p-4 flex
|
@apply transition-all w-full h-auto p-4 flex
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue