diff --git a/src/Components/App/MenuActions.ts b/src/Components/App/MenuActions.ts index c144acc..8dec2a1 100644 --- a/src/Components/App/MenuActions.ts +++ b/src/Components/App/MenuActions.ts @@ -20,8 +20,8 @@ export function NewEditor( parentId: 'null', x: 0, y: 0, - width: configuration.MainContainer.Width, - height: configuration.MainContainer.Height, + width: Number(configuration.MainContainer.Width), + height: Number(configuration.MainContainer.Height), isRigidBody: false, isAnchor: false, fillOpacity: 0, diff --git a/src/Components/Editor/Behaviors/AnchorBehaviors.ts b/src/Components/Editor/Behaviors/AnchorBehaviors.ts index d9731e8..22ce2f4 100644 --- a/src/Components/Editor/Behaviors/AnchorBehaviors.ts +++ b/src/Components/Editor/Behaviors/AnchorBehaviors.ts @@ -49,7 +49,7 @@ function getOverlappingContainers( containers: IContainerModel[] ): IContainerModel[] { const min1 = container.properties.x; - const max1 = container.properties.x + Number(container.properties.width); + const max1 = container.properties.x + container.properties.width; const overlappingContainers: IContainerModel[] = []; for (const other of containers) { if (other === container) { @@ -57,7 +57,7 @@ function getOverlappingContainers( } const min2 = other.properties.x; - const max2 = other.properties.x + Number(other.properties.width); + const max2 = other.properties.x + other.properties.width; const isOverlapping = Math.min(max1, max2) - Math.max(min1, min2) > 0; if (!isOverlapping) { diff --git a/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts b/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts index eabec85..e0ffb2f 100644 --- a/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts +++ b/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts @@ -42,8 +42,8 @@ function constraintBodyInsideParent( } const parentProperties = container.parent.properties; - const parentWidth = Number(parentProperties.width); - const parentHeight = Number(parentProperties.height); + const parentWidth = parentProperties.width; + const parentHeight = parentProperties.height; return constraintBodyInsideSpace(container, 0, 0, parentWidth, parentHeight); } @@ -66,10 +66,10 @@ function constraintBodyInsideSpace( height: number ): IContainerModel { const containerProperties = container.properties; - const containerX = Number(containerProperties.x); - const containerY = Number(containerProperties.y); - const containerWidth = Number(containerProperties.width); - const containerHeight = Number(containerProperties.height); + const containerX = containerProperties.x; + const containerY = containerProperties.y; + const containerWidth = containerProperties.width; + const containerHeight = containerProperties.height; // Check size bigger than parent const isBodyLargerThanParent = containerWidth > width; @@ -121,8 +121,8 @@ export function constraintBodyInsideUnallocatedWidth( // Get the available spaces of the parent const availableWidths = getAvailableWidths(container.parent, container); - const containerX = Number(container.properties.x); - const containerWidth = Number(container.properties.width); + const containerX = container.properties.x; + const containerWidth = container.properties.width; // Check if there is still some space if (availableWidths.length === 0) { @@ -177,7 +177,7 @@ export function constraintBodyInsideUnallocatedWidth( availableWidthFound.x, 0, availableWidthFound.width, - Number(container.parent.properties.height) + container.parent.properties.height ); } @@ -197,7 +197,7 @@ function getAvailableWidths( // Initialize the first size pointer // which takes full width of the available space const x = 0; - const width = Number(container.properties.width); + const width = container.properties.width; let unallocatedSpaces: ISizePointer[] = [{ x, width }]; // We will only uses containers that also are rigid or are anchors @@ -211,7 +211,7 @@ function getAvailableWidths( continue; } const childX = child.properties.x; - const childWidth = Number(child.properties.width); + const childWidth = child.properties.width; // get the space of the child that is inside the parent let newUnallocatedSpace: ISizePointer[] = []; @@ -309,4 +309,4 @@ function getAvailableWidthsTwoLines( const isFitting = ( container: IContainerModel, sizePointer: ISizePointer -): boolean => Number(container.properties.width) <= sizePointer.width; +): boolean => container.properties.width <= sizePointer.width; diff --git a/src/Components/Editor/ContainerOperations.ts b/src/Components/Editor/ContainerOperations.ts index f1736fc..0ab670d 100644 --- a/src/Components/Editor/ContainerOperations.ts +++ b/src/Components/Editor/ContainerOperations.ts @@ -1,10 +1,13 @@ -import { Dispatch, SetStateAction } from 'react'; +import React, { Dispatch, SetStateAction } from 'react'; import { IHistoryState } from '../../Interfaces/IHistoryState'; 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 { transformPosition } from '../SVG/Elements/Container'; /** * Select a container @@ -169,10 +172,10 @@ export function AddContainer( } // Get the preset properties from the API - const properties = configuration.AvailableContainers + const containerConfig = configuration.AvailableContainers .find(option => option.Type === type); - if (properties === undefined) { + if (containerConfig === undefined) { throw new Error(`[AddContainer] Object type not found. Found: ${type}`); } @@ -198,25 +201,24 @@ export function AddContainer( throw new Error('[AddContainer] Container model was not found among children of the main container!'); } - let x = 0; - if (index > 0) { - const lastChild: IContainerModel | undefined = parentClone.children.at(index - 1); - if (lastChild !== undefined) { - x = lastChild.properties.x + Number(lastChild.properties.width); - } - } + 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 = { + ...containerConfig.Style, id: `${type}-${count}`, parentId: parentClone.properties.id, x, - y: 0, - width: properties.Width, - height: parentClone.properties.height, + y, + width, + height, isRigidBody: false, isAnchor: false, - XPositionReference: properties.XPositionReference, - ...properties.Style + XPositionReference: containerConfig.XPositionReference }; // Create the container @@ -247,3 +249,32 @@ export function AddContainer( setHistory(history); setHistoryCurrentStep(history.length - 1); } + +/** + * Returns a new offset by applying an Add method (append, insert etc.) + * See AddMethod + * @param index Index of the container + * @param containerConfig Configuration of a container + * @param parent Parent container + * @param x Additionnal offset + * @returns New offset + */ +function ApplyAddMethod(index: number, containerConfig: IAvailableContainer, parent: IContainerModel, x: number): number { + if (index > 0 && ( + containerConfig.AddMethod === undefined || + containerConfig.AddMethod === AddMethod.Append)) { + const lastChild: IContainerModel | undefined = parent.children.at(index - 1); + + if (lastChild !== undefined) { + const [transformedX] = transformPosition( + lastChild.properties.x, + lastChild.properties.y, + lastChild.properties.width, + lastChild.properties.XPositionReference + ); + + x += transformedX + lastChild.properties.width; + } + } + return x; +} diff --git a/src/Components/Editor/Editor.tsx b/src/Components/Editor/Editor.tsx index 3a728e3..fcd62f9 100644 --- a/src/Components/Editor/Editor.tsx +++ b/src/Components/Editor/Editor.tsx @@ -133,8 +133,8 @@ const Editor: React.FunctionComponent = (props) => { LoadState={(move) => setHistoryCurrentStep(move)} /> { current.MainContainer } diff --git a/src/Components/Properties/Properties.test.tsx b/src/Components/Properties/Properties.test.tsx index 9fbef37..8b8db31 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 IProperties from '../../Interfaces/IProperties'; import { Properties } from './Properties'; describe.concurrent('Properties', () => { @@ -18,11 +19,13 @@ describe.concurrent('Properties', () => { }); it('Some properties, change values with dynamic input', () => { - const prop = { + const prop: IProperties = { id: 'stuff', parentId: 'parentId', x: 1, y: 1, + width: 1, + height: 1, isRigidBody: false, isAnchor: false }; diff --git a/src/Components/Properties/PropertiesInputTypes.tsx b/src/Components/Properties/PropertiesInputTypes.tsx index d91ddbc..b8f29d3 100644 --- a/src/Components/Properties/PropertiesInputTypes.tsx +++ b/src/Components/Properties/PropertiesInputTypes.tsx @@ -4,5 +4,6 @@ export const INPUT_TYPES: Record = { width: 'number', height: 'number', isRigidBody: 'checkbox', - isAnchor: 'checkbox' + isAnchor: 'checkbox', + XPositionReference: 'number' }; diff --git a/src/Components/SVG/Elements/Container.tsx b/src/Components/SVG/Elements/Container.tsx index e743899..56d4797 100644 --- a/src/Components/SVG/Elements/Container.tsx +++ b/src/Components/SVG/Elements/Container.tsx @@ -15,13 +15,13 @@ interface IContainerProps { */ export const Container: React.FC = (props: IContainerProps) => { const containersElements = props.model.children.map(child => ); - const xText = Number(props.model.properties.width) / 2; - const yText = Number(props.model.properties.height) / 2; + const xText = props.model.properties.width / 2; + const yText = props.model.properties.height / 2; const [transformedX, transformedY] = transformPosition( - Number(props.model.properties.x), - Number(props.model.properties.y), - Number(props.model.properties.width), + props.model.properties.x, + props.model.properties.y, + props.model.properties.width, props.model.properties.XPositionReference ); const transform = `translate(${transformedX}, ${transformedY})`; @@ -48,7 +48,7 @@ export const Container: React.FC = (props: IContainerProps) => const dimensionMargin = DIMENSION_MARGIN * (depth + 1); const id = `dim-${props.model.properties.id}`; const xStart: number = 0; - const xEnd = Number(props.model.properties.width); + const xEnd = props.model.properties.width; const y = -dimensionMargin; const strokeWidth = 1; const text = (props.model.properties.width ?? 0).toString(); @@ -112,25 +112,25 @@ function GetChildrenDimensionProps(props: IContainerProps, dimensionMargin: numb const lastChild = props.model.children[props.model.children.length - 1]; let xChildrenStart = lastChild.properties.x; - let xChildrenEnd = lastChild.properties.x + Number(lastChild.properties.width); + let xChildrenEnd = lastChild.properties.x + lastChild.properties.width; for (let i = props.model.children.length - 2; i >= 0; i--) { const child = props.model.children[i]; const left = child.properties.x; if (left < xChildrenStart) { xChildrenStart = left; } - const right = child.properties.x + Number(child.properties.width); + const right = child.properties.x + child.properties.width; if (right > xChildrenEnd) { xChildrenEnd = right; } } - const yChildren = Number(props.model.properties.height) + dimensionMargin; + const yChildren = props.model.properties.height + dimensionMargin; const textChildren = (xChildrenEnd - xChildrenStart).toString(); return { childrenId, xChildrenStart, xChildrenEnd, yChildren, textChildren }; } -function transformPosition(x: number, y: number, width: number, xPositionReference = XPositionReference.Left): [number, number] { +export function transformPosition(x: number, y: number, width: number, xPositionReference = XPositionReference.Left): [number, number] { let transformedX = x; if (xPositionReference === XPositionReference.Center) { transformedX -= width / 2; diff --git a/src/Components/SVG/Elements/Selector.tsx b/src/Components/SVG/Elements/Selector.tsx index e70ca79..101f19a 100644 --- a/src/Components/SVG/Elements/Selector.tsx +++ b/src/Components/SVG/Elements/Selector.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { IContainerModel } from '../../../Interfaces/IContainerModel'; import { getAbsolutePosition } from '../../../utils/itertools'; +import { transformPosition } from './Container'; interface ISelectorProps { selected: IContainerModel | null @@ -15,6 +16,12 @@ export const Selector: React.FC = (props) => { } const [x, y] = getAbsolutePosition(props.selected); + const [transformedX, transformedY] = transformPosition( + x, + y, + props.selected.properties.width, + props.selected.properties.XPositionReference + ); const [width, height] = [ props.selected.properties.width, props.selected.properties.height @@ -31,8 +38,8 @@ export const Selector: React.FC = (props) => { return ( { id: string parentId: string | null x: number y: number + width: number + height: number isRigidBody: boolean isAnchor: boolean XPositionReference?: XPositionReference diff --git a/src/utils/default.ts b/src/utils/default.ts index 44f737f..b16e528 100644 --- a/src/utils/default.ts +++ b/src/utils/default.ts @@ -30,8 +30,8 @@ export const DEFAULT_MAINCONTAINER_PROPS: IProperties = { parentId: 'null', x: 0, y: 0, - width: DEFAULT_CONFIG.MainContainer.Width, - height: DEFAULT_CONFIG.MainContainer.Height, + width: Number(DEFAULT_CONFIG.MainContainer.Width), + height: Number(DEFAULT_CONFIG.MainContainer.Height), isRigidBody: false, isAnchor: false, fillOpacity: 0, diff --git a/src/utils/itertools.ts b/src/utils/itertools.ts index d50a089..221d6c0 100644 --- a/src/utils/itertools.ts +++ b/src/utils/itertools.ts @@ -43,12 +43,12 @@ export function getDepth(parent: IContainerModel): number { * @returns The absolute position of the container */ export function getAbsolutePosition(container: IContainerModel): [number, number] { - let x = Number(container.properties.x); - let y = Number(container.properties.y); + let x = container.properties.x; + let y = container.properties.y; let current = container.parent; while (current != null) { - x += Number(current.properties.x); - y += Number(current.properties.y); + x += current.properties.x; + y += current.properties.y; current = current.parent; } return [x, y]; diff --git a/test-server/http.js b/test-server/http.js index 69088c9..e6b7051 100644 --- a/test-server/http.js +++ b/test-server/http.js @@ -55,27 +55,44 @@ const GetSVGLayoutConfiguration = () => { Type: 'Chassis', Width: 500, Style: { - fillOpacity: 0, + fillOpacity: 1, borderWidth: 2, + stroke: 'red', + fill: '#78350F', stroke: 'red' } }, { Type: 'Trou', - Width: 300, + DefaultX: 10, + DefaultY: 10, + Width: 480, + Height: 180, Style: { - fillOpacity: 0, + fillOpacity: 1, borderWidth: 2, - stroke: 'green' + stroke: 'green', + fill: 'white' + } + }, + { + Type: 'Remplissage', + Style: { + fillOpacity: 1, + borderWidth: 2, + stroke: '#bfdbfe', + fill: '#bfdbfe' } }, { Type: 'Montant', - Width: 100, + Width: 10, + XPositionReference: 1, Style: { fillOpacity: 0, borderWidth: 2, - stroke: 'blue', + stroke: '#713f12', + fill: '#713f12', } } ],