Unrefactor Properties form to allow more freedom on the input types and form
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Eric NGUYEN 2022-08-16 11:38:43 +02:00
parent 706f9624cc
commit 0e7e8a47ce
9 changed files with 351 additions and 109 deletions

View file

@ -91,16 +91,15 @@ const Editor: React.FunctionComponent<IEditorProps> = (props) => {
setHistory,
setHistoryCurrentStep
)}
OnPropertyChange={(key, value) => OnPropertyChange(
key, value,
OnPropertyChange={(key, value, isStyle) => OnPropertyChange(
key, value, isStyle,
history,
historyCurrentStep,
setHistory,
setHistoryCurrentStep
)}
OnPropertiesSubmit={(event, properties) => OnPropertiesSubmit(
OnPropertiesSubmit={(event) => OnPropertiesSubmit(
event,
properties,
history,
historyCurrentStep,
setHistory,

View file

@ -1,7 +1,6 @@
import { Dispatch, SetStateAction } from 'react';
import { IContainerModel, ContainerModel } from '../../Interfaces/IContainerModel';
import { IHistoryState } from '../../Interfaces/IHistoryState';
import IProperties from '../../Interfaces/IProperties';
import { findContainerById } from '../../utils/itertools';
import { getCurrentHistory } from './Editor';
import { RecalculatePhysics } from './Behaviors/RigidBodyBehaviors';
@ -17,6 +16,7 @@ import { ImposePosition } from './Behaviors/AnchorBehaviors';
export function OnPropertyChange(
key: string,
value: string | number | boolean,
isStyle: boolean = false,
fullHistory: IHistoryState[],
historyCurrentStep: number,
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
@ -37,10 +37,14 @@ export function OnPropertyChange(
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
}
if (INPUT_TYPES[key] === 'number') {
(container.properties as any)[key] = Number(value);
if (isStyle) {
(container.properties.style as any)[key] = value;
} else {
(container.properties as any)[key] = value;
if (INPUT_TYPES[key] === 'number') {
(container.properties as any)[key] = Number(value);
} else {
(container.properties as any)[key] = value;
}
}
if (container.properties.isAnchor) {
@ -70,7 +74,6 @@ export function OnPropertyChange(
*/
export function OnPropertiesSubmit(
event: React.SyntheticEvent<HTMLFormElement>,
properties: IProperties,
fullHistory: IHistoryState[],
historyCurrentStep: number,
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
@ -92,15 +95,33 @@ export function OnPropertiesSubmit(
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
}
for (const property in properties) {
// Assign container properties
for (const property in container.properties) {
const input = (event.target as HTMLFormElement).querySelector(`#${property}`);
if (input instanceof HTMLInputElement) {
(container.properties as any)[property] = input.value;
if (INPUT_TYPES[property] === 'number') {
(container.properties as any)[property] = Number(input.value);
} else {
(container.properties as any)[property] = input.value;
continue;
}
(container.properties as any)[property] = input.value;
}
}
// Assign cssproperties
for (const styleProperty in container.properties.style) {
const input = (event.target as HTMLFormElement).querySelector(`#${styleProperty}`);
if (input instanceof HTMLInputElement) {
(container.properties.style as any)[styleProperty] = input.value;
if (INPUT_TYPES[styleProperty] === 'number') {
(container.properties.style as any)[styleProperty] = Number(input.value);
continue;
}
(container.properties.style as any)[styleProperty] = input.value;
}
}

View file

@ -14,8 +14,8 @@ interface IElementsSidebarProps {
isOpen: boolean
isHistoryOpen: boolean
SelectedContainer: IContainerModel | null
OnPropertyChange: (key: string, value: string | number | boolean) => void
OnPropertiesSubmit: (event: React.FormEvent<HTMLFormElement>, properties: ContainerProperties) => void
OnPropertyChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
OnPropertiesSubmit: (event: React.FormEvent<HTMLFormElement>) => void
SelectContainer: (container: IContainerModel) => void
DeleteContainer: (containerid: string) => void
AddContainer: (index: number, type: string, parent: string) => void

View file

@ -0,0 +1,48 @@
import * as React from 'react';
interface IInputGroupProps {
labelKey?: string
labelText: string
inputKey: string
labelClassName: string
inputClassName: string
type: string
value?: string
checked?: boolean
defaultValue?: string
defaultChecked?: boolean
isDisabled?: boolean
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
}
const className = `
w-full
text-xs font-medium transition-all text-gray-800 mt-1 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`;
export const InputGroup: React.FunctionComponent<IInputGroupProps> = (props) => {
return <>
<label
key={props.labelKey}
className={`mt-4 text-xs font-medium text-gray-800 ${props.labelClassName}`}
htmlFor={props.inputKey}
>
{ props.labelText }
</label>
<input
key={props.inputKey}
id={props.inputKey}
className={`${className} ${props.inputClassName}`}
type={props.type}
value={props.value}
defaultValue={props.defaultValue}
checked={props.checked}
defaultChecked={props.defaultChecked}
onChange={props.onChange}
disabled={props.isDisabled}
/>
</>;
};

View file

@ -0,0 +1,119 @@
import * as React from 'react';
import IProperties from '../../Interfaces/IProperties';
import { InputGroup } from '../InputGroup/InputGroup';
interface IDynamicFormProps {
properties: IProperties
onChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
}
const getCSSInputs = (
properties: IProperties,
onChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
): JSX.Element[] => {
const groupInput: JSX.Element[] = [];
for (const key in properties.style) {
groupInput.push(<InputGroup
key={key}
labelText={key}
inputKey={key}
labelClassName=''
inputClassName=''
type='string'
value={(properties.style as any)[key]}
onChange={(event) => onChange(key, event.target.value, true)}
/>);
}
return groupInput;
};
const DynamicForm: React.FunctionComponent<IDynamicFormProps> = (props) => {
return (
<div className='grid grid-cols-2 gap-y-4'>
<InputGroup
labelText='Name'
inputKey='id'
labelClassName=''
inputClassName=''
type='string'
value={props.properties.id.toString()}
isDisabled={true}
/>
<InputGroup
labelText='Parent name'
inputKey='id'
labelClassName=''
inputClassName=''
type='string'
value={props.properties.parentId?.toString()}
isDisabled={true}
/>
<InputGroup
labelText='x'
inputKey='x'
labelClassName=''
inputClassName=''
type='number'
value={props.properties.x.toString()}
onChange={(event) => props.onChange('x', event.target.value)}
/>
<InputGroup
labelText='y'
inputKey='y'
labelClassName=''
inputClassName=''
type='number'
value={props.properties.y.toString()}
onChange={(event) => props.onChange('y', event.target.value)}
/>
<InputGroup
labelText='Width'
inputKey='width'
labelClassName=''
inputClassName=''
type='number'
value={props.properties.width.toString()}
onChange={(event) => props.onChange('width', event.target.value)}
/>
<InputGroup
labelText='Height'
inputKey='height'
labelClassName=''
inputClassName=''
type='number'
value={props.properties.height.toString()}
onChange={(event) => props.onChange('height', event.target.value)}
/>
<InputGroup
labelText='Rigid'
inputKey='isRigidBody'
labelClassName=''
inputClassName=''
type='checkbox'
checked={props.properties.isRigidBody}
onChange={(event) => props.onChange('isRigidBody', event.target.checked)}
/>
<InputGroup
labelText='Anchor'
inputKey='isAnchor'
labelClassName=''
inputClassName=''
type='checkbox'
checked={props.properties.isAnchor}
onChange={(event) => props.onChange('isAnchor', event.target.checked)}
/>
<InputGroup
labelText='Horizontal alignment'
inputKey='xPositionReference'
labelClassName=''
inputClassName=''
type='number'
checked={props.properties.isRigidBody}
onChange={(event) => props.onChange('xPositionReference', event.target.value)}
/>
{ getCSSInputs(props.properties, props.onChange) }
</div>
);
};
export default DynamicForm;

View file

@ -0,0 +1,24 @@
import * as React from 'react';
import IProperties from '../../Interfaces/IProperties';
import DynamicForm from './DynamicForm';
import StaticForm from './StaticForm';
interface IFormProps {
properties: IProperties
isDynamicInput: boolean
onChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void
}
export const Form: React.FunctionComponent<IFormProps> = (props) => {
if (props.isDynamicInput) {
return <DynamicForm
properties={props.properties}
onChange={props.onChange}
/>;
}
return <StaticForm
properties={props.properties}
onSubmit={props.onSubmit}
/>;
};

View file

@ -1,12 +1,12 @@
import React, { useState } from 'react';
import ContainerProperties from '../../Interfaces/IProperties';
import IProperties from '../../Interfaces/IProperties';
import { ToggleButton } from '../ToggleButton/ToggleButton';
import { INPUT_TYPES } from './PropertiesInputTypes';
import { Form } from './Form';
interface IPropertiesProps {
properties?: ContainerProperties
onChange: (key: string, value: string | number | boolean) => void
onSubmit: (event: React.FormEvent<HTMLFormElement>, properties: ContainerProperties) => void
properties?: IProperties
onChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void
}
export const Properties: React.FC<IPropertiesProps> = (props: IPropertiesProps) => {
@ -16,26 +16,6 @@ export const Properties: React.FC<IPropertiesProps> = (props: IPropertiesProps)
return <div></div>;
}
const groupInput: React.ReactNode[] = [];
Object
.entries(props.properties)
.forEach((pair) => handleProperties(pair, groupInput, isDynamicInput, props.onChange));
const form = isDynamicInput
? <div className='grid grid-cols-2 gap-4'>
{ groupInput }
</div>
: <form
key={props.properties.id}
onSubmit={(event) => props.onSubmit(event, props.properties as ContainerProperties)}
>
<input type='submit' className='normal-btn block mx-auto mb-4 border-2 border-blue-400 cursor-pointer' value='Submit'/>
<div className='grid grid-cols-2 gap-y-4'>
{ groupInput }
</div>
</form>
;
return (
<div className='h-3/5 p-3 bg-slate-200 overflow-y-auto'>
<ToggleButton
@ -45,72 +25,12 @@ export const Properties: React.FC<IPropertiesProps> = (props: IPropertiesProps)
checked={isDynamicInput}
onChange={() => setIsDynamicInput(!isDynamicInput)}
/>
{ form }
<Form
properties={props.properties}
isDynamicInput={isDynamicInput}
onChange={props.onChange}
onSubmit={props.onSubmit}
/>
</div>
);
};
const handleProperties = (
[key, value]: [string, string | number],
groupInput: React.ReactNode[],
isDynamicInput: boolean,
onChange: (key: string, value: string | number | boolean) => void
): void => {
const id = `property-${key}`;
let type = 'text';
let checked;
/// hardcoded stuff for ergonomy ///
if (typeof value === 'boolean') {
checked = value;
}
if (key in INPUT_TYPES) {
type = INPUT_TYPES[key];
}
const className = `
w-full
text-xs font-medium transition-all text-gray-800 mt-1 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`;
const isDisabled = ['id', 'parentId'].includes(key);
const input = isDynamicInput
? <input
key={key}
id={key}
className={className}
type={type}
value={value}
checked={checked}
onChange={(event) => {
if (type === 'checkbox') {
onChange(key, event.target.checked);
return;
}
onChange(key, event.target.value);
}}
disabled={isDisabled}
/>
: <input
key={key}
id={key}
className={className}
type={type}
defaultValue={value}
defaultChecked={checked}
disabled={isDisabled}
/>;
groupInput.push(
<label
key={id}
className='mt-4 text-xs font-medium text-gray-800'
htmlFor={key}
>
{key}
</label>
);
groupInput.push(input);
};
};

View file

@ -0,0 +1,112 @@
import * as React from 'react';
import IProperties from '../../Interfaces/IProperties';
import { InputGroup } from '../InputGroup/InputGroup';
interface IStaticFormProps {
properties: IProperties
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void
}
const getCSSInputs = (properties: IProperties): JSX.Element[] => {
const groupInput: JSX.Element[] = [];
for (const key in properties.style) {
groupInput.push(<InputGroup
key={key}
labelText={key}
inputKey={key}
labelClassName=''
inputClassName=''
type='string'
defaultValue={(properties.style as any)[key]}
/>);
}
return groupInput;
};
const StaticForm: React.FunctionComponent<IStaticFormProps> = (props) => {
return (<form
key={props.properties.id}
onSubmit={(event) => props.onSubmit(event)}
>
<input type='submit' className='normal-btn block mx-auto mb-4 border-2 border-blue-400 cursor-pointer' value='Submit'/>
<div className='grid grid-cols-2 gap-y-4'>
<InputGroup
labelText='Name'
inputKey='id'
labelClassName=''
inputClassName=''
type='string'
defaultValue={props.properties.id.toString()}
isDisabled={true}
/>
<InputGroup
labelText='Parent name'
inputKey='id'
labelClassName=''
inputClassName=''
type='string'
defaultValue={props.properties.parentId?.toString()}
isDisabled={true}
/>
<InputGroup
labelText='x'
inputKey='x'
labelClassName=''
inputClassName=''
type='number'
defaultValue={props.properties.x.toString()}
/>
<InputGroup
labelText='y'
inputKey='y'
labelClassName=''
inputClassName=''
type='number'
defaultValue={props.properties.y.toString()}
/>
<InputGroup
labelText='Width'
inputKey='width'
labelClassName=''
inputClassName=''
type='number'
defaultValue={props.properties.width.toString()}
/>
<InputGroup
labelText='Height'
inputKey='height'
labelClassName=''
inputClassName=''
type='number'
defaultValue={props.properties.height.toString()}
/>
<InputGroup
labelText='Rigid'
inputKey='isRigidBody'
labelClassName=''
inputClassName=''
type='checkbox'
defaultChecked={props.properties.isRigidBody}
/>
<InputGroup
labelText='Anchor'
inputKey='isAnchor'
labelClassName=''
inputClassName=''
type='checkbox'
defaultChecked={props.properties.isAnchor}
/>
<InputGroup
labelText='Horizontal alignment'
inputKey='xPositionReference'
labelClassName=''
inputClassName=''
type='number'
defaultChecked={props.properties.isRigidBody}
/>
{ getCSSInputs(props.properties) }
</div>
</form>);
};
export default StaticForm;

View file

@ -8,7 +8,6 @@ import { IHistoryState } from '../../Interfaces/IHistoryState';
import { PhotographIcon, UploadIcon } from '@heroicons/react/outline';
import { FloatingButton } from '../FloatingButton/FloatingButton';
import { Bar } from '../Bar/Bar';
import IProperties from '../../Interfaces/IProperties';
interface IUIProps {
current: IHistoryState
@ -17,8 +16,8 @@ interface IUIProps {
AvailableContainers: IAvailableContainer[]
SelectContainer: (container: ContainerModel) => void
DeleteContainer: (containerId: string) => void
OnPropertyChange: (key: string, value: string | number | boolean) => void
OnPropertiesSubmit: (event: React.FormEvent<HTMLFormElement>, properties: IProperties) => void
OnPropertyChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
OnPropertiesSubmit: (event: React.FormEvent<HTMLFormElement>) => void
AddContainerToSelectedContainer: (type: string) => void
AddContainer: (index: number, type: string, parentId: string) => void
SaveEditorAsJSON: () => void