Compare commits
No commits in common. "4c10b0f8d77bfbf876130304f12541e3fd7a4f59" and "f1e23260731b65cb5e8820777d346cff6b071063" have entirely different histories.
4c10b0f8d7
...
f1e2326073
5 changed files with 41 additions and 124 deletions
|
@ -12,8 +12,8 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
SelectedContainer={null}
|
SelectedContainer={null}
|
||||||
onPropertyChange={() => {}}
|
onPropertyChange={() => {}}
|
||||||
SelectContainer={() => {}}
|
selectContainer={() => {}}
|
||||||
DeleteContainer={() => {}}
|
deleteContainer={() => {}}
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
expect(screen.getByText(/Elements/i));
|
expect(screen.getByText(/Elements/i));
|
||||||
|
@ -40,8 +40,8 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
SelectedContainer={null}
|
SelectedContainer={null}
|
||||||
onPropertyChange={() => {}}
|
onPropertyChange={() => {}}
|
||||||
SelectContainer={() => {}}
|
selectContainer={() => {}}
|
||||||
DeleteContainer={() => {}}
|
deleteContainer={() => {}}
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
expect(screen.getByText(/Elements/i));
|
expect(screen.getByText(/Elements/i));
|
||||||
|
@ -70,8 +70,8 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
SelectedContainer={MainContainer}
|
SelectedContainer={MainContainer}
|
||||||
onPropertyChange={() => {}}
|
onPropertyChange={() => {}}
|
||||||
SelectContainer={() => {}}
|
selectContainer={() => {}}
|
||||||
DeleteContainer={() => {}}
|
deleteContainer={() => {}}
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
expect(screen.getByText(/Elements/i));
|
expect(screen.getByText(/Elements/i));
|
||||||
|
@ -155,8 +155,8 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
SelectedContainer={MainContainer}
|
SelectedContainer={MainContainer}
|
||||||
onPropertyChange={() => {}}
|
onPropertyChange={() => {}}
|
||||||
SelectContainer={() => {}}
|
selectContainer={() => {}}
|
||||||
DeleteContainer={() => {}}
|
deleteContainer={() => {}}
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
expect(screen.getByText(/Elements/i));
|
expect(screen.getByText(/Elements/i));
|
||||||
|
@ -208,8 +208,8 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
SelectedContainer={SelectedContainer}
|
SelectedContainer={SelectedContainer}
|
||||||
onPropertyChange={() => {}}
|
onPropertyChange={() => {}}
|
||||||
SelectContainer={selectContainer}
|
selectContainer={selectContainer}
|
||||||
DeleteContainer={() => {}}
|
deleteContainer={() => {}}
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
expect(screen.getByText(/Elements/i));
|
expect(screen.getByText(/Elements/i));
|
||||||
|
@ -230,8 +230,8 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
SelectedContainer={SelectedContainer}
|
SelectedContainer={SelectedContainer}
|
||||||
onPropertyChange={() => {}}
|
onPropertyChange={() => {}}
|
||||||
SelectContainer={selectContainer}
|
selectContainer={selectContainer}
|
||||||
DeleteContainer={() => {}}
|
deleteContainer={() => {}}
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
expect((propertyId as HTMLInputElement).value === 'main').toBeFalsy();
|
expect((propertyId as HTMLInputElement).value === 'main').toBeFalsy();
|
||||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Properties } from '../Properties/Properties';
|
import { Properties } from '../Properties/Properties';
|
||||||
import { IContainerModel } from '../../Interfaces/ContainerModel';
|
import { IContainerModel } from '../../Interfaces/ContainerModel';
|
||||||
import { findContainerById, getDepth, MakeIterator } from '../../utils/itertools';
|
import { getDepth, MakeIterator } from '../../utils/itertools';
|
||||||
import { Menu } from '../Menu/Menu';
|
import { Menu } from '../Menu/Menu';
|
||||||
import { MenuItem } from '../Menu/MenuItem';
|
import { MenuItem } from '../Menu/MenuItem';
|
||||||
|
|
||||||
|
@ -12,9 +12,8 @@ interface IElementsSidebarProps {
|
||||||
isHistoryOpen: boolean
|
isHistoryOpen: boolean
|
||||||
SelectedContainer: IContainerModel | null
|
SelectedContainer: IContainerModel | null
|
||||||
onPropertyChange: (key: string, value: string) => void
|
onPropertyChange: (key: string, value: string) => void
|
||||||
SelectContainer: (container: IContainerModel) => void
|
selectContainer: (container: IContainerModel) => void
|
||||||
DeleteContainer: (containerid: string) => void
|
deleteContainer: (containerid: string) => void
|
||||||
AddContainer: (index: number, type: string, parent: string) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Point {
|
interface Point {
|
||||||
|
@ -85,68 +84,6 @@ export class ElementsSidebar extends React.PureComponent<IElementsSidebarProps>
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleDragOver(event: React.DragEvent): void {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
public handleOnDrop(event: React.DragEvent): void {
|
|
||||||
event.preventDefault();
|
|
||||||
const type = event.dataTransfer.getData('type');
|
|
||||||
const target: HTMLButtonElement = event.target as HTMLButtonElement;
|
|
||||||
|
|
||||||
if (this.props.MainContainer === null) {
|
|
||||||
throw new Error('[handleOnDrop] Tried to drop into the tree without a required MainContainer!');
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetContainer: IContainerModel | undefined = findContainerById(
|
|
||||||
this.props.MainContainer,
|
|
||||||
target.id
|
|
||||||
);
|
|
||||||
|
|
||||||
if (targetContainer === undefined) {
|
|
||||||
throw new Error('[handleOnDrop] Tried to drop onto a unknown container!');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetContainer === this.props.MainContainer) {
|
|
||||||
// if the container is the root, only add type as child
|
|
||||||
this.props.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);
|
|
||||||
this.props.AddContainer(
|
|
||||||
index,
|
|
||||||
type,
|
|
||||||
targetContainer.parent.properties.id
|
|
||||||
);
|
|
||||||
} else if (y < 24) {
|
|
||||||
this.props.AddContainer(
|
|
||||||
targetContainer.children.length,
|
|
||||||
type,
|
|
||||||
targetContainer.properties.id);
|
|
||||||
} else {
|
|
||||||
const index = targetContainer.parent.children.indexOf(targetContainer);
|
|
||||||
this.props.AddContainer(
|
|
||||||
index + 1,
|
|
||||||
type,
|
|
||||||
targetContainer.parent.properties.id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public iterateChilds(handleContainer: (container: IContainerModel) => void): React.ReactNode {
|
public iterateChilds(handleContainer: (container: IContainerModel) => void): React.ReactNode {
|
||||||
if (this.props.MainContainer == null) {
|
if (this.props.MainContainer == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -191,9 +128,7 @@ export class ElementsSidebar extends React.PureComponent<IElementsSidebarProps>
|
||||||
}
|
}
|
||||||
id={key}
|
id={key}
|
||||||
key={key}
|
key={key}
|
||||||
onDrop={(event) => this.handleOnDrop(event)}
|
onClick={() => this.props.selectContainer(container)}>
|
||||||
onDragOver={(event) => this.handleDragOver(event)}
|
|
||||||
onClick={() => this.props.SelectContainer(container)}>
|
|
||||||
{ text }
|
{ text }
|
||||||
</motion.button>
|
</motion.button>
|
||||||
);
|
);
|
||||||
|
@ -213,7 +148,7 @@ export class ElementsSidebar extends React.PureComponent<IElementsSidebarProps>
|
||||||
y={this.state.contextMenuPosition.y}
|
y={this.state.contextMenuPosition.y}
|
||||||
isOpen={this.state.isContextMenuOpen}
|
isOpen={this.state.isContextMenuOpen}
|
||||||
>
|
>
|
||||||
<MenuItem className='contextmenu-item' text='Delete' onClick={() => this.props.DeleteContainer(this.state.onClickContainerId)} />
|
<MenuItem className='contextmenu-item' text='Delete' onClick={() => this.props.deleteContainer(this.state.onClickContainerId)} />
|
||||||
</Menu>
|
</Menu>
|
||||||
<Properties properties={this.props.SelectedContainer?.properties} onChange={this.props.onPropertyChange}></Properties>
|
<Properties properties={this.props.SelectedContainer?.properties} onChange={this.props.onPropertyChange}></Properties>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,21 +8,14 @@ interface ISidebarProps {
|
||||||
buttonOnClick: (type: string) => void
|
buttonOnClick: (type: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDragStart(event: React.DragEvent<HTMLButtonElement>): void {
|
|
||||||
event.dataTransfer.setData('type', (event.target as HTMLButtonElement).id);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Sidebar extends React.PureComponent<ISidebarProps> {
|
export default class Sidebar extends React.PureComponent<ISidebarProps> {
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
const listElements = this.props.componentOptions.map(componentOption =>
|
const listElements = this.props.componentOptions.map(componentOption =>
|
||||||
<button
|
<button
|
||||||
className='justify-center transition-all sidebar-component'
|
className='justify-center transition-all sidebar-component'
|
||||||
key={componentOption.Type}
|
key={componentOption.Type}
|
||||||
id={componentOption.Type}
|
|
||||||
title={componentOption.Type}
|
title={componentOption.Type}
|
||||||
onClick={() => this.props.buttonOnClick(componentOption.Type)}
|
onClick={() => this.props.buttonOnClick(componentOption.Type)}
|
||||||
draggable={true}
|
|
||||||
onDragStart={(event) => handleDragStart(event)}
|
|
||||||
>
|
>
|
||||||
{truncateString(componentOption.Type, 5)}
|
{truncateString(componentOption.Type, 5)}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -17,8 +17,7 @@ interface IUIProps {
|
||||||
SelectContainer: (container: ContainerModel) => void
|
SelectContainer: (container: ContainerModel) => void
|
||||||
DeleteContainer: (containerId: string) => void
|
DeleteContainer: (containerId: string) => void
|
||||||
OnPropertyChange: (key: string, value: string) => void
|
OnPropertyChange: (key: string, value: string) => void
|
||||||
AddContainerToSelectedContainer: (type: string) => void
|
AddContainer: (type: string) => void
|
||||||
AddContainer: (index: number, type: string, parentId: string) => void
|
|
||||||
SaveEditorAsJSON: () => void
|
SaveEditorAsJSON: () => void
|
||||||
SaveEditorAsSVG: () => void
|
SaveEditorAsSVG: () => void
|
||||||
LoadState: (move: number) => void
|
LoadState: (move: number) => void
|
||||||
|
@ -90,7 +89,7 @@ export class UI extends React.PureComponent<IUIProps, IUIState> {
|
||||||
<Sidebar
|
<Sidebar
|
||||||
componentOptions={this.props.AvailableContainers}
|
componentOptions={this.props.AvailableContainers}
|
||||||
isOpen={this.state.isSidebarOpen}
|
isOpen={this.state.isSidebarOpen}
|
||||||
buttonOnClick={(type: string) => this.props.AddContainerToSelectedContainer(type)}
|
buttonOnClick={(type: string) => this.props.AddContainer(type)}
|
||||||
/>
|
/>
|
||||||
<ElementsSidebar
|
<ElementsSidebar
|
||||||
MainContainer={this.props.current.MainContainer}
|
MainContainer={this.props.current.MainContainer}
|
||||||
|
@ -98,9 +97,8 @@ export class UI extends React.PureComponent<IUIProps, IUIState> {
|
||||||
isOpen={this.state.isElementsSidebarOpen}
|
isOpen={this.state.isElementsSidebarOpen}
|
||||||
isHistoryOpen={this.state.isHistoryOpen}
|
isHistoryOpen={this.state.isHistoryOpen}
|
||||||
onPropertyChange={this.props.OnPropertyChange}
|
onPropertyChange={this.props.OnPropertyChange}
|
||||||
SelectContainer={this.props.SelectContainer}
|
selectContainer={this.props.SelectContainer}
|
||||||
DeleteContainer={this.props.DeleteContainer}
|
deleteContainer={this.props.DeleteContainer}
|
||||||
AddContainer={this.props.AddContainer}
|
|
||||||
/>
|
/>
|
||||||
<History
|
<History
|
||||||
history={this.props.history}
|
history={this.props.history}
|
||||||
|
|
|
@ -195,7 +195,7 @@ class Editor extends React.Component<IEditorProps> {
|
||||||
* @param type The type of container
|
* @param type The type of container
|
||||||
* @returns void
|
* @returns void
|
||||||
*/
|
*/
|
||||||
public AddContainerToSelectedContainer(type: string): void {
|
public AddContainer(type: string): void {
|
||||||
const history = this.getCurrentHistory();
|
const history = this.getCurrentHistory();
|
||||||
const current = history[history.length - 1];
|
const current = history[history.length - 1];
|
||||||
|
|
||||||
|
@ -204,22 +204,13 @@ class Editor extends React.Component<IEditorProps> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parent = current.SelectedContainer;
|
|
||||||
this.AddContainer(parent.children.length, type, parent.properties.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AddContainer(index: number, type: string, parentId: string): void {
|
|
||||||
const history = this.getCurrentHistory();
|
|
||||||
const current = history[history.length - 1];
|
|
||||||
|
|
||||||
if (current.MainContainer === null ||
|
if (current.MainContainer === null ||
|
||||||
current.MainContainer === undefined) {
|
current.MainContainer === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the preset properties from the API
|
// Get the preset properties from the API
|
||||||
const properties = this.props.configuration.AvailableContainers
|
const properties = this.props.configuration.AvailableContainers.find(option => option.Type === type);
|
||||||
.find(option => option.Type === type);
|
|
||||||
|
|
||||||
if (properties === undefined) {
|
if (properties === undefined) {
|
||||||
throw new Error(`[AddContainer] Object type not found. Found: ${type}`);
|
throw new Error(`[AddContainer] Object type not found. Found: ${type}`);
|
||||||
|
@ -239,30 +230,35 @@ class Editor extends React.Component<IEditorProps> {
|
||||||
const clone: IContainerModel = structuredClone(current.MainContainer);
|
const clone: IContainerModel = structuredClone(current.MainContainer);
|
||||||
|
|
||||||
// Find the parent
|
// Find the parent
|
||||||
const parentClone: IContainerModel | undefined = findContainerById(
|
const it = MakeIterator(clone);
|
||||||
clone, parentId
|
let parent: ContainerModel | null = null;
|
||||||
);
|
for (const child of it) {
|
||||||
|
if (child.properties.id === current.SelectedContainer.properties.id) {
|
||||||
|
parent = child as ContainerModel;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (parentClone === null || parentClone === undefined) {
|
if (parent === null) {
|
||||||
throw new Error('[AddContainer] Container model was not found among children of the main container!');
|
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
||||||
}
|
}
|
||||||
|
|
||||||
let x = 0;
|
let x = 0;
|
||||||
const lastChild: IContainerModel | undefined = parentClone.children.at(-1);
|
const lastChild: IContainerModel | undefined = parent.children.at(-1);
|
||||||
if (lastChild !== undefined) {
|
if (lastChild !== undefined) {
|
||||||
x = lastChild.properties.x + Number(lastChild.properties.width);
|
x = lastChild.properties.x + Number(lastChild.properties.width);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the container
|
// Create the container
|
||||||
const newContainer = new ContainerModel(
|
const newContainer = new ContainerModel(
|
||||||
parentClone,
|
parent,
|
||||||
{
|
{
|
||||||
id: `${type}-${count}`,
|
id: `${type}-${count}`,
|
||||||
parentId: parentClone.properties.id,
|
parentId: parent.properties.id,
|
||||||
x,
|
x,
|
||||||
y: 0,
|
y: 0,
|
||||||
width: properties?.Width,
|
width: properties?.Width,
|
||||||
height: parentClone.properties.height,
|
height: parent.properties.height,
|
||||||
...properties.Style
|
...properties.Style
|
||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
|
@ -272,19 +268,15 @@ class Editor extends React.Component<IEditorProps> {
|
||||||
);
|
);
|
||||||
|
|
||||||
// And push it the the parent children
|
// And push it the the parent children
|
||||||
if (index === parentClone.children.length) {
|
parent.children.push(newContainer);
|
||||||
parentClone.children.push(newContainer);
|
|
||||||
} else {
|
|
||||||
parentClone.children.splice(index, 0, newContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the state
|
// Update the state
|
||||||
this.setState({
|
this.setState({
|
||||||
history: history.concat([{
|
history: history.concat([{
|
||||||
MainContainer: clone,
|
MainContainer: clone,
|
||||||
TypeCounters: newCounters,
|
TypeCounters: newCounters,
|
||||||
SelectedContainer: parentClone,
|
SelectedContainer: parent,
|
||||||
SelectedContainerId: parentClone.properties.id
|
SelectedContainerId: parent.properties.id
|
||||||
}]),
|
}]),
|
||||||
historyCurrentStep: history.length
|
historyCurrentStep: history.length
|
||||||
});
|
});
|
||||||
|
@ -339,8 +331,7 @@ class Editor extends React.Component<IEditorProps> {
|
||||||
SelectContainer={(container) => this.SelectContainer(container)}
|
SelectContainer={(container) => this.SelectContainer(container)}
|
||||||
DeleteContainer={(containerId: string) => this.DeleteContainer(containerId)}
|
DeleteContainer={(containerId: string) => this.DeleteContainer(containerId)}
|
||||||
OnPropertyChange={(key, value) => this.OnPropertyChange(key, value)}
|
OnPropertyChange={(key, value) => this.OnPropertyChange(key, value)}
|
||||||
AddContainerToSelectedContainer={(type) => this.AddContainerToSelectedContainer(type)}
|
AddContainer={(type) => this.AddContainer(type)}
|
||||||
AddContainer={(index, type, parentId) => this.AddContainer(index, type, parentId)}
|
|
||||||
SaveEditorAsJSON={() => this.SaveEditorAsJSON()}
|
SaveEditorAsJSON={() => this.SaveEditorAsJSON()}
|
||||||
SaveEditorAsSVG={() => this.SaveEditorAsSVG()}
|
SaveEditorAsSVG={() => this.SaveEditorAsSVG()}
|
||||||
LoadState={(move) => this.LoadState(move)}
|
LoadState={(move) => this.LoadState(move)}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue