From 706f9624cc2564bd660e3d5a461a5c0dd91f5686 Mon Sep 17 00:00:00 2001 From: Eric NGUYEN Date: Tue, 16 Aug 2022 10:11:16 +0200 Subject: [PATCH 01/10] Reset Class Properties to separate css style into a property --- src/Components/App/MenuActions.ts | 6 ++++-- src/Components/Editor/ContainerOperations.ts | 6 +++--- src/Components/SVG/Elements/Container.tsx | 8 ++------ src/Components/SVG/Elements/Selector.tsx | 2 +- src/Interfaces/IProperties.ts | 5 +++-- src/utils/default.ts | 6 ++++-- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/Components/App/MenuActions.ts b/src/Components/App/MenuActions.ts index 8dec2a1..b9a455e 100644 --- a/src/Components/App/MenuActions.ts +++ b/src/Components/App/MenuActions.ts @@ -24,8 +24,10 @@ export function NewEditor( height: Number(configuration.MainContainer.Height), isRigidBody: false, isAnchor: false, - fillOpacity: 0, - stroke: 'black' + style: { + fillOpacity: 0, + stroke: 'black' + } } ); diff --git a/src/Components/Editor/ContainerOperations.ts b/src/Components/Editor/ContainerOperations.ts index 0ab670d..d778d82 100644 --- a/src/Components/Editor/ContainerOperations.ts +++ b/src/Components/Editor/ContainerOperations.ts @@ -209,7 +209,6 @@ export function AddContainer( x = ApplyAddMethod(index, containerConfig, parentClone, x); const defaultProperties: IProperties = { - ...containerConfig.Style, id: `${type}-${count}`, parentId: parentClone.properties.id, x, @@ -218,7 +217,8 @@ export function AddContainer( height, isRigidBody: false, isAnchor: false, - XPositionReference: containerConfig.XPositionReference + xPositionReference: containerConfig.XPositionReference, + style: containerConfig.Style }; // Create the container @@ -270,7 +270,7 @@ function ApplyAddMethod(index: number, containerConfig: IAvailableContainer, par lastChild.properties.x, lastChild.properties.y, lastChild.properties.width, - lastChild.properties.XPositionReference + lastChild.properties.xPositionReference ); x += transformedX + lastChild.properties.width; diff --git a/src/Components/SVG/Elements/Container.tsx b/src/Components/SVG/Elements/Container.tsx index 56d4797..11ec38d 100644 --- a/src/Components/SVG/Elements/Container.tsx +++ b/src/Components/SVG/Elements/Container.tsx @@ -22,7 +22,7 @@ export const Container: React.FC = (props: IContainerProps) => props.model.properties.x, props.model.properties.y, props.model.properties.width, - props.model.properties.XPositionReference + props.model.properties.xPositionReference ); const transform = `translate(${transformedX}, ${transformedY})`; @@ -36,12 +36,8 @@ export const Container: React.FC = (props: IContainerProps) => // Rect style const style = Object.assign( JSON.parse(JSON.stringify(defaultStyle)), - props.model.properties + props.model.properties.style ); - style.x = 0; - style.y = 0; - delete style.height; - delete style.width; // Dimension props const depth = getDepth(props.model); diff --git a/src/Components/SVG/Elements/Selector.tsx b/src/Components/SVG/Elements/Selector.tsx index 101f19a..5a0bb96 100644 --- a/src/Components/SVG/Elements/Selector.tsx +++ b/src/Components/SVG/Elements/Selector.tsx @@ -20,7 +20,7 @@ export const Selector: React.FC = (props) => { x, y, props.selected.properties.width, - props.selected.properties.XPositionReference + props.selected.properties.xPositionReference ); const [width, height] = [ props.selected.properties.width, diff --git a/src/Interfaces/IProperties.ts b/src/Interfaces/IProperties.ts index ad23442..c04584f 100644 --- a/src/Interfaces/IProperties.ts +++ b/src/Interfaces/IProperties.ts @@ -10,7 +10,7 @@ import { XPositionReference } from '../Enums/XPositionReference'; * @property isRigidBody if true apply rigid body behaviors * @property isAnchor if true apply anchor behaviors */ -export default interface IProperties extends Omit { +export default interface IProperties { id: string parentId: string | null x: number @@ -19,5 +19,6 @@ export default interface IProperties extends Omit Date: Tue, 16 Aug 2022 11:38:43 +0200 Subject: [PATCH 02/10] Unrefactor Properties form to allow more freedom on the input types and form --- src/Components/Editor/Editor.tsx | 7 +- src/Components/Editor/PropertiesOperations.ts | 37 ++++-- .../ElementsSidebar/ElementsSidebar.tsx | 4 +- src/Components/InputGroup/InputGroup.tsx | 48 +++++++ src/Components/Properties/DynamicForm.tsx | 119 ++++++++++++++++++ src/Components/Properties/Form.tsx | 24 ++++ src/Components/Properties/Properties.tsx | 104 ++------------- src/Components/Properties/StaticForm.tsx | 112 +++++++++++++++++ src/Components/UI/UI.tsx | 5 +- 9 files changed, 351 insertions(+), 109 deletions(-) create mode 100644 src/Components/InputGroup/InputGroup.tsx create mode 100644 src/Components/Properties/DynamicForm.tsx create mode 100644 src/Components/Properties/Form.tsx create mode 100644 src/Components/Properties/StaticForm.tsx diff --git a/src/Components/Editor/Editor.tsx b/src/Components/Editor/Editor.tsx index fcd62f9..75bf90c 100644 --- a/src/Components/Editor/Editor.tsx +++ b/src/Components/Editor/Editor.tsx @@ -91,16 +91,15 @@ const Editor: React.FunctionComponent = (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, diff --git a/src/Components/Editor/PropertiesOperations.ts b/src/Components/Editor/PropertiesOperations.ts index 4bd4c2d..cccc363 100644 --- a/src/Components/Editor/PropertiesOperations.ts +++ b/src/Components/Editor/PropertiesOperations.ts @@ -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>, @@ -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, - properties: IProperties, fullHistory: IHistoryState[], historyCurrentStep: number, setHistory: Dispatch>, @@ -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; } } diff --git a/src/Components/ElementsSidebar/ElementsSidebar.tsx b/src/Components/ElementsSidebar/ElementsSidebar.tsx index ab3efdd..21eb6b2 100644 --- a/src/Components/ElementsSidebar/ElementsSidebar.tsx +++ b/src/Components/ElementsSidebar/ElementsSidebar.tsx @@ -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, properties: ContainerProperties) => void + OnPropertyChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void + OnPropertiesSubmit: (event: React.FormEvent) => void SelectContainer: (container: IContainerModel) => void DeleteContainer: (containerid: string) => void AddContainer: (index: number, type: string, parent: string) => void diff --git a/src/Components/InputGroup/InputGroup.tsx b/src/Components/InputGroup/InputGroup.tsx new file mode 100644 index 0000000..bc794d3 --- /dev/null +++ b/src/Components/InputGroup/InputGroup.tsx @@ -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) => 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 = (props) => { + return <> + + + ; +}; diff --git a/src/Components/Properties/DynamicForm.tsx b/src/Components/Properties/DynamicForm.tsx new file mode 100644 index 0000000..7ef1031 --- /dev/null +++ b/src/Components/Properties/DynamicForm.tsx @@ -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( onChange(key, event.target.value, true)} + />); + } + return groupInput; +}; + +const DynamicForm: React.FunctionComponent = (props) => { + return ( +
+ + + props.onChange('x', event.target.value)} + /> + props.onChange('y', event.target.value)} + /> + props.onChange('width', event.target.value)} + /> + props.onChange('height', event.target.value)} + /> + props.onChange('isRigidBody', event.target.checked)} + /> + props.onChange('isAnchor', event.target.checked)} + /> + props.onChange('xPositionReference', event.target.value)} + /> + { getCSSInputs(props.properties, props.onChange) } +
+ ); +}; + +export default DynamicForm; diff --git a/src/Components/Properties/Form.tsx b/src/Components/Properties/Form.tsx new file mode 100644 index 0000000..bc0f508 --- /dev/null +++ b/src/Components/Properties/Form.tsx @@ -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) => void +} + +export const Form: React.FunctionComponent = (props) => { + if (props.isDynamicInput) { + return ; + } + return ; +}; diff --git a/src/Components/Properties/Properties.tsx b/src/Components/Properties/Properties.tsx index 516f23d..b72eead 100644 --- a/src/Components/Properties/Properties.tsx +++ b/src/Components/Properties/Properties.tsx @@ -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, properties: ContainerProperties) => void + properties?: IProperties + onChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void + onSubmit: (event: React.FormEvent) => void } export const Properties: React.FC = (props: IPropertiesProps) => { @@ -16,26 +16,6 @@ export const Properties: React.FC = (props: IPropertiesProps) return
; } - const groupInput: React.ReactNode[] = []; - Object - .entries(props.properties) - .forEach((pair) => handleProperties(pair, groupInput, isDynamicInput, props.onChange)); - - const form = isDynamicInput - ?
- { groupInput } -
- :
props.onSubmit(event, props.properties as ContainerProperties)} - > - -
- { groupInput } -
-
- ; - return (
= (props: IPropertiesProps) checked={isDynamicInput} onChange={() => setIsDynamicInput(!isDynamicInput)} /> - { form } +
); -}; - -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 - ? { - if (type === 'checkbox') { - onChange(key, event.target.checked); - return; - } - onChange(key, event.target.value); - }} - disabled={isDisabled} - /> - : ; - - groupInput.push( - - ); - groupInput.push(input); -}; +}; \ No newline at end of file diff --git a/src/Components/Properties/StaticForm.tsx b/src/Components/Properties/StaticForm.tsx new file mode 100644 index 0000000..06a186e --- /dev/null +++ b/src/Components/Properties/StaticForm.tsx @@ -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) => void +} + +const getCSSInputs = (properties: IProperties): JSX.Element[] => { + const groupInput: JSX.Element[] = []; + for (const key in properties.style) { + groupInput.push(); + } + return groupInput; +}; + +const StaticForm: React.FunctionComponent = (props) => { + return ( props.onSubmit(event)} + > + +
+ + + + + + + + + + { getCSSInputs(props.properties) } +
+ ); +}; + +export default StaticForm; diff --git a/src/Components/UI/UI.tsx b/src/Components/UI/UI.tsx index 4fa36e6..c57daee 100644 --- a/src/Components/UI/UI.tsx +++ b/src/Components/UI/UI.tsx @@ -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, properties: IProperties) => void + OnPropertyChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void + OnPropertiesSubmit: (event: React.FormEvent) => void AddContainerToSelectedContainer: (type: string) => void AddContainer: (index: number, type: string, parentId: string) => void SaveEditorAsJSON: () => void -- 2.47.2 From 8759c7704233a57a1d1f858bc5a6448c9751feed Mon Sep 17 00:00:00 2001 From: Eric NGUYEN Date: Tue, 16 Aug 2022 11:43:31 +0200 Subject: [PATCH 03/10] Refactor PropertiesOperations duplicate --- src/Components/Editor/PropertiesOperations.ts | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/Components/Editor/PropertiesOperations.ts b/src/Components/Editor/PropertiesOperations.ts index cccc363..92e707d 100644 --- a/src/Components/Editor/PropertiesOperations.ts +++ b/src/Components/Editor/PropertiesOperations.ts @@ -97,32 +97,25 @@ export function OnPropertiesSubmit( // 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; + const input: HTMLInputElement | null = (event.target as HTMLFormElement).querySelector(`#${property}`); - if (INPUT_TYPES[property] === 'number') { - (container.properties as any)[property] = Number(input.value); - continue; - } + if (input === null) { + continue; + } - (container.properties as any)[property] = input.value; + (container.properties as any)[property] = input.value; + if (INPUT_TYPES[property] === 'number') { + (container.properties as any)[property] = Number(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; + const input: HTMLInputElement | null = (event.target as HTMLFormElement).querySelector(`#${styleProperty}`); + if (input === null) { + continue; } + (container.properties.style as any)[styleProperty] = input.value; } if (container.properties.isRigidBody) { -- 2.47.2 From 9ce184df2606537ef0a5f99f756d355878e65e97 Mon Sep 17 00:00:00 2001 From: Eric NGUYEN Date: Tue, 16 Aug 2022 13:12:53 +0200 Subject: [PATCH 04/10] Fix tests + implement radio buttons for XPositionReference --- src/Components/App/MenuActions.ts | 2 + src/Components/Editor/ContainerOperations.ts | 7 +-- src/Components/Editor/PropertiesOperations.ts | 21 +++++++-- .../ElementsSidebar/ElementsSidebar.test.tsx | 12 ++++- src/Components/Properties/DynamicForm.tsx | 45 +++++++++++++++---- src/Components/Properties/Properties.test.tsx | 2 + src/Components/Properties/Properties.tsx | 2 +- src/Components/Properties/StaticForm.tsx | 42 +++++++++++++---- src/Components/SVG/Elements/Container.tsx | 2 +- src/Components/SVG/Elements/Selector.tsx | 2 +- src/Interfaces/IProperties.ts | 2 +- src/utils/default.ts | 2 + 12 files changed, 111 insertions(+), 30 deletions(-) diff --git a/src/Components/App/MenuActions.ts b/src/Components/App/MenuActions.ts index b9a455e..1e37328 100644 --- a/src/Components/App/MenuActions.ts +++ b/src/Components/App/MenuActions.ts @@ -4,6 +4,7 @@ import { ContainerModel } from '../../Interfaces/IContainerModel'; import { fetchConfiguration } from '../API/api'; import { IEditorState } from '../../Interfaces/IEditorState'; import { LoadState } from './Load'; +import { XPositionReference } from '../../Enums/XPositionReference'; export function NewEditor( setEditorState: Dispatch>, @@ -24,6 +25,7 @@ export function NewEditor( height: Number(configuration.MainContainer.Height), isRigidBody: false, isAnchor: false, + XPositionReference: XPositionReference.Left, style: { fillOpacity: 0, stroke: 'black' diff --git a/src/Components/Editor/ContainerOperations.ts b/src/Components/Editor/ContainerOperations.ts index d778d82..95a0889 100644 --- a/src/Components/Editor/ContainerOperations.ts +++ b/src/Components/Editor/ContainerOperations.ts @@ -1,4 +1,4 @@ -import React, { Dispatch, SetStateAction } from 'react'; +import { Dispatch, SetStateAction } from 'react'; import { IHistoryState } from '../../Interfaces/IHistoryState'; import { IConfiguration } from '../../Interfaces/IConfiguration'; import { ContainerModel, IContainerModel } from '../../Interfaces/IContainerModel'; @@ -8,6 +8,7 @@ import IProperties from '../../Interfaces/IProperties'; import { AddMethod } from '../../Enums/AddMethod'; import { IAvailableContainer } from '../../Interfaces/IAvailableContainer'; import { transformPosition } from '../SVG/Elements/Container'; +import { XPositionReference } from '../../Enums/XPositionReference'; /** * Select a container @@ -217,7 +218,7 @@ export function AddContainer( height, isRigidBody: false, isAnchor: false, - xPositionReference: containerConfig.XPositionReference, + XPositionReference: containerConfig.XPositionReference ?? XPositionReference.Left, style: containerConfig.Style }; @@ -270,7 +271,7 @@ function ApplyAddMethod(index: number, containerConfig: IAvailableContainer, par lastChild.properties.x, lastChild.properties.y, lastChild.properties.width, - lastChild.properties.xPositionReference + lastChild.properties.XPositionReference ); x += transformedX + lastChild.properties.width; diff --git a/src/Components/Editor/PropertiesOperations.ts b/src/Components/Editor/PropertiesOperations.ts index 92e707d..9f35e63 100644 --- a/src/Components/Editor/PropertiesOperations.ts +++ b/src/Components/Editor/PropertiesOperations.ts @@ -97,15 +97,28 @@ export function OnPropertiesSubmit( // Assign container properties for (const property in container.properties) { - const input: HTMLInputElement | null = (event.target as HTMLFormElement).querySelector(`#${property}`); + const input: HTMLInputElement | HTMLDivElement | null = (event.target as HTMLFormElement).querySelector(`#${property}`); if (input === null) { continue; } - (container.properties as any)[property] = input.value; - if (INPUT_TYPES[property] === 'number') { - (container.properties as any)[property] = Number(input.value); + 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 if (input instanceof HTMLDivElement) { + const radiobutton: HTMLInputElement | null = input.querySelector(`input[name="${property}"]:checked`); + + if (radiobutton === null) { + continue; + } + + (container.properties as any)[property] = radiobutton.value; + if (INPUT_TYPES[property] === 'number') { + (container.properties as any)[property] = Number(radiobutton.value); + } } } diff --git a/src/Components/ElementsSidebar/ElementsSidebar.test.tsx b/src/Components/ElementsSidebar/ElementsSidebar.test.tsx index 8d34480..f207a46 100644 --- a/src/Components/ElementsSidebar/ElementsSidebar.test.tsx +++ b/src/Components/ElementsSidebar/ElementsSidebar.test.tsx @@ -3,6 +3,7 @@ import * as React from 'react'; import { fireEvent, render, screen } from '../../utils/test-utils'; import { ElementsSidebar } from './ElementsSidebar'; import { IContainerModel } from '../../Interfaces/IContainerModel'; +import { XPositionReference } from '../../Enums/XPositionReference'; describe.concurrent('Elements sidebar', () => { it('With a MainContainer', () => { @@ -17,6 +18,7 @@ describe.concurrent('Elements sidebar', () => { y: 0, width: 2000, height: 100, + XPositionReference: XPositionReference.Left, isRigidBody: false, isAnchor: false }, @@ -49,7 +51,8 @@ describe.concurrent('Elements sidebar', () => { width: 2000, height: 100, isRigidBody: false, - isAnchor: false + isAnchor: false, + XPositionReference: XPositionReference.Left }, userData: {} }; @@ -105,6 +108,7 @@ describe.concurrent('Elements sidebar', () => { y: 0, width: 2000, height: 100, + XPositionReference: XPositionReference.Left, isRigidBody: false, isAnchor: false }, @@ -123,7 +127,8 @@ describe.concurrent('Elements sidebar', () => { width: 0, height: 0, isRigidBody: false, - isAnchor: false + isAnchor: false, + XPositionReference: XPositionReference.Left }, userData: {} } @@ -140,6 +145,7 @@ describe.concurrent('Elements sidebar', () => { y: 0, width: 0, height: 0, + XPositionReference: XPositionReference.Left, isRigidBody: false, isAnchor: false }, @@ -178,6 +184,7 @@ describe.concurrent('Elements sidebar', () => { y: 0, width: 2000, height: 100, + XPositionReference: XPositionReference.Left, isRigidBody: false, isAnchor: false }, @@ -194,6 +201,7 @@ describe.concurrent('Elements sidebar', () => { y: 0, width: 0, height: 0, + XPositionReference: XPositionReference.Left, isRigidBody: false, isAnchor: false }, diff --git a/src/Components/Properties/DynamicForm.tsx b/src/Components/Properties/DynamicForm.tsx index 7ef1031..008c6c2 100644 --- a/src/Components/Properties/DynamicForm.tsx +++ b/src/Components/Properties/DynamicForm.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { XPositionReference } from '../../Enums/XPositionReference'; import IProperties from '../../Interfaces/IProperties'; import { InputGroup } from '../InputGroup/InputGroup'; @@ -102,15 +103,41 @@ const DynamicForm: React.FunctionComponent = (props) => { checked={props.properties.isAnchor} onChange={(event) => props.onChange('isAnchor', event.target.checked)} /> - props.onChange('xPositionReference', event.target.value)} - /> + +
+ + + +
{ getCSSInputs(props.properties, props.onChange) } ); diff --git a/src/Components/Properties/Properties.test.tsx b/src/Components/Properties/Properties.test.tsx index 8b8db31..e88bb68 100644 --- a/src/Components/Properties/Properties.test.tsx +++ b/src/Components/Properties/Properties.test.tsx @@ -1,6 +1,7 @@ import { fireEvent, render, screen } from '@testing-library/react'; import * as React from 'react'; import { expect, describe, it, vi } from 'vitest'; +import { XPositionReference } from '../../Enums/XPositionReference'; import IProperties from '../../Interfaces/IProperties'; import { Properties } from './Properties'; @@ -26,6 +27,7 @@ describe.concurrent('Properties', () => { y: 1, width: 1, height: 1, + XPositionReference: XPositionReference.Left, isRigidBody: false, isAnchor: false }; diff --git a/src/Components/Properties/Properties.tsx b/src/Components/Properties/Properties.tsx index b72eead..cb38a60 100644 --- a/src/Components/Properties/Properties.tsx +++ b/src/Components/Properties/Properties.tsx @@ -33,4 +33,4 @@ export const Properties: React.FC = (props: IPropertiesProps) /> ); -}; \ No newline at end of file +}; diff --git a/src/Components/Properties/StaticForm.tsx b/src/Components/Properties/StaticForm.tsx index 06a186e..bd3bcec 100644 --- a/src/Components/Properties/StaticForm.tsx +++ b/src/Components/Properties/StaticForm.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { XPositionReference } from '../../Enums/XPositionReference'; import IProperties from '../../Interfaces/IProperties'; import { InputGroup } from '../InputGroup/InputGroup'; @@ -96,14 +97,39 @@ const StaticForm: React.FunctionComponent = (props) => { type='checkbox' defaultChecked={props.properties.isAnchor} /> - + +
+ + + +
+ { getCSSInputs(props.properties) } ); diff --git a/src/Components/SVG/Elements/Container.tsx b/src/Components/SVG/Elements/Container.tsx index 11ec38d..3fea3a4 100644 --- a/src/Components/SVG/Elements/Container.tsx +++ b/src/Components/SVG/Elements/Container.tsx @@ -22,7 +22,7 @@ export const Container: React.FC = (props: IContainerProps) => props.model.properties.x, props.model.properties.y, props.model.properties.width, - props.model.properties.xPositionReference + props.model.properties.XPositionReference ); const transform = `translate(${transformedX}, ${transformedY})`; diff --git a/src/Components/SVG/Elements/Selector.tsx b/src/Components/SVG/Elements/Selector.tsx index 5a0bb96..101f19a 100644 --- a/src/Components/SVG/Elements/Selector.tsx +++ b/src/Components/SVG/Elements/Selector.tsx @@ -20,7 +20,7 @@ export const Selector: React.FC = (props) => { x, y, props.selected.properties.width, - props.selected.properties.xPositionReference + props.selected.properties.XPositionReference ); const [width, height] = [ props.selected.properties.width, diff --git a/src/Interfaces/IProperties.ts b/src/Interfaces/IProperties.ts index c04584f..2513d74 100644 --- a/src/Interfaces/IProperties.ts +++ b/src/Interfaces/IProperties.ts @@ -19,6 +19,6 @@ export default interface IProperties { height: number isRigidBody: boolean isAnchor: boolean - xPositionReference?: XPositionReference + XPositionReference: XPositionReference style?: React.CSSProperties } diff --git a/src/utils/default.ts b/src/utils/default.ts index ae952bd..97df4b0 100644 --- a/src/utils/default.ts +++ b/src/utils/default.ts @@ -1,3 +1,4 @@ +import { XPositionReference } from '../Enums/XPositionReference'; import { IConfiguration } from '../Interfaces/IConfiguration'; import IProperties from '../Interfaces/IProperties'; @@ -34,6 +35,7 @@ export const DEFAULT_MAINCONTAINER_PROPS: IProperties = { height: Number(DEFAULT_CONFIG.MainContainer.Height), isRigidBody: false, isAnchor: false, + XPositionReference: XPositionReference.Left, style: { stroke: 'black', fillOpacity: 0 -- 2.47.2 From 6d56ea49cf80d87887770199ac1aff75d1d76046 Mon Sep 17 00:00:00 2001 From: Eric NGUYEN Date: Tue, 16 Aug 2022 13:46:54 +0200 Subject: [PATCH 05/10] Refactor radio group buttons --- src/Components/Properties/DynamicForm.tsx | 47 +++++----------- src/Components/Properties/StaticForm.tsx | 44 ++++----------- .../RadioGroupButtons/RadioGroupButtons.tsx | 53 +++++++++++++++++++ src/Interfaces/IInputGroup.ts | 4 ++ 4 files changed, 80 insertions(+), 68 deletions(-) create mode 100644 src/Components/RadioGroupButtons/RadioGroupButtons.tsx create mode 100644 src/Interfaces/IInputGroup.ts diff --git a/src/Components/Properties/DynamicForm.tsx b/src/Components/Properties/DynamicForm.tsx index 008c6c2..142763f 100644 --- a/src/Components/Properties/DynamicForm.tsx +++ b/src/Components/Properties/DynamicForm.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { XPositionReference } from '../../Enums/XPositionReference'; import IProperties from '../../Interfaces/IProperties'; import { InputGroup } from '../InputGroup/InputGroup'; +import { RadioGroupButtons } from '../RadioGroupButtons/RadioGroupButtons'; interface IDynamicFormProps { properties: IProperties @@ -103,41 +104,17 @@ const DynamicForm: React.FunctionComponent = (props) => { checked={props.properties.isAnchor} onChange={(event) => props.onChange('isAnchor', event.target.checked)} /> - -
- - - -
+ props.onChange('XPositionReference', event.target.value)} + /> { getCSSInputs(props.properties, props.onChange) } ); diff --git a/src/Components/Properties/StaticForm.tsx b/src/Components/Properties/StaticForm.tsx index bd3bcec..e7cb6d8 100644 --- a/src/Components/Properties/StaticForm.tsx +++ b/src/Components/Properties/StaticForm.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { XPositionReference } from '../../Enums/XPositionReference'; import IProperties from '../../Interfaces/IProperties'; import { InputGroup } from '../InputGroup/InputGroup'; +import { RadioGroupButtons } from '../RadioGroupButtons/RadioGroupButtons'; interface IStaticFormProps { properties: IProperties @@ -97,39 +98,16 @@ const StaticForm: React.FunctionComponent = (props) => { type='checkbox' defaultChecked={props.properties.isAnchor} /> - -
- - - -
- + { getCSSInputs(props.properties) } ); diff --git a/src/Components/RadioGroupButtons/RadioGroupButtons.tsx b/src/Components/RadioGroupButtons/RadioGroupButtons.tsx new file mode 100644 index 0000000..76f1433 --- /dev/null +++ b/src/Components/RadioGroupButtons/RadioGroupButtons.tsx @@ -0,0 +1,53 @@ +import * as React from 'react'; +import { IInputGroup } from '../../Interfaces/IInputGroup'; + +interface IRadioGroupButtonsProps { + name: string + value?: string + defaultValue?: string + labelText: string + inputGroups: IInputGroup[] + onChange?: (event: React.ChangeEvent) => void +} + +export const RadioGroupButtons: React.FunctionComponent = (props) => { + let inputGroups; + if (props.value !== undefined) { + inputGroups = props.inputGroups.map((inputGroup) => ( + + )); + } else { + inputGroups = props.inputGroups.map((inputGroup) => ( + + )); + } + + return ( + <> + +
+ { inputGroups } +
+ + ); +}; diff --git a/src/Interfaces/IInputGroup.ts b/src/Interfaces/IInputGroup.ts new file mode 100644 index 0000000..eba23cd --- /dev/null +++ b/src/Interfaces/IInputGroup.ts @@ -0,0 +1,4 @@ +export interface IInputGroup { + text: string + value: string +} -- 2.47.2 From ed16cdd61d87cbdb13c52a7c2b9e4711b0a8b08c Mon Sep 17 00:00:00 2001 From: Eric NGUYEN Date: Tue, 16 Aug 2022 13:53:47 +0200 Subject: [PATCH 06/10] Fix regressions + fix tests because some inputs does not have onChange event anymore (id, parentId) --- src/Components/Properties/DynamicForm.tsx | 2 +- src/Components/Properties/Properties.test.tsx | 10 +++++----- src/Components/Properties/StaticForm.tsx | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Components/Properties/DynamicForm.tsx b/src/Components/Properties/DynamicForm.tsx index 142763f..c604178 100644 --- a/src/Components/Properties/DynamicForm.tsx +++ b/src/Components/Properties/DynamicForm.tsx @@ -43,7 +43,7 @@ const DynamicForm: React.FunctionComponent = (props) => { /> { fireEvent.change(propertyParentId as Element, { target: { value: 'parentedId' } }); fireEvent.change(propertyX as Element, { target: { value: '2' } }); fireEvent.change(propertyY as Element, { target: { value: '2' } }); - expect(handleChange).toBeCalledTimes(4); + expect(handleChange).toBeCalledTimes(2); - expect(prop.id).toBe('stuffed'); - expect(prop.parentId).toBe('parentedId'); + expect(prop.id).toBe('stuff'); + expect(prop.parentId).toBe('parentId'); expect(prop.x).toBe('2'); expect(prop.y).toBe('2'); rerender( { propertyX = container.querySelector('#x'); propertyY = container.querySelector('#y'); expect(propertyId).toBeDefined(); - expect((propertyId as HTMLInputElement).value).toBe('stuffed'); + expect((propertyId as HTMLInputElement).value).toBe('stuff'); expect(propertyParentId).toBeDefined(); - expect((propertyParentId as HTMLInputElement).value).toBe('parentedId'); + expect((propertyParentId as HTMLInputElement).value).toBe('parentId'); expect(propertyX).toBeDefined(); expect((propertyX as HTMLInputElement).value).toBe('2'); expect(propertyY).toBeDefined(); diff --git a/src/Components/Properties/StaticForm.tsx b/src/Components/Properties/StaticForm.tsx index e7cb6d8..174fd46 100644 --- a/src/Components/Properties/StaticForm.tsx +++ b/src/Components/Properties/StaticForm.tsx @@ -43,7 +43,7 @@ const StaticForm: React.FunctionComponent = (props) => { /> Date: Tue, 16 Aug 2022 14:08:54 +0200 Subject: [PATCH 07/10] RadioGroupButtons: Improve CSS --- src/Components/RadioGroupButtons/RadioGroupButtons.tsx | 6 +++++- src/Interfaces/IInputGroup.ts | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Components/RadioGroupButtons/RadioGroupButtons.tsx b/src/Components/RadioGroupButtons/RadioGroupButtons.tsx index 76f1433..f007392 100644 --- a/src/Components/RadioGroupButtons/RadioGroupButtons.tsx +++ b/src/Components/RadioGroupButtons/RadioGroupButtons.tsx @@ -18,6 +18,7 @@ export const RadioGroupButtons: React.FunctionComponent -
+
{ inputGroups }
diff --git a/src/Interfaces/IInputGroup.ts b/src/Interfaces/IInputGroup.ts index eba23cd..dfc9942 100644 --- a/src/Interfaces/IInputGroup.ts +++ b/src/Interfaces/IInputGroup.ts @@ -1,4 +1,6 @@ +import React from 'react'; + export interface IInputGroup { - text: string + text: React.ReactNode value: string } -- 2.47.2 From 658b9d9cc74b1ae177687a73a83fb07fecb41ddf Mon Sep 17 00:00:00 2001 From: Eric NGUYEN Date: Tue, 16 Aug 2022 14:47:13 +0200 Subject: [PATCH 08/10] Improve XPositionReference radio input group --- .../ElementsSidebar/ElementsSidebar.tsx | 1 - src/Components/Properties/DynamicForm.tsx | 29 +++++++++++++++++-- src/Components/Properties/StaticForm.tsx | 29 +++++++++++++++++-- .../RadioGroupButtons/RadioGroupButtons.tsx | 27 +++++++++++------ src/index.scss | 4 +++ 5 files changed, 74 insertions(+), 16 deletions(-) diff --git a/src/Components/ElementsSidebar/ElementsSidebar.tsx b/src/Components/ElementsSidebar/ElementsSidebar.tsx index 21eb6b2..98eb071 100644 --- a/src/Components/ElementsSidebar/ElementsSidebar.tsx +++ b/src/Components/ElementsSidebar/ElementsSidebar.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { FixedSizeList as List } from 'react-window'; import { Properties } from '../Properties/Properties'; -import ContainerProperties from '../../Interfaces/IProperties'; import { IContainerModel } from '../../Interfaces/IContainerModel'; import { getDepth, MakeIterator } from '../../utils/itertools'; import { Menu } from '../Menu/Menu'; diff --git a/src/Components/Properties/DynamicForm.tsx b/src/Components/Properties/DynamicForm.tsx index c604178..0be155d 100644 --- a/src/Components/Properties/DynamicForm.tsx +++ b/src/Components/Properties/DynamicForm.tsx @@ -1,3 +1,4 @@ +import { MenuAlt2Icon, MenuAlt3Icon, MenuIcon } from '@heroicons/react/outline'; import * as React from 'react'; import { XPositionReference } from '../../Enums/XPositionReference'; import IProperties from '../../Interfaces/IProperties'; @@ -107,11 +108,33 @@ const DynamicForm: React.FunctionComponent = (props) => { + +
+ ), + value: XPositionReference.Left.toString() + }, + { + text: ( +
+ +
+ ), + value: XPositionReference.Center.toString() + }, + { + text: ( +
+ +
+ ), + value: XPositionReference.Right.toString() + } ]} onChange={(event) => props.onChange('XPositionReference', event.target.value)} /> diff --git a/src/Components/Properties/StaticForm.tsx b/src/Components/Properties/StaticForm.tsx index 174fd46..f16d196 100644 --- a/src/Components/Properties/StaticForm.tsx +++ b/src/Components/Properties/StaticForm.tsx @@ -1,3 +1,4 @@ +import { MenuAlt2Icon, MenuIcon, MenuAlt3Icon } from '@heroicons/react/outline'; import * as React from 'react'; import { XPositionReference } from '../../Enums/XPositionReference'; import IProperties from '../../Interfaces/IProperties'; @@ -101,11 +102,33 @@ const StaticForm: React.FunctionComponent = (props) => { + + + ), + value: XPositionReference.Left.toString() + }, + { + text: ( +
+ +
+ ), + value: XPositionReference.Center.toString() + }, + { + text: ( +
+ +
+ ), + value: XPositionReference.Right.toString() + } ]} /> { getCSSInputs(props.properties) } diff --git a/src/Components/RadioGroupButtons/RadioGroupButtons.tsx b/src/Components/RadioGroupButtons/RadioGroupButtons.tsx index f007392..db6c278 100644 --- a/src/Components/RadioGroupButtons/RadioGroupButtons.tsx +++ b/src/Components/RadioGroupButtons/RadioGroupButtons.tsx @@ -5,6 +5,7 @@ interface IRadioGroupButtonsProps { name: string value?: string defaultValue?: string + inputClassName: string labelText: string inputGroups: IInputGroup[] onChange?: (event: React.ChangeEvent) => void @@ -13,32 +14,40 @@ interface IRadioGroupButtonsProps { export const RadioGroupButtons: React.FunctionComponent = (props) => { let inputGroups; if (props.value !== undefined) { + // dynamic inputGroups = props.inputGroups.map((inputGroup) => ( -