diff --git a/src/Components/App/MenuActions.ts b/src/Components/App/MenuActions.ts index c144acc..c849ca8 100644 --- a/src/Components/App/MenuActions.ts +++ b/src/Components/App/MenuActions.ts @@ -2,7 +2,7 @@ import { Dispatch, SetStateAction } from 'react'; import { IConfiguration } from '../../Interfaces/IConfiguration'; import { ContainerModel } from '../../Interfaces/IContainerModel'; import { fetchConfiguration } from '../API/api'; -import { IEditorState } from '../../Interfaces/IEditorState'; +import { IEditorState } from "../../Interfaces/IEditorState"; import { LoadState } from './Load'; export function NewEditor( @@ -23,7 +23,6 @@ export function NewEditor( width: configuration.MainContainer.Width, height: configuration.MainContainer.Height, isRigidBody: false, - isAnchor: false, fillOpacity: 0, stroke: 'black' } diff --git a/src/Components/Editor/Behaviors/AnchorBehaviors.ts b/src/Components/Editor/Behaviors/AnchorBehaviors.ts deleted file mode 100644 index 8d1adf9..0000000 --- a/src/Components/Editor/Behaviors/AnchorBehaviors.ts +++ /dev/null @@ -1,64 +0,0 @@ -/** - * @module AnchorBehavior - * - * An anchor is a container that takes physical priority in the representation : - * - It cannot be moved by other rigid siblings container - * - It cannot be resized by any other siblings container - * - It cannot overlap any other siblings rigid container : - * - overlapping container are shifted to the nearest available space/width - * - or resized when there is no available space left other than theirs - * - or lose their rigid body properties when there is no available space left) - * Meaning that: - * - Moving an anchor container will resize the width of an overlapping container - * or make them lose their property as a rigid body - */ - -import { IContainerModel } from '../../../Interfaces/IContainerModel'; -import { constraintBodyInsideUnallocatedWidth } from './RigidBodyBehaviors'; - -/** - * Impose the container position - * Apply the following modification to the overlapping rigid body container : - */ -export function ImposePosition(container: IContainerModel): IContainerModel { - if (container.parent === undefined || - container.parent === null) { - return container; - } - - // Get the closest one - const rigidBodies = container.parent.children.filter( - child => child.properties.isRigidBody && !child.properties.isAnchor - ); - - const overlappingContainers = getOverlappingContainers(container, rigidBodies); - for (const overlappingContainer of overlappingContainers) { - constraintBodyInsideUnallocatedWidth(overlappingContainer); - } - return container; -} - -function getOverlappingContainers( - container: IContainerModel, - siblings: IContainerModel[] -): IContainerModel[] { - const min1 = container.properties.x; - const max1 = container.properties.x + Number(container.properties.width); - const overlappingContainers: IContainerModel[] = []; - for (const sibling of siblings) { - if (sibling === container) { - continue; - } - - const min2 = sibling.properties.x; - const max2 = sibling.properties.x + Number(sibling.properties.width); - const isOverlapping = Math.min(max1, max2) - Math.max(min1, min2) > 0; - - if (!isOverlapping) { - continue; - } - - overlappingContainers.push(sibling); - } - return overlappingContainers; -} diff --git a/src/Components/Editor/ContainerOperations.ts b/src/Components/Editor/ContainerOperations.ts index 63d90a0..40391c5 100644 --- a/src/Components/Editor/ContainerOperations.ts +++ b/src/Components/Editor/ContainerOperations.ts @@ -4,7 +4,6 @@ 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'; /** * Select a container @@ -204,23 +203,20 @@ export function AddContainer( } } - const defaultProperties: IProperties = { - id: `${type}-${count}`, - parentId: parentClone.properties.id, - x, - y: 0, - width: properties.Width, - height: parentClone.properties.height, - isRigidBody: false, - isAnchor: false, - XPositionReference: properties.XPositionReference, - ...properties.Style - }; - // Create the container const newContainer = new ContainerModel( parentClone, - defaultProperties, + { + id: `${type}-${count}`, + parentId: parentClone.properties.id, + x, + y: 0, + width: properties?.Width, + height: parentClone.properties.height, + isRigidBody: false, + XPositionReference: properties.XPositionReference, + ...properties.Style + }, [], { type diff --git a/src/Components/Editor/PropertiesOperations.ts b/src/Components/Editor/PropertiesOperations.ts index ed5a4db..11c1b62 100644 --- a/src/Components/Editor/PropertiesOperations.ts +++ b/src/Components/Editor/PropertiesOperations.ts @@ -4,9 +4,7 @@ import { IHistoryState } from '../../Interfaces/IHistoryState'; import IProperties from '../../Interfaces/IProperties'; import { findContainerById } from '../../utils/itertools'; import { getCurrentHistory } from './Editor'; -import { RecalculatePhysics } from './Behaviors/RigidBodyBehaviors'; -import { INPUT_TYPES } from '../Properties/PropertiesInputTypes'; -import { ImposePosition } from './Behaviors/AnchorBehaviors'; +import { RecalculatePhysics } from './RigidBodyBehaviors'; /** * Handled the property change event in the properties form @@ -30,6 +28,20 @@ export function OnPropertyChange( throw new Error('[OnPropertyChange] Property was changed before selecting a Container'); } + if (parent === null) { + const selectedContainerClone: IContainerModel = structuredClone(current.SelectedContainer); + (selectedContainerClone.properties as any)[key] = 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); @@ -37,15 +49,7 @@ 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); - } else { - (container.properties as any)[key] = value; - } - - if (container.properties.isAnchor) { - ImposePosition(container); - } + (container.properties as any)[key] = value; if (container.properties.isRigidBody) { RecalculatePhysics(container); @@ -84,6 +88,25 @@ export function OnPropertiesSubmit( throw new Error('[OnPropertyChange] Property was changed before selecting a Container'); } + if (parent === null) { + const selectedContainerClone: IContainerModel = structuredClone(current.SelectedContainer); + for (const property in properties) { + const input = (event.target as HTMLFormElement).querySelector(`#${property}`); + if (input instanceof HTMLInputElement) { + (selectedContainerClone.properties as any)[property] = 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); @@ -95,11 +118,6 @@ export function OnPropertiesSubmit( 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; - } } } diff --git a/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts b/src/Components/Editor/RigidBodyBehaviors.ts similarity index 81% rename from src/Components/Editor/Behaviors/RigidBodyBehaviors.ts rename to src/Components/Editor/RigidBodyBehaviors.ts index b683baa..116fbc4 100644 --- a/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts +++ b/src/Components/Editor/RigidBodyBehaviors.ts @@ -1,13 +1,5 @@ -/** - * @module RigidBodyBehaviors - * Apply the following contraints to the `container` : - * - The container must be kept inside its parent - * - The container must find an unallocated space within the parent - * If the contraints fails, an error message will be returned - */ - -import { IContainerModel } from '../../../Interfaces/IContainerModel'; -import { ISizePointer } from '../../../Interfaces/ISizePointer'; +import { IContainerModel } from '../../Interfaces/IContainerModel'; +import { ISizePointer } from '../../Interfaces/ISizePointer'; /** * "Transform the container into a rigid body" @@ -90,7 +82,7 @@ function constraintBodyInsideSpace( if (containerX < x) { containerProperties.x = x; } - if (containerX + containerWidth > x + width) { + if (containerX + containerWidth > width) { containerProperties.x = x + width - containerWidth; } @@ -98,7 +90,7 @@ function constraintBodyInsideSpace( if (containerY < y) { containerProperties.y = y; } - if (containerY + containerHeight > y + height) { + if (containerY + containerHeight > height) { containerProperties.y = y + height - containerHeight; } @@ -112,7 +104,7 @@ function constraintBodyInsideSpace( * @param container * @returns Updated container */ -export function constraintBodyInsideUnallocatedWidth( +function constraintBodyInsideUnallocatedWidth( container: IContainerModel ): IContainerModel { if (container.parent === null) { @@ -122,7 +114,12 @@ 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); + + // Sort the available width to find the closest one + availableWidths.sort( + (width1, width2) => + Math.abs(width1.x - containerX) - Math.abs(width2.x - containerX) + ); // Check if there is still some space if (availableWidths.length === 0) { @@ -131,24 +128,6 @@ export function constraintBodyInsideUnallocatedWidth( ); } - const middle = containerX + containerWidth / 2; - // Sort the available width to find the space with the closest position - availableWidths.sort( - (width1, width2) => { - let compared1X = width1.x; - if (width1.x < containerX) { - compared1X = width1.x + width1.width - containerWidth; - } - - let compared2X = width2.x; - if (width2.x < containerX) { - compared2X = width2.x + width2.width - containerWidth; - } - - return Math.abs(compared1X - middle) - Math.abs(compared2X - middle); - } - ); - // Check if the container actually fit inside // It will usually fit if it was alrady fitting const availableWidthFound = availableWidths.find((width) => @@ -200,18 +179,17 @@ function getAvailableWidths( const width = Number(container.properties.width); let unallocatedSpaces: ISizePointer[] = [{ x, width }]; - // We will only uses containers that also are rigid or are anchors - const solidBodies = container.children.filter( - (child) => child.properties.isRigidBody || child.properties.isAnchor + // We will only uses containers that also have the rigid bodies + // as out-of-bound or enormouse containers should be ignored + const rigidBodies = container.children.filter( + (child) => child.properties.isRigidBody ); - for (const child of solidBodies) { + for (const child of rigidBodies) { // Ignore the exception if (child === exception) { continue; } - const childX = child.properties.x; - const childWidth = Number(child.properties.width); // get the space of the child that is inside the parent let newUnallocatedSpace: ISizePointer[] = []; @@ -224,8 +202,8 @@ function getAvailableWidths( const newUnallocatedWidths = getAvailableWidthsTwoLines( unallocatedSpace.x, unallocatedSpace.x + unallocatedSpace.width, - childX, - childX + childWidth + child.properties.x, + child.properties.x + Number(child.properties.width) ); // Concat the new list of SizePointer pointing to availables spaces @@ -284,7 +262,7 @@ function getAvailableWidthsTwoLines( width: min2 - min1 }, { - x: max2, + x: min2, width: max1 - max2 } ]; diff --git a/src/Components/ElementsSidebar/ElementsSidebar.test.tsx b/src/Components/ElementsSidebar/ElementsSidebar.test.tsx index 8d34480..f1776c0 100644 --- a/src/Components/ElementsSidebar/ElementsSidebar.test.tsx +++ b/src/Components/ElementsSidebar/ElementsSidebar.test.tsx @@ -17,8 +17,7 @@ describe.concurrent('Elements sidebar', () => { y: 0, width: 2000, height: 100, - isRigidBody: false, - isAnchor: false + isRigidBody: false }, userData: {} }} @@ -48,8 +47,7 @@ describe.concurrent('Elements sidebar', () => { y: 0, width: 2000, height: 100, - isRigidBody: false, - isAnchor: false + isRigidBody: false }, userData: {} }; @@ -105,8 +103,7 @@ describe.concurrent('Elements sidebar', () => { y: 0, width: 2000, height: 100, - isRigidBody: false, - isAnchor: false + isRigidBody: false }, userData: {} }; @@ -122,8 +119,7 @@ describe.concurrent('Elements sidebar', () => { y: 0, width: 0, height: 0, - isRigidBody: false, - isAnchor: false + isRigidBody: false }, userData: {} } @@ -140,8 +136,7 @@ describe.concurrent('Elements sidebar', () => { y: 0, width: 0, height: 0, - isRigidBody: false, - isAnchor: false + isRigidBody: false }, userData: {} } @@ -178,8 +173,7 @@ describe.concurrent('Elements sidebar', () => { y: 0, width: 2000, height: 100, - isRigidBody: false, - isAnchor: false + isRigidBody: false }, userData: {} }; @@ -194,8 +188,7 @@ describe.concurrent('Elements sidebar', () => { y: 0, width: 0, height: 0, - isRigidBody: false, - isAnchor: false + isRigidBody: false }, userData: {} }; diff --git a/src/Components/Properties/Properties.test.tsx b/src/Components/Properties/Properties.test.tsx index 9fbef37..2afc364 100644 --- a/src/Components/Properties/Properties.test.tsx +++ b/src/Components/Properties/Properties.test.tsx @@ -23,8 +23,7 @@ describe.concurrent('Properties', () => { parentId: 'parentId', x: 1, y: 1, - isRigidBody: false, - isAnchor: false + isRigidBody: false }; const handleChange = vi.fn((key, value) => { diff --git a/src/Components/Properties/PropertiesInputTypes.tsx b/src/Components/Properties/PropertiesInputTypes.tsx index d91ddbc..c62a8fd 100644 --- a/src/Components/Properties/PropertiesInputTypes.tsx +++ b/src/Components/Properties/PropertiesInputTypes.tsx @@ -3,6 +3,5 @@ export const INPUT_TYPES: Record = { y: 'number', width: 'number', height: 'number', - isRigidBody: 'checkbox', - isAnchor: 'checkbox' + isRigidBody: 'checkbox' }; diff --git a/src/Interfaces/IEditorState.ts b/src/Interfaces/IEditorState.tsx similarity index 100% rename from src/Interfaces/IEditorState.ts rename to src/Interfaces/IEditorState.tsx diff --git a/src/Interfaces/IProperties.ts b/src/Interfaces/IProperties.ts index ef2db7e..4996ce5 100644 --- a/src/Interfaces/IProperties.ts +++ b/src/Interfaces/IProperties.ts @@ -1,21 +1,11 @@ import * as React from 'react'; 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 extends React.CSSProperties { id: string parentId: string | null x: number y: number isRigidBody: boolean - isAnchor: boolean XPositionReference?: XPositionReference } diff --git a/src/utils/default.ts b/src/utils/default.ts index 2bd22dc..785a840 100644 --- a/src/utils/default.ts +++ b/src/utils/default.ts @@ -33,7 +33,6 @@ export const DEFAULT_MAINCONTAINER_PROPS: IProperties = { width: DEFAULT_CONFIG.MainContainer.Width, height: DEFAULT_CONFIG.MainContainer.Height, isRigidBody: false, - isAnchor: false, fillOpacity: 0, stroke: 'black' };