From 443a15e150d2476536c7731415d78efc0dae077f Mon Sep 17 00:00:00 2001 From: Eric Nguyen Date: Thu, 8 Sep 2022 10:29:44 +0000 Subject: [PATCH] Merged PR 179: Fix bugs about flex and context menu (see desc) + disable hard rigid behavior + add missing properties to form + Clean up css - Clean up some css class - Fix wrong order when applying flex - Fix Replace behavior not working because previous container was still existing - Disable hard rigid behavior which disallow two container to overlap - Add ENABLE_FLEX, ENABLE_HARD_RIGID ENABLE_SWAP - Add missing form properties with dimensions - Update readme --- README.md | 39 ++++++++++++++-- src/Components/API/api.ts | 2 +- src/Components/App/App.scss | 0 src/Components/App/App.tsx | 3 +- src/Components/Bar/Bar.tsx | 2 +- .../ContainerProperties/ContainerForm.tsx | 46 ++++++++++++++++++- .../Editor/Actions/ContainerOperations.ts | 6 +-- .../Editor/Actions/ContextMenuActions.ts | 27 ++++++----- src/Components/Editor/Behaviors/Behaviors.ts | 10 ++-- .../Editor/Behaviors/FlexBehaviors.ts | 12 ++--- .../Editor/Behaviors/RigidBodyBehaviors.ts | 7 ++- src/Components/InputGroup/InputGroup.tsx | 7 +-- src/index.scss | 23 ++++++++-- src/main.tsx | 3 ++ src/utils/default.ts | 13 ++++++ test-server/http.js | 3 +- 16 files changed, 158 insertions(+), 45 deletions(-) delete mode 100644 src/Components/App/App.scss diff --git a/README.md b/README.md index a3d003d..e413c05 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,9 @@ An svg layout designer. Requierements : - NodeJS - npm -- pnpm (optional but recommanded unless you prefer having a huge `node_modules` directory) - Chrome > 98 +- pnpm (optional but recommanded unless you prefer having a huge `node_modules` directory) +- [`git-lfs`](https://git-lfs.github.com/) (in order to clone the documentation) # Developping @@ -22,9 +23,6 @@ Run `npm ci` Run `npm run dev` - - - # Deploy Run `npm ci` @@ -72,4 +70,35 @@ bun run http.js The web server will be running at `http://localhost:5000` -Configure the file `.env.development` with the url \ No newline at end of file +Configure the file `.env.development` with the url + +# Recommanded tools + +- [VSCode](https://code.visualstudio.com/) +- [React DevTools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi) +- [vscode-tailwindcss](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) +- [vscode-eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + +# Setup debuggin with chrome + +Inside `.vscode/settings.json`, set the following : + +```json +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "Launch Chrome against localhost", + "url": "http://localhost:5173", + "webRoot": "${workspaceFolder}", + } + ] +} +``` + +Change the `url` to the dev server url. Set the `runtimeExecutable` to you favorite chromium browser. \ No newline at end of file diff --git a/src/Components/API/api.ts b/src/Components/API/api.ts index 510428d..6a43c72 100644 --- a/src/Components/API/api.ts +++ b/src/Components/API/api.ts @@ -1,5 +1,4 @@ import { IConfiguration } from '../../Interfaces/IConfiguration'; -import { IHistoryState } from '../../Interfaces/IHistoryState'; import { ISetContainerListRequest } from '../../Interfaces/ISetContainerListRequest'; import { ISetContainerListResponse } from '../../Interfaces/ISetContainerListResponse'; import { GetCircularReplacer } from '../../utils/saveload'; @@ -43,6 +42,7 @@ export async function SetContainerList(request: ISetContainerListRequest): Promi return await fetch(url, { method: 'POST', headers: new Headers({ + // eslint-disable-next-line @typescript-eslint/naming-convention 'Content-Type': 'application/json' }), body: dataParsed diff --git a/src/Components/App/App.scss b/src/Components/App/App.scss deleted file mode 100644 index e69de29..0000000 diff --git a/src/Components/App/App.tsx b/src/Components/App/App.tsx index b0a2374..3b1771b 100644 --- a/src/Components/App/App.tsx +++ b/src/Components/App/App.tsx @@ -1,5 +1,4 @@ import React, { Dispatch, SetStateAction, useEffect, useState } from 'react'; -import './App.scss'; import { MainMenu } from '../MainMenu/MainMenu'; import { ContainerModel } from '../../Interfaces/IContainerModel'; import { Editor } from '../Editor/Editor'; @@ -78,7 +77,7 @@ export function App(props: IAppProps): JSX.Element { } return ( -
+
NewEditor( setEditorState, setLoaded diff --git a/src/Components/Bar/Bar.tsx b/src/Components/Bar/Bar.tsx index a8ed605..7125f30 100644 --- a/src/Components/Bar/Bar.tsx +++ b/src/Components/Bar/Bar.tsx @@ -16,7 +16,7 @@ export const BAR_WIDTH = 64; // 4rem export function Bar(props: IBarProps): JSX.Element { return ( -
+
+
props.onChange('linkedSymbolId', event.target.value)} /> {GetCSSInputs(props.properties, props.onChange)} + { + SHOW_SELF_DIMENSIONS && + props.onChange('showSelfDimensions', event.target.checked)} /> + } + { + SHOW_CHILDREN_DIMENSIONS && + props.onChange('showChildrenDimensions', event.target.checked)} /> + } + { + SHOW_BORROWER_DIMENSIONS && + <> + props.onChange('markPositionToDimensionBorrower', event.target.checked)} /> + props.onChange('isDimensionBorrower', event.target.checked)} /> + + }
); } diff --git a/src/Components/Editor/Actions/ContainerOperations.ts b/src/Components/Editor/Actions/ContainerOperations.ts index b1d24a9..d0dc7e4 100644 --- a/src/Components/Editor/Actions/ContainerOperations.ts +++ b/src/Components/Editor/Actions/ContainerOperations.ts @@ -276,6 +276,9 @@ export function AddContainers( parentClone.children.splice(index, 0, newContainer); } + // Sort the parent children by x + UpdateParentChildrenList(parentClone); + /// Handle behaviors here /// // Initialize default children of the container @@ -287,9 +290,6 @@ export function AddContainers( // Then, apply the behaviors on its siblings (mostly for flex) ApplyBehaviorsOnSiblingsChildren(newContainer, current.symbols); - // Sort the parent children by x - UpdateParentChildrenList(parentClone); - // Add to the list of container id for logging purpose containerIds.push(newContainer.properties.id); }); diff --git a/src/Components/Editor/Actions/ContextMenuActions.ts b/src/Components/Editor/Actions/ContextMenuActions.ts index 260ce73..64f37fc 100644 --- a/src/Components/Editor/Actions/ContextMenuActions.ts +++ b/src/Components/Editor/Actions/ContextMenuActions.ts @@ -105,27 +105,30 @@ function HandleReplace( const index = selectedContainer.parent.children.indexOf(selectedContainer); const types = response.Containers.map(container => container.Type); - const newHistoryBeforeDelete = AddContainers( - index + 1, - types, - selectedContainer.properties.parentId, - configuration, + + const newHistoryAfterDelete = DeleteContainer( + selectedContainer.properties.id, history, historyCurrentStep ); - const newHistoryAfterDelete = DeleteContainer( - selectedContainer.properties.id, - newHistoryBeforeDelete, - newHistoryBeforeDelete.length - 1 + const newHistoryBeforeDelete = AddContainers( + index, + types, + selectedContainer.properties.parentId, + configuration, + newHistoryAfterDelete, + newHistoryAfterDelete.length - 1 ); // Remove AddContainers from history - newHistoryAfterDelete.splice(newHistoryAfterDelete.length - 2, 1); + if (import.meta.env.PROD) { + newHistoryBeforeDelete.splice(newHistoryBeforeDelete.length - 2, 1); + } // Rename the last action by Replace - newHistoryAfterDelete[newHistoryAfterDelete.length - 1].lastAction = + newHistoryBeforeDelete[newHistoryBeforeDelete.length - 1].lastAction = `Replace ${selectedContainer.properties.id} by [${types.join(', ')}]`; - return newHistoryAfterDelete; + return newHistoryBeforeDelete; } diff --git a/src/Components/Editor/Behaviors/Behaviors.ts b/src/Components/Editor/Behaviors/Behaviors.ts index 7dfc78a..116917b 100644 --- a/src/Components/Editor/Behaviors/Behaviors.ts +++ b/src/Components/Editor/Behaviors/Behaviors.ts @@ -1,6 +1,6 @@ import { IContainerModel } from '../../../Interfaces/IContainerModel'; import { ISymbolModel } from '../../../Interfaces/ISymbolModel'; -import { APPLY_BEHAVIORS_ON_CHILDREN } from '../../../utils/default'; +import { APPLY_BEHAVIORS_ON_CHILDREN, ENABLE_RIGID, ENABLE_SWAP } from '../../../utils/default'; import { ApplyAnchor } from './AnchorBehaviors'; import { Flex } from './FlexBehaviors'; import { ApplyRigidBody } from './RigidBodyBehaviors'; @@ -23,11 +23,15 @@ export function ApplyBehaviors(container: IContainerModel, symbols: Map sibling.properties.minWidth); @@ -50,12 +54,8 @@ function FlexGroup(flexibleGroup: IFlexibleGroup): void { const checkSumMinWidthsIsFitting = minimumPossibleWidth > requiredMaxWidth; if (checkSumMinWidthsIsFitting) { - Swal.fire({ - icon: 'error', - title: 'Cannot fit!', - text: 'Cannot fit at all even when squeezing all flex containers to the minimum.' - }); - throw new Error('[FlexBehavior] Cannot fit at all even when squeezing all flex containers to the minimum.'); + console.warn('[FlexBehavior] Cannot fit at all even when squeezing all flex containers to the minimum.'); + return; } const maxMinWidths = Math.max(...minWidths); diff --git a/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts b/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts index bdf2e71..90e4ac9 100644 --- a/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts +++ b/src/Components/Editor/Behaviors/RigidBodyBehaviors.ts @@ -9,6 +9,7 @@ import Swal from 'sweetalert2'; import { IContainerModel } from '../../../Interfaces/IContainerModel'; import { ISizePointer } from '../../../Interfaces/ISizePointer'; +import { ENABLE_HARD_RIGID } from '../../../utils/default'; /** * "Transform the container into a rigid body" @@ -23,7 +24,11 @@ export function ApplyRigidBody( container: IContainerModel ): IContainerModel { container = ConstraintBodyInsideParent(container); - container = ConstraintBodyInsideUnallocatedWidth(container); + + if (ENABLE_HARD_RIGID) { + container = ConstraintBodyInsideUnallocatedWidth(container); + } + return container; } diff --git a/src/Components/InputGroup/InputGroup.tsx b/src/Components/InputGroup/InputGroup.tsx index 8bb83f6..68b5910 100644 --- a/src/Components/InputGroup/InputGroup.tsx +++ b/src/Components/InputGroup/InputGroup.tsx @@ -17,12 +17,7 @@ interface IInputGroupProps { 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`; +const className = 'input-group'; export function InputGroup(props: IInputGroupProps): JSX.Element { return <> diff --git a/src/index.scss b/src/index.scss index c9b1963..4237259 100644 --- a/src/index.scss +++ b/src/index.scss @@ -18,7 +18,6 @@ .elements-sidebar-row { @apply pl-6 pr-6 pt-2 pb-2 w-full } - .symbols-sidebar-row { @apply elements-sidebar-row } @@ -27,6 +26,10 @@ @apply transition-all w-full h-auto p-4 flex } + .mainmenu-bg { + @apply bg-blue-100 h-full w-full + } + .mainmenu-btn { @apply transition-all bg-blue-100 hover:bg-blue-200 text-blue-700 text-lg font-semibold p-8 rounded-lg } @@ -42,7 +45,13 @@ } .floating-btn { - @apply h-full w-full text-white align-middle items-center justify-center + @apply h-full w-full text-white align-middle + items-center justify-center + } + + .bar { + @apply fixed z-20 flex flex-col top-0 left-0 + h-full w-16 bg-slate-100 } .bar-btn { @@ -64,10 +73,18 @@ text-gray-800 bg-slate-100 dark:text-white dark:bg-gray-800 text-xs font-bold - transition-all duration-100 scale-0 origin-left; + transition-all duration-100 scale-0 origin-left } .contextmenu-item { @apply px-2 py-1 hover:bg-slate-300 text-left } + + .input-group { + @apply 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; + } } \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index 264c622..21c0771 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -11,7 +11,10 @@ function RenderRoot(root: Element | Document): void { ); } +// Specific for Modeler apps +// eslint-disable-next-line @typescript-eslint/no-namespace namespace SVGLayoutDesigner { + // eslint-disable-next-line @typescript-eslint/naming-convention export const Render = RenderRoot; } diff --git a/src/utils/default.ts b/src/utils/default.ts index 5af68aa..0488fe4 100644 --- a/src/utils/default.ts +++ b/src/utils/default.ts @@ -9,6 +9,19 @@ import { ISymbolModel } from '../Interfaces/ISymbolModel'; /// CONTAINER DEFAULTS /// +/** Enable the swap behavior */ +export const ENABLE_SWAP = false; + +/** Enable the rigid behavior */ +export const ENABLE_RIGID = true; + +/** + * Enable the hard rigid behavior + * disallowing the container to overlap (ENABLE_RIGID must be true) + */ +export const ENABLE_HARD_RIGID = false; + +/** Enalbe the text in the containers */ export const SHOW_TEXT = false; export const SHOW_SELECTOR_TEXT = true; export const DEFAULTCHILDTYPE_ALLOW_CYCLIC = false; diff --git a/test-server/http.js b/test-server/http.js index 4c84bfd..be800ae 100644 --- a/test-server/http.js +++ b/test-server/http.js @@ -55,6 +55,7 @@ const GetSVGLayoutConfiguration = () => { Type: 'Chassis', MaxWidth: 500, MinWidth: 200, + Width: 200, DefaultChildType: 'Trou', Style: { fillOpacity: 1, @@ -63,7 +64,7 @@ const GetSVGLayoutConfiguration = () => { fill: '#d3c9b7', }, ShowSelfDimensions: true, - IsDimensionBorrower: true + IsDimensionBorrower: true, }, { Type: 'Trou',