Keep the extended sidebar open when changing Sidebar type + Rename ElementsSidebar
This commit is contained in:
parent
61807b621f
commit
00cf80d9e3
4 changed files with 81 additions and 101 deletions
250
src/Components/ElementsSidebar/ElementsSidebar.tsx
Normal file
250
src/Components/ElementsSidebar/ElementsSidebar.tsx
Normal file
|
@ -0,0 +1,250 @@
|
|||
import * as React from 'react';
|
||||
import useSize from '@react-hook/size';
|
||||
import { FixedSizeList as List } from 'react-window';
|
||||
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
|
||||
import { ContainerProperties } from '../ContainerProperties/ContainerProperties';
|
||||
import { type IContainerModel } from '../../Interfaces/IContainerModel';
|
||||
import { FindContainerById, MakeRecursionDFSIterator } from '../../utils/itertools';
|
||||
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
|
||||
import { type PropertyType } from '../../Enums/PropertyType';
|
||||
import { ToggleSideBar } from '../Sidebar/ToggleSideBar/ToggleSideBar';
|
||||
import { Text } from '../Text/Text';
|
||||
import { ExtendedSidebar } from '../UI/UI';
|
||||
|
||||
interface IElementsSidebarProps {
|
||||
containers: Map<string, IContainerModel>
|
||||
mainContainer: IContainerModel
|
||||
symbols: Map<string, ISymbolModel>
|
||||
selectedContainer: IContainerModel | undefined
|
||||
selectedExtendedSidebar: ExtendedSidebar
|
||||
onPropertyChange: (
|
||||
key: string,
|
||||
value: string | number | boolean | number[],
|
||||
type?: PropertyType
|
||||
) => void
|
||||
selectContainer: (containerId: string) => void
|
||||
addContainer: (index: number, type: string, parent: string) => void
|
||||
onExpandChange: (value: ExtendedSidebar) => void
|
||||
}
|
||||
|
||||
function RemoveBorderClasses(target: HTMLButtonElement, exception: string = ''): void {
|
||||
const bordersClasses = ['border-t-8', 'border-8', 'border-b-8'].filter(className => className !== exception);
|
||||
target.classList.remove(...bordersClasses);
|
||||
}
|
||||
|
||||
function HandleDragLeave(event: React.DragEvent): void {
|
||||
const target: HTMLButtonElement = event.target as HTMLButtonElement;
|
||||
RemoveBorderClasses(target);
|
||||
}
|
||||
|
||||
function HandleDragOver(
|
||||
event: React.DragEvent,
|
||||
mainContainer: IContainerModel
|
||||
): void {
|
||||
event.preventDefault();
|
||||
const target: HTMLButtonElement = event.target as HTMLButtonElement;
|
||||
const rect = target.getBoundingClientRect();
|
||||
const y = event.clientY - rect.top; // y position within the element.
|
||||
|
||||
if (target.id === mainContainer.properties.id) {
|
||||
target.classList.add('border-8');
|
||||
return;
|
||||
}
|
||||
|
||||
if (y < 12) {
|
||||
RemoveBorderClasses(target, 'border-t-8');
|
||||
target.classList.add('border-t-8');
|
||||
} else if (y < 24) {
|
||||
RemoveBorderClasses(target, 'border-8');
|
||||
target.classList.add('border-8');
|
||||
} else {
|
||||
RemoveBorderClasses(target, 'border-b-8');
|
||||
target.classList.add('border-b-8');
|
||||
}
|
||||
}
|
||||
|
||||
function HandleOnDrop(
|
||||
event: React.DragEvent,
|
||||
containers: Map<string, IContainerModel>,
|
||||
mainContainer: IContainerModel,
|
||||
addContainer: (index: number, type: string, parent: string) => void
|
||||
): void {
|
||||
event.preventDefault();
|
||||
const type = event.dataTransfer.getData('type');
|
||||
const target: HTMLButtonElement = event.target as HTMLButtonElement;
|
||||
RemoveBorderClasses(target);
|
||||
|
||||
const targetContainer: IContainerModel | undefined = FindContainerById(
|
||||
containers,
|
||||
target.id
|
||||
);
|
||||
|
||||
if (targetContainer === undefined) {
|
||||
throw new Error('[handleOnDrop] Tried to drop onto a unknown container!');
|
||||
}
|
||||
|
||||
if (targetContainer === mainContainer) {
|
||||
// if the container is the root, only add type as child
|
||||
addContainer(
|
||||
targetContainer.children.length,
|
||||
type,
|
||||
targetContainer.properties.id);
|
||||
return;
|
||||
}
|
||||
|
||||
const parent = FindContainerById(containers, targetContainer.properties.parentId);
|
||||
if (parent === null ||
|
||||
parent === undefined) {
|
||||
throw new Error('[handleDrop] Tried to drop into a child container without a parent!');
|
||||
}
|
||||
|
||||
const rect = target.getBoundingClientRect();
|
||||
const y = event.clientY - rect.top; // y position within the element.
|
||||
|
||||
// locate the hitboxes
|
||||
if (y < 12) {
|
||||
const index = parent.children.indexOf(targetContainer.properties.id);
|
||||
addContainer(
|
||||
index,
|
||||
type,
|
||||
parent.properties.id
|
||||
);
|
||||
} else if (y < 24) {
|
||||
addContainer(
|
||||
targetContainer.children.length,
|
||||
type,
|
||||
targetContainer.properties.id);
|
||||
} else {
|
||||
const index = parent.children.indexOf(targetContainer.properties.id);
|
||||
addContainer(
|
||||
index + 1,
|
||||
type,
|
||||
parent.properties.id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function ElementsSidebar(props: IElementsSidebarProps): JSX.Element {
|
||||
// States
|
||||
const divRef = React.useRef<HTMLDivElement>(null);
|
||||
const [,height] = useSize(divRef);
|
||||
|
||||
// Render
|
||||
const it = MakeRecursionDFSIterator(props.mainContainer, props.containers, 0, [0, 0], true);
|
||||
const containers = [...it];
|
||||
function Row({
|
||||
index, style
|
||||
}: {
|
||||
index: number
|
||||
style: React.CSSProperties
|
||||
}): JSX.Element {
|
||||
const { container, depth } = containers[index];
|
||||
const key = container.properties.id.toString();
|
||||
const text = container.properties.displayedText === key
|
||||
? `${key}`
|
||||
: `${container.properties.displayedText}`;
|
||||
|
||||
const isSelected = props.selectedContainer !== undefined &&
|
||||
props.selectedContainer !== null &&
|
||||
props.selectedContainer.properties.id === container.properties.id;
|
||||
|
||||
return (
|
||||
ElementsListRow(
|
||||
key,
|
||||
container,
|
||||
depth,
|
||||
isSelected,
|
||||
style,
|
||||
text,
|
||||
props.containers,
|
||||
props.mainContainer,
|
||||
props.addContainer,
|
||||
props.selectContainer
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex flex-row h-full w-full' >
|
||||
{props.selectedExtendedSidebar === ExtendedSidebar.Property &&
|
||||
<div className='flex flex-1 flex-col w-64 border-r-2 border-slate-400'>
|
||||
<ContainerProperties
|
||||
properties={props.selectedContainer?.properties}
|
||||
symbols={props.symbols}
|
||||
onChange={props.onPropertyChange}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
<div className='flex w-64' ref={divRef}>
|
||||
<div className='w-6'>
|
||||
<ToggleSideBar
|
||||
title={Text({ textId: '@Properties' })}
|
||||
checked={props.selectedExtendedSidebar === ExtendedSidebar.Property}
|
||||
onClick={() => {
|
||||
const newValue = props.selectedExtendedSidebar !== ExtendedSidebar.Property
|
||||
? ExtendedSidebar.Property
|
||||
: ExtendedSidebar.None;
|
||||
props.onExpandChange(newValue);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<List
|
||||
itemCount={containers.length}
|
||||
itemSize={35}
|
||||
height={height}
|
||||
width={'100%'}
|
||||
>
|
||||
{Row}
|
||||
</List>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ElementsListRow(
|
||||
key: string,
|
||||
container: IContainerModel,
|
||||
depth: number,
|
||||
isSelected: boolean,
|
||||
style: React.CSSProperties,
|
||||
text: string,
|
||||
containers: Map<string, IContainerModel>,
|
||||
mainContainer: IContainerModel,
|
||||
addContainer: (index: number, type: string, parent: string) => void,
|
||||
selectContainer: (containerId: string) => void
|
||||
): JSX.Element {
|
||||
const verticalBars: JSX.Element[] = [];
|
||||
const verticalBarSelectedClass = isSelected
|
||||
? 'border-l-blue-400 group-hover:border-l-blue-300'
|
||||
: 'border-l-slate-400 group-hover:border-l-slate-300';
|
||||
for (let i = 0; i < depth; i++) {
|
||||
verticalBars.push(
|
||||
<span
|
||||
key={`${key}-${i}`}
|
||||
className={`h-full border-l-2 pr-2 ${verticalBarSelectedClass}`}
|
||||
></span>
|
||||
);
|
||||
}
|
||||
|
||||
const buttonSelectedClass: string = isSelected
|
||||
? 'bg-blue-500 shadow-lg shadow-blue-500/60 hover:bg-blue-600 hover:shadow-blue-500 text-slate-50'
|
||||
: 'bg-slate-300/60 hover:bg-slate-400 hover:shadow-slate-400';
|
||||
|
||||
return <button type="button"
|
||||
className={`transition-all border-blue-500 hover:shadow-lg elements-sidebar-row whitespace-pre
|
||||
text-left text-sm font-medium flex items-center align-middle group ${container.properties.type} ${buttonSelectedClass}`}
|
||||
id={key}
|
||||
key={key}
|
||||
style={style}
|
||||
title={container.properties.warning}
|
||||
onClick={() => { selectContainer(container.properties.id); }}
|
||||
onDrop={(event) => { HandleOnDrop(event, containers, mainContainer, addContainer); }}
|
||||
onDragOver={(event) => { HandleDragOver(event, mainContainer); }}
|
||||
onDragLeave={(event) => { HandleDragLeave(event); }}
|
||||
>
|
||||
{verticalBars}
|
||||
{text}
|
||||
{container.properties.warning.length > 0 &&
|
||||
<ExclamationTriangleIcon className='pl-2 w-7' />}
|
||||
</button>;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue