Implement basic form for properties to avoid history pollution
This commit is contained in:
parent
7c16d6c97d
commit
af73fa6083
8 changed files with 167 additions and 28 deletions
|
@ -1,4 +1,4 @@
|
|||
import { Dispatch, SetStateAction } from 'react';
|
||||
import React, { Dispatch, SetStateAction } from 'react';
|
||||
import { HistoryState } from '../../Interfaces/HistoryState';
|
||||
import { Configuration } from '../../Interfaces/Configuration';
|
||||
import { ContainerModel, IContainerModel } from '../../Interfaces/ContainerModel';
|
||||
|
@ -60,7 +60,7 @@ export function DeleteContainer(
|
|||
}
|
||||
|
||||
if (container === null || container === undefined) {
|
||||
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
||||
throw new Error('[DeleteContainer] Container model was not found among children of the main container!');
|
||||
}
|
||||
|
||||
if (container.parent != null) {
|
||||
|
@ -266,6 +266,76 @@ export function OnPropertyChange(
|
|||
setHistoryCurrentStep(history.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handled the property change event in the properties form
|
||||
* @param key Property name
|
||||
* @param value New value of the property
|
||||
* @returns void
|
||||
*/
|
||||
export function OnPropertiesSubmit(
|
||||
event: React.SyntheticEvent<HTMLFormElement>,
|
||||
refs: Array<React.RefObject<HTMLInputElement>>,
|
||||
fullHistory: HistoryState[],
|
||||
historyCurrentStep: number,
|
||||
setHistory: Dispatch<SetStateAction<HistoryState[]>>,
|
||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||
): void {
|
||||
event.preventDefault();
|
||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||
const current = history[history.length - 1];
|
||||
|
||||
if (current.SelectedContainer === null ||
|
||||
current.SelectedContainer === undefined) {
|
||||
throw new Error('[OnPropertyChange] Property was changed before selecting a Container');
|
||||
}
|
||||
|
||||
if (parent === null) {
|
||||
const selectedContainerClone: IContainerModel = structuredClone(current.SelectedContainer);
|
||||
for (const ref of refs) {
|
||||
const input = ref.current;
|
||||
if (input instanceof HTMLInputElement) {
|
||||
(selectedContainerClone.properties as any)[input.id] = input.value;
|
||||
}
|
||||
}
|
||||
setHistory(history.concat([{
|
||||
LastAction: 'Change property of main',
|
||||
MainContainer: selectedContainerClone,
|
||||
SelectedContainer: selectedContainerClone,
|
||||
SelectedContainerId: selectedContainerClone.properties.id,
|
||||
TypeCounters: Object.assign({}, current.TypeCounters)
|
||||
}]));
|
||||
setHistoryCurrentStep(history.length);
|
||||
return;
|
||||
}
|
||||
|
||||
const mainContainerClone: IContainerModel = structuredClone(current.MainContainer);
|
||||
const container: ContainerModel | undefined = findContainerById(mainContainerClone, current.SelectedContainer.properties.id);
|
||||
|
||||
if (container === null || container === undefined) {
|
||||
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
||||
}
|
||||
|
||||
for (const ref of refs) {
|
||||
const input = ref.current;
|
||||
if (input instanceof HTMLInputElement) {
|
||||
(container.properties as any)[input.id] = input.value;
|
||||
}
|
||||
}
|
||||
|
||||
if (container.properties.isRigidBody) {
|
||||
RecalculatePhysics(container);
|
||||
}
|
||||
|
||||
setHistory(history.concat([{
|
||||
LastAction: `Change property of container ${container.properties.id}`,
|
||||
MainContainer: mainContainerClone,
|
||||
SelectedContainer: container,
|
||||
SelectedContainerId: container.properties.id,
|
||||
TypeCounters: Object.assign({}, current.TypeCounters)
|
||||
}]));
|
||||
setHistoryCurrentStep(history.length);
|
||||
}
|
||||
|
||||
// TODO put this in a different file
|
||||
|
||||
export function RecalculatePhysics(container: IContainerModel): IContainerModel {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Configuration } from '../../Interfaces/Configuration';
|
|||
import { SVG } from '../SVG/SVG';
|
||||
import { HistoryState } from '../../Interfaces/HistoryState';
|
||||
import { UI } from '../UI/UI';
|
||||
import { SelectContainer, DeleteContainer, OnPropertyChange, AddContainerToSelectedContainer, AddContainer } from './ContainerOperations';
|
||||
import { SelectContainer, DeleteContainer, OnPropertyChange, AddContainerToSelectedContainer, AddContainer, OnPropertiesSubmit } from './ContainerOperations';
|
||||
import { SaveEditorAsJSON, SaveEditorAsSVG } from './Save';
|
||||
import { onKeyDown } from './Shortcuts';
|
||||
|
||||
|
@ -72,6 +72,14 @@ const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
|||
setHistory,
|
||||
setHistoryCurrentStep
|
||||
)}
|
||||
OnPropertiesSubmit={(event, refs) => OnPropertiesSubmit(
|
||||
event,
|
||||
refs,
|
||||
history,
|
||||
historyCurrentStep,
|
||||
setHistory,
|
||||
setHistoryCurrentStep
|
||||
)}
|
||||
AddContainerToSelectedContainer={(type) => AddContainerToSelectedContainer(
|
||||
type,
|
||||
configuration,
|
||||
|
|
|
@ -14,6 +14,7 @@ interface IElementsSidebarProps {
|
|||
isHistoryOpen: boolean
|
||||
SelectedContainer: IContainerModel | null
|
||||
OnPropertyChange: (key: string, value: string | number | boolean) => void
|
||||
OnPropertiesSubmit: (event: React.FormEvent<HTMLFormElement>, refs: Array<React.RefObject<HTMLInputElement>>) => void
|
||||
SelectContainer: (container: IContainerModel) => void
|
||||
DeleteContainer: (containerid: string) => void
|
||||
AddContainer: (index: number, type: string, parent: string) => void
|
||||
|
@ -145,7 +146,11 @@ export const ElementsSidebar: React.FC<IElementsSidebarProps> = (props: IElement
|
|||
props.DeleteContainer(onClickContainerId);
|
||||
}} />
|
||||
</Menu>
|
||||
<Properties properties={props.SelectedContainer?.properties} onChange={props.OnPropertyChange}></Properties>
|
||||
<Properties
|
||||
properties={props.SelectedContainer?.properties}
|
||||
onChange={props.OnPropertyChange}
|
||||
onSubmit={props.OnPropertiesSubmit}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -38,13 +38,8 @@ export const MainMenu: React.FC<IMainMenuProps> = (props) => {
|
|||
</form>
|
||||
<button
|
||||
onClick={() => setWindowState(WindowState.MAIN)}
|
||||
className='block text-sm
|
||||
mt-8 py-2 px-4
|
||||
rounded-full border-0
|
||||
font-semibold
|
||||
transition-all
|
||||
bg-blue-100 text-blue-700
|
||||
hover:bg-blue-200'
|
||||
className='normal-btn block
|
||||
mt-8 '
|
||||
>
|
||||
Go back
|
||||
</button>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import * as React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import ContainerProperties from '../../Interfaces/Properties';
|
||||
import { INPUT_TYPES } from './PropertiesInputTypes';
|
||||
|
||||
interface IPropertiesProps {
|
||||
properties?: ContainerProperties
|
||||
onChange: (key: string, value: string | number | boolean) => void
|
||||
onSubmit: (event: React.FormEvent<HTMLFormElement>, refs: Array<React.RefObject<HTMLInputElement>>) => void
|
||||
}
|
||||
|
||||
export const Properties: React.FC<IPropertiesProps> = (props: IPropertiesProps) => {
|
||||
|
@ -12,14 +13,35 @@ export const Properties: React.FC<IPropertiesProps> = (props: IPropertiesProps)
|
|||
return <div></div>;
|
||||
}
|
||||
|
||||
const [isDynamicInput, setIsDynamicInput] = useState<boolean>(true);
|
||||
|
||||
const groupInput: React.ReactNode[] = [];
|
||||
const refs: Array<React.RefObject<HTMLInputElement>> = [];
|
||||
Object
|
||||
.entries(props.properties)
|
||||
.forEach((pair) => handleProperties(pair, groupInput, props.onChange));
|
||||
.forEach((pair) => handleProperties(pair, groupInput, refs, isDynamicInput, props.onChange));
|
||||
|
||||
const form = isDynamicInput
|
||||
? <div>
|
||||
{ groupInput }
|
||||
</div>
|
||||
: <form
|
||||
key={props.properties.id}
|
||||
onSubmit={(event) => props.onSubmit(event, refs)}
|
||||
>
|
||||
<input type='submit' className='normal-btn' value='Submit'/>
|
||||
{ groupInput }
|
||||
</form>
|
||||
;
|
||||
|
||||
return (
|
||||
<div className='p-3 bg-slate-200 h-3/5 overflow-y-auto'>
|
||||
{ groupInput }
|
||||
<div className='h-3/5 p-3 bg-slate-200 overflow-y-auto'>
|
||||
<input
|
||||
type='checkbox'
|
||||
onChange={() => { setIsDynamicInput(!isDynamicInput); }}
|
||||
checked={isDynamicInput}
|
||||
/>
|
||||
{ form }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -27,6 +49,8 @@ export const Properties: React.FC<IPropertiesProps> = (props: IPropertiesProps)
|
|||
const handleProperties = (
|
||||
[key, value]: [string, string | number],
|
||||
groupInput: React.ReactNode[],
|
||||
refs: Array<React.RefObject<HTMLInputElement>>,
|
||||
isDynamicInput: boolean,
|
||||
onChange: (key: string, value: string | number | boolean) => void
|
||||
): void => {
|
||||
const id = `property-${key}`;
|
||||
|
@ -42,9 +66,11 @@ const handleProperties = (
|
|||
type = INPUT_TYPES[key];
|
||||
}
|
||||
|
||||
const isDisabled = ['id', 'parentId'].includes(key);
|
||||
///
|
||||
const ref: React.RefObject<HTMLInputElement> = React.useRef<HTMLInputElement>(null);
|
||||
refs.push(ref);
|
||||
|
||||
const isDisabled = ['id', 'parentId'].includes(key);
|
||||
if (isDynamicInput) {
|
||||
groupInput.push(
|
||||
<div key={id} className='mt-4'>
|
||||
<label className='text-sm font-medium text-gray-800' htmlFor={id}>{key}</label>
|
||||
|
@ -56,6 +82,7 @@ const handleProperties = (
|
|||
'
|
||||
type={type}
|
||||
id={id}
|
||||
ref={ref}
|
||||
value={value}
|
||||
checked={checked}
|
||||
onChange={(event) => {
|
||||
|
@ -69,4 +96,26 @@ const handleProperties = (
|
|||
/>
|
||||
</div>
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
///
|
||||
groupInput.push(
|
||||
<div key={id} className='mt-4'>
|
||||
<label className='text-sm font-medium text-gray-800' htmlFor={id}>{key}</label>
|
||||
<input
|
||||
className='text-base font-medium transition-all text-gray-800 mt-1 block w-full px-3 py-2
|
||||
bg-white border-2 border-white rounded-lg placeholder-gray-800
|
||||
focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500
|
||||
disabled:bg-slate-300 disabled:text-gray-500 disabled:border-slate-300 disabled:shadow-none
|
||||
'
|
||||
type={type}
|
||||
id={key}
|
||||
ref={ref}
|
||||
defaultValue={value}
|
||||
defaultChecked={checked}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@ interface IUIProps {
|
|||
SelectContainer: (container: ContainerModel) => void
|
||||
DeleteContainer: (containerId: string) => void
|
||||
OnPropertyChange: (key: string, value: string | number | boolean) => void
|
||||
OnPropertiesSubmit: (event: React.FormEvent<HTMLFormElement>, refs: Array<React.RefObject<HTMLInputElement>>) => void
|
||||
AddContainerToSelectedContainer: (type: string) => void
|
||||
AddContainer: (index: number, type: string, parentId: string) => void
|
||||
SaveEditorAsJSON: () => void
|
||||
|
@ -59,6 +60,7 @@ export const UI: React.FunctionComponent<IUIProps> = (props: IUIProps) => {
|
|||
isOpen={isElementsSidebarOpen}
|
||||
isHistoryOpen={isHistoryOpen}
|
||||
OnPropertyChange={props.OnPropertyChange}
|
||||
OnPropertiesSubmit={props.OnPropertiesSubmit}
|
||||
SelectContainer={props.SelectContainer}
|
||||
DeleteContainer={props.DeleteContainer}
|
||||
AddContainer={props.AddContainer}
|
||||
|
|
|
@ -23,6 +23,16 @@
|
|||
@apply transition-all bg-blue-100 hover:bg-blue-200 text-blue-700 text-lg font-semibold p-8 rounded-lg
|
||||
}
|
||||
|
||||
.normal-btn {
|
||||
@apply text-sm
|
||||
py-2 px-4
|
||||
rounded-full border-0
|
||||
font-semibold
|
||||
transition-all
|
||||
bg-blue-100 text-blue-700
|
||||
hover:bg-blue-200
|
||||
}
|
||||
|
||||
.floating-btn {
|
||||
@apply h-full w-full text-white align-middle items-center justify-center
|
||||
}
|
||||
|
|
|
@ -30,9 +30,9 @@ export const DEFAULT_MAINCONTAINER_PROPS: Properties = {
|
|||
parentId: 'null',
|
||||
x: 0,
|
||||
y: 0,
|
||||
isRigidBody: false,
|
||||
width: DEFAULT_CONFIG.MainContainer.Width,
|
||||
height: DEFAULT_CONFIG.MainContainer.Height,
|
||||
isRigidBody: false,
|
||||
fillOpacity: 0,
|
||||
stroke: 'black'
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue