From 3da8a0c55e2a0c006a1bce910cae1b05722c7056 Mon Sep 17 00:00:00 2001 From: Eric NGUYEN Date: Wed, 17 Aug 2022 16:34:38 +0200 Subject: [PATCH 1/5] Add MinWidth to properties + add docs to IProperties + refactor default container properties into a function in default.ts --- src/Components/Editor/ContainerOperations.ts | 22 +++----- src/Interfaces/IAvailableContainer.ts | 7 +-- src/Interfaces/IProperties.ts | 56 +++++++++++++++++--- src/utils/default.ts | 25 +++++++++ 4 files changed, 87 insertions(+), 23 deletions(-) diff --git a/src/Components/Editor/ContainerOperations.ts b/src/Components/Editor/ContainerOperations.ts index e4e7c0d..93d9634 100644 --- a/src/Components/Editor/ContainerOperations.ts +++ b/src/Components/Editor/ContainerOperations.ts @@ -8,6 +8,7 @@ import IProperties from '../../Interfaces/IProperties'; import { AddMethod } from '../../Enums/AddMethod'; import { IAvailableContainer } from '../../Interfaces/IAvailableContainer'; import { XPositionReference } from '../../Enums/XPositionReference'; +import { GetDefaultContainerProps } from '../../utils/default'; /** * Select a container @@ -203,25 +204,17 @@ export function AddContainer( let x = containerConfig.DefaultX ?? 0; const y = containerConfig.DefaultY ?? 0; - const width = containerConfig.Width ?? parentClone.properties.width; - const height = containerConfig.Height ?? parentClone.properties.height; x = ApplyAddMethod(index, containerConfig, parentClone, x); - const defaultProperties: IProperties = { - id: `${type}-${count}`, - parentId: parentClone.properties.id, + const defaultProperties = GetDefaultContainerProps( + type, + count, + parentClone, x, y, - width, - height, - isRigidBody: false, - isAnchor: false, - XPositionReference: containerConfig.XPositionReference ?? XPositionReference.Left, - customSVG: containerConfig.CustomSVG, - style: containerConfig.Style, - userData: containerConfig.UserData - }; + containerConfig + ); // Create the container const newContainer = new ContainerModel( @@ -265,6 +258,7 @@ function ApplyAddMethod(index: number, containerConfig: IAvailableContainer, par if (index > 0 && ( containerConfig.AddMethod === undefined || containerConfig.AddMethod === AddMethod.Append)) { + // Append method (default) const lastChild: IContainerModel | undefined = parent.children.at(index - 1); if (lastChild !== undefined) { diff --git a/src/Interfaces/IAvailableContainer.ts b/src/Interfaces/IAvailableContainer.ts index 800af5d..8dd0a83 100644 --- a/src/Interfaces/IAvailableContainer.ts +++ b/src/Interfaces/IAvailableContainer.ts @@ -5,13 +5,14 @@ import { XPositionReference } from '../Enums/XPositionReference'; /** Model of available container used in application configuration */ export interface IAvailableContainer { Type: string - Width?: number - Height?: number DefaultX?: number DefaultY?: number + Width?: number + Height?: number + MinWidth?: number AddMethod?: AddMethod XPositionReference?: XPositionReference CustomSVG?: string - Style: React.CSSProperties + Style?: React.CSSProperties UserData?: object } diff --git a/src/Interfaces/IProperties.ts b/src/Interfaces/IProperties.ts index 38153ce..c1a3b6a 100644 --- a/src/Interfaces/IProperties.ts +++ b/src/Interfaces/IProperties.ts @@ -3,24 +3,68 @@ import { XPositionReference } from '../Enums/XPositionReference'; /** * Properties of a container - * @property id id of the container - * @property parentId id of the parent container - * @property x horizontal offset of the container - * @property y vertical offset of the container - * @property isRigidBody if true apply rigid body behaviors - * @property isAnchor if true apply anchor behaviors */ export default interface IProperties { + /** id of the container */ id: string + + /** id of the parent container (null when there is no parent) */ parentId: string | null + + /** horizontal offset */ x: number + + /** vertical offset */ y: number + + /** width */ width: number + + /** height */ height: number + + /** true if rigid, false otherwise */ isRigidBody: boolean + + /** true if anchor, false otherwise */ isAnchor: boolean + + /** Horizontal alignment, also determines the visual location of x {Left = 0, Center, Right } */ XPositionReference: XPositionReference + + /** + * (optional) + * Minimum width used when isRigidBody = true. + * Allows the container to set isRigidBody to false when it gets squeezed + * by an anchor + */ + minWidth?: number + + /** + * (optional) + * Replace a by a customized "SVG". It is not really an svg but it at least allows + * to draw some patterns that can be bind to the properties of the container + * Use {prop} to bind a property. Use {{ styleProp }} to use an object. + * Example : + * ``` + * ` + * + * + * + * ` + * ``` + */ customSVG?: string + + /** + * (optional) + * Style of the + */ style?: React.CSSProperties + + /** + * (optional) + * User data that can be used for data storage or custom SVG + */ userData?: object } diff --git a/src/utils/default.ts b/src/utils/default.ts index 97df4b0..6eaf561 100644 --- a/src/utils/default.ts +++ b/src/utils/default.ts @@ -1,5 +1,7 @@ import { XPositionReference } from '../Enums/XPositionReference'; +import { IAvailableContainer } from '../Interfaces/IAvailableContainer'; import { IConfiguration } from '../Interfaces/IConfiguration'; +import { IContainerModel } from '../Interfaces/IContainerModel'; import IProperties from '../Interfaces/IProperties'; export const DEFAULT_CONFIG: IConfiguration = { @@ -42,6 +44,29 @@ export const DEFAULT_MAINCONTAINER_PROPS: IProperties = { } }; +export const GetDefaultContainerProps = ( + type: string, + typeCount: number, + parent: IContainerModel, + x: number, + y: number, + containerConfig: IAvailableContainer +): IProperties => ({ + id: `${type}-${typeCount}`, + parentId: parent.properties.id, + x, + y, + width: containerConfig.Width ?? containerConfig.MinWidth ?? parent.properties.width, + height: containerConfig.Height ?? parent.properties.height, + isRigidBody: false, + isAnchor: false, + XPositionReference: containerConfig.XPositionReference ?? XPositionReference.Left, + minWidth: containerConfig.MinWidth, + customSVG: containerConfig.CustomSVG, + style: containerConfig.Style, + userData: containerConfig.UserData +}); + export const DIMENSION_MARGIN = 50; export const NOTCHES_LENGTH = 4; -- 2.47.2 From 83d4990bba93a446277debd79d88029d76f927cb Mon Sep 17 00:00:00 2001 From: Eric NGUYEN Date: Wed, 17 Aug 2022 16:35:05 +0200 Subject: [PATCH 2/5] Remove useless import --- src/Components/Editor/ContainerOperations.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Components/Editor/ContainerOperations.ts b/src/Components/Editor/ContainerOperations.ts index 93d9634..a913d79 100644 --- a/src/Components/Editor/ContainerOperations.ts +++ b/src/Components/Editor/ContainerOperations.ts @@ -4,10 +4,8 @@ import { IConfiguration } from '../../Interfaces/IConfiguration'; import { ContainerModel, IContainerModel } from '../../Interfaces/IContainerModel'; import { findContainerById } from '../../utils/itertools'; import { getCurrentHistory } from './Editor'; -import IProperties from '../../Interfaces/IProperties'; import { AddMethod } from '../../Enums/AddMethod'; import { IAvailableContainer } from '../../Interfaces/IAvailableContainer'; -import { XPositionReference } from '../../Enums/XPositionReference'; import { GetDefaultContainerProps } from '../../utils/default'; /** -- 2.47.2 From 4bf4f01dc2615cd4509c82a40deddd9af49807c3 Mon Sep 17 00:00:00 2001 From: Eric NGUYEN Date: Wed, 17 Aug 2022 16:52:36 +0200 Subject: [PATCH 3/5] Implemented logic for minWidth and rigidBody --- .../Editor/Behaviors/RigidBodyBehaviors.ts | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts b/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts index e96da4a..eab0970 100644 --- a/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts +++ b/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts @@ -152,7 +152,7 @@ export function constraintBodyInsideUnallocatedWidth( // Check if the container actually fit inside // It will usually fit if it was alrady fitting const availableWidthFound = availableWidths.find((width) => - isFitting(container, width) + isFitting(container.properties.width, width) ); if (availableWidthFound === undefined) { @@ -163,12 +163,21 @@ export function constraintBodyInsideUnallocatedWidth( // We want the container to fit automatically inside the available space // even if it means to resize the container - // The end goal is that the code never show the error message no matter what action is done - // TODO: Actually give an option to not fit and show the error message shown below - const availableWidth = availableWidths[0]; + const availableWidth: ISizePointer | undefined = availableWidths.find((width) => { + if (container.properties.minWidth === undefined) { + return true; + } + return isFitting(container.properties.minWidth, width); + }); + + if (availableWidth === undefined) { + console.warn(`Container ${container.properties.id} cannot fit in any space due to its minimum width being to large. Consequently, its rigid body property is disabled.`); + container.properties.isRigidBody = false; + return container; + } + container.properties.x = availableWidth.x; container.properties.width = availableWidth.width; - // throw new Error('[constraintBodyInsideUnallocatedWidth] BIGERR: No available space found on the parent container, even though there is some.'); return container; } @@ -188,9 +197,9 @@ export function constraintBodyInsideUnallocatedWidth( * @returns */ const isFitting = ( - container: IContainerModel, + containerWidth: number, sizePointer: ISizePointer -): boolean => container.properties.width <= sizePointer.width; +): boolean => containerWidth <= sizePointer.width; /** * Get the unallocated widths inside a container -- 2.47.2 From f0c1803e101611d4076c7634363f7c139a8abad1 Mon Sep 17 00:00:00 2001 From: Eric NGUYEN Date: Wed, 17 Aug 2022 16:53:09 +0200 Subject: [PATCH 4/5] test-server: Set minWidth --- test-server/http.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test-server/http.js b/test-server/http.js index 75c8628..c50792d 100644 --- a/test-server/http.js +++ b/test-server/http.js @@ -54,6 +54,7 @@ const GetSVGLayoutConfiguration = () => { { Type: 'Chassis', Width: 500, + MinWidth: 200, Style: { fillOpacity: 1, strokeWidth: 2, -- 2.47.2 From 3dd376813706e76d6cd68a9ca58e00eccb16e269 Mon Sep 17 00:00:00 2001 From: Eric NGUYEN Date: Wed, 17 Aug 2022 17:13:12 +0200 Subject: [PATCH 5/5] Add minWidth to containers (default 1) and forms --- src/Components/App/MenuActions.ts | 15 +++------------ .../Editor/Behaviors/RigidBodyBehaviors.ts | 3 --- .../ElementsSidebar/ElementsSidebar.test.tsx | 7 +++++++ src/Components/InputGroup/InputGroup.tsx | 2 ++ src/Components/Properties/DynamicForm.tsx | 12 ++++++++++++ src/Components/Properties/Properties.test.tsx | 1 + src/Components/Properties/StaticForm.tsx | 11 +++++++++++ src/Interfaces/IProperties.ts | 15 +++++++-------- src/utils/default.ts | 3 ++- 9 files changed, 45 insertions(+), 24 deletions(-) diff --git a/src/Components/App/MenuActions.ts b/src/Components/App/MenuActions.ts index 1e37328..601e4c7 100644 --- a/src/Components/App/MenuActions.ts +++ b/src/Components/App/MenuActions.ts @@ -5,6 +5,7 @@ import { fetchConfiguration } from '../API/api'; import { IEditorState } from '../../Interfaces/IEditorState'; import { LoadState } from './Load'; import { XPositionReference } from '../../Enums/XPositionReference'; +import { DEFAULT_MAINCONTAINER_PROPS } from '../../utils/default'; export function NewEditor( setEditorState: Dispatch>, @@ -17,19 +18,9 @@ export function NewEditor( const MainContainer = new ContainerModel( null, { - id: 'main', - parentId: 'null', - x: 0, - y: 0, + ...DEFAULT_MAINCONTAINER_PROPS, width: Number(configuration.MainContainer.Width), - height: Number(configuration.MainContainer.Height), - isRigidBody: false, - isAnchor: false, - XPositionReference: XPositionReference.Left, - style: { - fillOpacity: 0, - stroke: 'black' - } + height: Number(configuration.MainContainer.Height) } ); diff --git a/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts b/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts index eab0970..5d7b729 100644 --- a/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts +++ b/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts @@ -164,9 +164,6 @@ export function constraintBodyInsideUnallocatedWidth( // We want the container to fit automatically inside the available space // even if it means to resize the container const availableWidth: ISizePointer | undefined = availableWidths.find((width) => { - if (container.properties.minWidth === undefined) { - return true; - } return isFitting(container.properties.minWidth, width); }); diff --git a/src/Components/ElementsSidebar/ElementsSidebar.test.tsx b/src/Components/ElementsSidebar/ElementsSidebar.test.tsx index f207a46..bf88f22 100644 --- a/src/Components/ElementsSidebar/ElementsSidebar.test.tsx +++ b/src/Components/ElementsSidebar/ElementsSidebar.test.tsx @@ -18,6 +18,7 @@ describe.concurrent('Elements sidebar', () => { y: 0, width: 2000, height: 100, + minWidth: 1, XPositionReference: XPositionReference.Left, isRigidBody: false, isAnchor: false @@ -50,6 +51,7 @@ describe.concurrent('Elements sidebar', () => { y: 0, width: 2000, height: 100, + minWidth: 1, isRigidBody: false, isAnchor: false, XPositionReference: XPositionReference.Left @@ -106,6 +108,7 @@ describe.concurrent('Elements sidebar', () => { parentId: '', x: 0, y: 0, + minWidth: 1, width: 2000, height: 100, XPositionReference: XPositionReference.Left, @@ -124,6 +127,7 @@ describe.concurrent('Elements sidebar', () => { parentId: 'main', x: 0, y: 0, + minWidth: 1, width: 0, height: 0, isRigidBody: false, @@ -143,6 +147,7 @@ describe.concurrent('Elements sidebar', () => { parentId: 'main', x: 0, y: 0, + minWidth: 1, width: 0, height: 0, XPositionReference: XPositionReference.Left, @@ -182,6 +187,7 @@ describe.concurrent('Elements sidebar', () => { parentId: '', x: 0, y: 0, + minWidth: 1, width: 2000, height: 100, XPositionReference: XPositionReference.Left, @@ -199,6 +205,7 @@ describe.concurrent('Elements sidebar', () => { parentId: 'main', x: 0, y: 0, + minWidth: 1, width: 0, height: 0, XPositionReference: XPositionReference.Left, diff --git a/src/Components/InputGroup/InputGroup.tsx b/src/Components/InputGroup/InputGroup.tsx index bc794d3..ff67a6f 100644 --- a/src/Components/InputGroup/InputGroup.tsx +++ b/src/Components/InputGroup/InputGroup.tsx @@ -11,6 +11,7 @@ interface IInputGroupProps { checked?: boolean defaultValue?: string defaultChecked?: boolean + min?: number isDisabled?: boolean onChange?: (event: React.ChangeEvent) => void } @@ -42,6 +43,7 @@ export const InputGroup: React.FunctionComponent = (props) => checked={props.checked} defaultChecked={props.defaultChecked} onChange={props.onChange} + min={props.min} disabled={props.isDisabled} /> ; diff --git a/src/Components/Properties/DynamicForm.tsx b/src/Components/Properties/DynamicForm.tsx index b5665b6..76bc4e1 100644 --- a/src/Components/Properties/DynamicForm.tsx +++ b/src/Components/Properties/DynamicForm.tsx @@ -70,12 +70,23 @@ const DynamicForm: React.FunctionComponent = (props) => { value={props.properties.y.toString()} onChange={(event) => props.onChange('y', Number(event.target.value))} /> + props.onChange('minWidth', Number(event.target.value))} + /> props.onChange('width', Number(event.target.value))} /> @@ -85,6 +96,7 @@ const DynamicForm: React.FunctionComponent = (props) => { labelClassName='' inputClassName='' type='number' + min={0} value={props.properties.height.toString()} onChange={(event) => props.onChange('height', Number(event.target.value))} /> diff --git a/src/Components/Properties/Properties.test.tsx b/src/Components/Properties/Properties.test.tsx index dc9473e..0ede184 100644 --- a/src/Components/Properties/Properties.test.tsx +++ b/src/Components/Properties/Properties.test.tsx @@ -27,6 +27,7 @@ describe.concurrent('Properties', () => { y: 1, width: 1, height: 1, + minWidth: 1, XPositionReference: XPositionReference.Left, isRigidBody: false, isAnchor: false diff --git a/src/Components/Properties/StaticForm.tsx b/src/Components/Properties/StaticForm.tsx index 9f6055b..2b0bd6a 100644 --- a/src/Components/Properties/StaticForm.tsx +++ b/src/Components/Properties/StaticForm.tsx @@ -68,12 +68,22 @@ const StaticForm: React.FunctionComponent = (props) => { type='number' defaultValue={props.properties.y.toString()} /> + = (props) => { labelClassName='' inputClassName='' type='number' + min={1} defaultValue={props.properties.height.toString()} /> by a customized "SVG". It is not really an svg but it at least allows diff --git a/src/utils/default.ts b/src/utils/default.ts index 6eaf561..cc7acf6 100644 --- a/src/utils/default.ts +++ b/src/utils/default.ts @@ -33,6 +33,7 @@ export const DEFAULT_MAINCONTAINER_PROPS: IProperties = { parentId: 'null', x: 0, y: 0, + minWidth: 1, width: Number(DEFAULT_CONFIG.MainContainer.Width), height: Number(DEFAULT_CONFIG.MainContainer.Height), isRigidBody: false, @@ -61,7 +62,7 @@ export const GetDefaultContainerProps = ( isRigidBody: false, isAnchor: false, XPositionReference: containerConfig.XPositionReference ?? XPositionReference.Left, - minWidth: containerConfig.MinWidth, + minWidth: containerConfig.MinWidth ?? 0, customSVG: containerConfig.CustomSVG, style: containerConfig.Style, userData: containerConfig.UserData -- 2.47.2