Merged PR 167: Add Flex and fix bugs (read desc)
Note: The branch name does not fit the new features. - Implement Flex with simplex - Enable rigid body by default (removed IsRigidBody property) <=== possibly a bad idea - Sort children in add and update properties - Implement MaxWidth - Add more docs Fixes : - .env.production url - Symbols: not blocking the linked container when the parent is moving
This commit is contained in:
parent
ec3fddec9d
commit
7f3f6a489a
43 changed files with 1127 additions and 453 deletions
|
@ -22,9 +22,12 @@ describe.concurrent('Elements sidebar', () => {
|
|||
y: 0,
|
||||
width: 2000,
|
||||
height: 100,
|
||||
margin: {},
|
||||
minWidth: 1,
|
||||
type: 'type',
|
||||
maxWidth: Infinity,
|
||||
isFlex: false,
|
||||
XPositionReference: XPositionReference.Left,
|
||||
isRigidBody: false,
|
||||
isAnchor: false
|
||||
},
|
||||
userData: {}
|
||||
|
@ -35,7 +38,6 @@ describe.concurrent('Elements sidebar', () => {
|
|||
OnPropertyChange={() => {}}
|
||||
SelectContainer={() => {}}
|
||||
DeleteContainer={() => {}}
|
||||
AddContainer={() => {}}
|
||||
/>);
|
||||
|
||||
expect(screen.getByText(/Elements/i));
|
||||
|
@ -56,8 +58,11 @@ describe.concurrent('Elements sidebar', () => {
|
|||
y: 0,
|
||||
width: 2000,
|
||||
height: 100,
|
||||
margin: {},
|
||||
minWidth: 1,
|
||||
isRigidBody: false,
|
||||
isFlex: false,
|
||||
maxWidth: Infinity,
|
||||
type: 'type',
|
||||
isAnchor: false,
|
||||
XPositionReference: XPositionReference.Left
|
||||
},
|
||||
|
@ -73,7 +78,6 @@ describe.concurrent('Elements sidebar', () => {
|
|||
OnPropertyChange={() => {}}
|
||||
SelectContainer={() => {}}
|
||||
DeleteContainer={() => {}}
|
||||
AddContainer={() => {}}
|
||||
/>);
|
||||
|
||||
expect(screen.getByText(/Elements/i));
|
||||
|
@ -119,7 +123,10 @@ describe.concurrent('Elements sidebar', () => {
|
|||
width: 2000,
|
||||
height: 100,
|
||||
XPositionReference: XPositionReference.Left,
|
||||
isRigidBody: false,
|
||||
margin: {},
|
||||
isFlex: false,
|
||||
maxWidth: Infinity,
|
||||
type: 'type',
|
||||
isAnchor: false
|
||||
},
|
||||
userData: {}
|
||||
|
@ -139,7 +146,10 @@ describe.concurrent('Elements sidebar', () => {
|
|||
minWidth: 1,
|
||||
width: 0,
|
||||
height: 0,
|
||||
isRigidBody: false,
|
||||
margin: {},
|
||||
isFlex: false,
|
||||
maxWidth: Infinity,
|
||||
type: 'type',
|
||||
isAnchor: false,
|
||||
XPositionReference: XPositionReference.Left
|
||||
},
|
||||
|
@ -158,11 +168,14 @@ describe.concurrent('Elements sidebar', () => {
|
|||
displayedText: 'child-2',
|
||||
x: 0,
|
||||
y: 0,
|
||||
margin: {},
|
||||
minWidth: 1,
|
||||
width: 0,
|
||||
height: 0,
|
||||
XPositionReference: XPositionReference.Left,
|
||||
isRigidBody: false,
|
||||
isFlex: false,
|
||||
maxWidth: Infinity,
|
||||
type: 'type',
|
||||
isAnchor: false
|
||||
},
|
||||
userData: {}
|
||||
|
@ -178,7 +191,6 @@ describe.concurrent('Elements sidebar', () => {
|
|||
OnPropertyChange={() => {}}
|
||||
SelectContainer={() => {}}
|
||||
DeleteContainer={() => {}}
|
||||
AddContainer={() => {}}
|
||||
/>);
|
||||
|
||||
expect(screen.getByText(/Elements/i));
|
||||
|
@ -204,7 +216,10 @@ describe.concurrent('Elements sidebar', () => {
|
|||
width: 2000,
|
||||
height: 100,
|
||||
XPositionReference: XPositionReference.Left,
|
||||
isRigidBody: false,
|
||||
margin: {},
|
||||
isFlex: false,
|
||||
maxWidth: Infinity,
|
||||
type: 'type',
|
||||
isAnchor: false
|
||||
},
|
||||
userData: {}
|
||||
|
@ -224,7 +239,10 @@ describe.concurrent('Elements sidebar', () => {
|
|||
width: 0,
|
||||
height: 0,
|
||||
XPositionReference: XPositionReference.Left,
|
||||
isRigidBody: false,
|
||||
margin: {},
|
||||
isFlex: false,
|
||||
maxWidth: Infinity,
|
||||
type: 'type',
|
||||
isAnchor: false
|
||||
},
|
||||
userData: {}
|
||||
|
@ -245,7 +263,6 @@ describe.concurrent('Elements sidebar', () => {
|
|||
OnPropertyChange={() => {}}
|
||||
SelectContainer={selectContainer}
|
||||
DeleteContainer={() => {}}
|
||||
AddContainer={() => {}}
|
||||
/>);
|
||||
|
||||
expect(screen.getByText(/Elements/i));
|
||||
|
@ -269,7 +286,6 @@ describe.concurrent('Elements sidebar', () => {
|
|||
OnPropertyChange={() => {}}
|
||||
SelectContainer={selectContainer}
|
||||
DeleteContainer={() => {}}
|
||||
AddContainer={() => {}}
|
||||
/>);
|
||||
|
||||
expect((propertyId as HTMLInputElement).value === 'main').toBeFalsy();
|
||||
|
|
|
@ -5,10 +5,10 @@ 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, useMouseEvents } from './MouseEventHandlers';
|
||||
import { useMouseEvents } from './MouseEventHandlers';
|
||||
import { IPoint } from '../../Interfaces/IPoint';
|
||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
||||
import { Dispatch, RefObject, SetStateAction } from 'react';
|
||||
import { PropertyType } from '../../Enums/PropertyType';
|
||||
|
||||
interface IElementsSidebarProps {
|
||||
MainContainer: IContainerModel
|
||||
|
@ -16,16 +16,23 @@ interface IElementsSidebarProps {
|
|||
isOpen: boolean
|
||||
isHistoryOpen: boolean
|
||||
SelectedContainer: IContainerModel | undefined
|
||||
OnPropertyChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
|
||||
OnPropertyChange: (
|
||||
key: string,
|
||||
value: string | number | boolean,
|
||||
type?: PropertyType
|
||||
) => void
|
||||
SelectContainer: (containerId: string) => void
|
||||
DeleteContainer: (containerid: string) => void
|
||||
AddContainer: (index: number, type: string, parent: string) => void
|
||||
}
|
||||
|
||||
export const ElementsSidebar: React.FC<IElementsSidebarProps> = (props: IElementsSidebarProps): JSX.Element => {
|
||||
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 [isContextMenuOpen, setIsContextMenuOpen] =
|
||||
React.useState<boolean>(false);
|
||||
const [onClickContainerId, setOnClickContainerId] =
|
||||
React.useState<string>('');
|
||||
const [contextMenuPosition, setContextMenuPosition] = React.useState<IPoint>({
|
||||
x: 0,
|
||||
y: 0
|
||||
|
@ -45,71 +52,78 @@ export const ElementsSidebar: React.FC<IElementsSidebarProps> = (props: IElement
|
|||
// Render
|
||||
let isOpenClasses = '-right-64';
|
||||
if (props.isOpen) {
|
||||
isOpenClasses = props.isHistoryOpen
|
||||
? 'right-64'
|
||||
: 'right-0';
|
||||
isOpenClasses = props.isHistoryOpen ? 'right-64' : 'right-0';
|
||||
}
|
||||
|
||||
const it = MakeIterator(props.MainContainer);
|
||||
const containers = [...it];
|
||||
const Row = ({ index, style }: {index: number, style: React.CSSProperties}): JSX.Element => {
|
||||
const Row = ({
|
||||
index,
|
||||
style
|
||||
}: {
|
||||
index: number
|
||||
style: React.CSSProperties
|
||||
}): JSX.Element => {
|
||||
const container = containers[index];
|
||||
const depth: number = getDepth(container);
|
||||
const key = container.properties.id.toString();
|
||||
const text = container.properties.displayedText === key
|
||||
? `${'|\t'.repeat(depth)} ${key}`
|
||||
: `${'|\t'.repeat(depth)} ${container.properties.displayedText} (${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';
|
||||
const text =
|
||||
container.properties.displayedText === key
|
||||
? `${'|\t'.repeat(depth)} ${key}`
|
||||
: `${'|\t'.repeat(depth)} ${
|
||||
container.properties.displayedText
|
||||
} (${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';
|
||||
|
||||
return (
|
||||
<button
|
||||
className={
|
||||
`w-full border-blue-500 elements-sidebar-row whitespace-pre
|
||||
text-left text-sm font-medium transition-all ${selectedClass}`
|
||||
}
|
||||
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}
|
||||
onDrop={(event) => handleOnDrop(event, props.MainContainer, props.AddContainer)}
|
||||
onDragOver={(event) => handleDragOver(event, props.MainContainer)}
|
||||
onDragLeave={(event) => handleDragLeave(event)}
|
||||
onClick={() => props.SelectContainer(container.properties.id)}
|
||||
>
|
||||
{ text }
|
||||
{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'>
|
||||
<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'
|
||||
className="List divide-y divide-black"
|
||||
itemCount={containers.length}
|
||||
itemSize={35}
|
||||
height={384}
|
||||
width={256}
|
||||
>
|
||||
{ Row }
|
||||
{Row}
|
||||
</List>
|
||||
</div>
|
||||
<Menu
|
||||
className='transition-opacity rounded bg-slate-200 py-1 drop-shadow-xl'
|
||||
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);
|
||||
}} />
|
||||
<MenuItem
|
||||
className="contextmenu-item"
|
||||
text="Delete"
|
||||
onClick={() => {
|
||||
setIsContextMenuOpen(false);
|
||||
props.DeleteContainer(onClickContainerId);
|
||||
}}
|
||||
/>
|
||||
</Menu>
|
||||
<Properties
|
||||
properties={props.SelectedContainer?.properties}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import React, { RefObject, Dispatch, SetStateAction, useEffect } from 'react';
|
||||
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||
import { IPoint } from '../../Interfaces/IPoint';
|
||||
import { findContainerById } from '../../utils/itertools';
|
||||
|
||||
export function useMouseEvents(
|
||||
isContextMenuOpen: boolean,
|
||||
|
@ -81,95 +79,3 @@ export function handleLeftClick(
|
|||
setOnClickContainerId('');
|
||||
}
|
||||
|
||||
export function removeBorderClasses(target: HTMLButtonElement): void {
|
||||
const bordersClasses = ['border-t-8', 'border-8', 'border-b-8'];
|
||||
target.classList.remove(...bordersClasses);
|
||||
}
|
||||
|
||||
export function handleDragLeave(event: React.DragEvent): void {
|
||||
const target: HTMLButtonElement = event.target as HTMLButtonElement;
|
||||
removeBorderClasses(target);
|
||||
}
|
||||
|
||||
export 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.
|
||||
removeBorderClasses(target);
|
||||
|
||||
if (target.id === mainContainer.properties.id) {
|
||||
target.classList.add('border-8');
|
||||
return;
|
||||
}
|
||||
|
||||
if (y < 12) {
|
||||
target.classList.add('border-t-8');
|
||||
} else if (y < 24) {
|
||||
target.classList.add('border-8');
|
||||
} else {
|
||||
target.classList.add('border-b-8');
|
||||
}
|
||||
}
|
||||
|
||||
export function handleOnDrop(
|
||||
event: React.DragEvent,
|
||||
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(
|
||||
mainContainer,
|
||||
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;
|
||||
}
|
||||
|
||||
if (targetContainer.parent === null ||
|
||||
targetContainer.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 = targetContainer.parent.children.indexOf(targetContainer);
|
||||
addContainer(
|
||||
index,
|
||||
type,
|
||||
targetContainer.parent.properties.id
|
||||
);
|
||||
} else if (y < 24) {
|
||||
addContainer(
|
||||
targetContainer.children.length,
|
||||
type,
|
||||
targetContainer.properties.id);
|
||||
} else {
|
||||
const index = targetContainer.parent.children.indexOf(targetContainer);
|
||||
addContainer(
|
||||
index + 1,
|
||||
type,
|
||||
targetContainer.parent.properties.id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue