Merged PR 212: Optimize FindChildrenById from O(n) to O(1)
Optimize FindChildrenById from O(n) to O(1): - Deprecate FindContainerByIdDFS - Container: Replace Children to string[] - Add HashMap to IHistoryState that contains all containers To access a container by id now cost O(1) without any additional cost + Implement CICD for SVGLibs
This commit is contained in:
parent
466ef2b08b
commit
c256a76e01
45 changed files with 775 additions and 450 deletions
|
@ -1,3 +0,0 @@
|
||||||
VITE_API_FETCH_URL=http://localhost:5000
|
|
||||||
VITE_API_SET_CONTAINER_LIST_URL=http://localhost:5000/SetContainerList
|
|
||||||
VITE_API_GET_FEEDBACK_URL=http://localhost:5000/GetFeedback
|
|
|
@ -1,3 +0,0 @@
|
||||||
VITE_API_FETCH_URL=https://localhost/SmartMenuiserieTemplate/Service.svc/GetSVGLayoutConfiguration
|
|
||||||
VITE_API_SET_CONTAINER_LIST_URL=https://localhost/SmartMenuiserieTemplate/Service.svc/SetContainerList
|
|
||||||
VITE_API_GET_FEEDBACK_URL=https://localhost/SmartMenuiserieTemplate/Service.svc/GetFeedback
|
|
|
@ -1,3 +0,0 @@
|
||||||
VITE_API_FETCH_URL=http://localhost:5000
|
|
||||||
VITE_API_SET_CONTAINER_LIST_URL=http://localhost:5000/SetContainerList
|
|
||||||
VITE_API_GET_FEEDBACK_URL=http://localhost:5000/GetFeedback
|
|
|
@ -13,6 +13,10 @@ pool:
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
pnpm_config_cache: $(Pipeline.Workspace)/.pnpm-store
|
pnpm_config_cache: $(Pipeline.Workspace)/.pnpm-store
|
||||||
|
CSWebProjectLocation: '$(System.DefaultWorkingDirectory)/csharp/SVGLDLibs/SVGLDWebAPI/SVGLDWebAPI.csproj'
|
||||||
|
CSLibsProjectLocation: '$(System.DefaultWorkingDirectory)/csharp/SVGLDLibs/SVGLDLibs/SVGLDLibs.csproj'
|
||||||
|
CSLibsProjectModelsLocation: '$(System.DefaultWorkingDirectory)/csharp/SVGLDLibs/SVGLDLibs/Models'
|
||||||
|
buildConfiguration: 'Release'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: Cache@2
|
- task: Cache@2
|
||||||
|
@ -47,7 +51,7 @@ steps:
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
node --version
|
node --version
|
||||||
node ./test-server/http.js &
|
node ./test-server/http.js &
|
||||||
dotnet run --project=./csharp/SVGLDLibs/SVGLDWebAPI/SVGLDWebAPI.csproj &
|
dotnet run --project=$(CSWebProjectLocation) &
|
||||||
jobs
|
jobs
|
||||||
sleep 10
|
sleep 10
|
||||||
pnpm i
|
pnpm i
|
||||||
|
@ -58,3 +62,12 @@ steps:
|
||||||
|
|
||||||
- publish: $(System.DefaultWorkingDirectory)/dist
|
- publish: $(System.DefaultWorkingDirectory)/dist
|
||||||
artifact: svg-layout-designer
|
artifact: svg-layout-designer
|
||||||
|
|
||||||
|
- script: dotnet build $(CSLibsProjectLocation) --configuration $(buildConfiguration) --output $(build.artifactstagingdirectory)
|
||||||
|
displayName: 'dotnet build $(buildConfiguration)'
|
||||||
|
|
||||||
|
- publish: $(Build.ArtifactStagingDirectory)
|
||||||
|
artifact: svg-layout-designer-net
|
||||||
|
|
||||||
|
- publish: $(CSLibsProjectModelsLocation)
|
||||||
|
artifact: svg-layout-designer-net-source
|
|
@ -9,7 +9,10 @@ namespace SVGLDLibs.Models
|
||||||
public string lastAction;
|
public string lastAction;
|
||||||
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public ContainerModel mainContainer;
|
public string mainContainer;
|
||||||
|
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public Dictionary<string, ContainerModel> containers;
|
||||||
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public string selectedContainerId;
|
public string selectedContainerId;
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace SVGLDLibs.Models
|
||||||
public class ContainerModel
|
public class ContainerModel
|
||||||
{
|
{
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public List<ContainerModel> children;
|
public List<string> children;
|
||||||
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public ContainerProperties properties;
|
public ContainerProperties properties;
|
||||||
|
|
217
docs/DataStructure.md
Normal file
217
docs/DataStructure.md
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
# Préface
|
||||||
|
|
||||||
|
Ce document explique la structure de données utilisée pour les conteneurs.
|
||||||
|
|
||||||
|
|
||||||
|
# Graphe
|
||||||
|
|
||||||
|
Avant de parler de la structure principale il faut comprendre ce qu'est un graphe.
|
||||||
|
|
||||||
|
Pourquoi ? Parce que la plupart des algorithmes utilisés dans ce projets découle de la théorie des graphes.
|
||||||
|
|
||||||
|
> Definition : Un graphe est une structure composée d'objets dans laquelle certaines paries d'objets sont en relation. (...) On distingue les graphes non orientés et les graphes orientés. (src: wikipedia)
|
||||||
|
|
||||||
|
Ces objets sont appelé Container dans notre projet. Mais dans cette documentation, nous appelerons cela un *Noeud*
|
||||||
|
|
||||||
|
En programmation un graphe peut être représenter par des divers structure de données (Vector/List/Array, Dictionaries, Tree, Pointer etc.).
|
||||||
|
|
||||||
|
Dans notre projet, nous utilisons les pointeurs, les listes et les dictionnaires.
|
||||||
|
|
||||||
|
# Structures de données
|
||||||
|
|
||||||
|
## Pointeur
|
||||||
|
|
||||||
|
Un graphe représenté par des pointeurs peut être représenté de cette manière.
|
||||||
|
|
||||||
|
Un exemple de graphe peut être représenté de la manière suivante :
|
||||||
|
```
|
||||||
|
type Node = {
|
||||||
|
child: Node
|
||||||
|
}
|
||||||
|
|
||||||
|
A: Node = {
|
||||||
|
child: B
|
||||||
|
}
|
||||||
|
|
||||||
|
B: Node = {
|
||||||
|
child: A
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Donc le graphe est simplement `A <-> B`
|
||||||
|
|
||||||
|
Ceci est un graphe cyclique que nous ne verrons pas souvent dans ce projet.
|
||||||
|
En effet, nous avons plutôt quelque chose comme ça:
|
||||||
|
|
||||||
|
```
|
||||||
|
type Node = {
|
||||||
|
parent: Node
|
||||||
|
child: Node
|
||||||
|
}
|
||||||
|
|
||||||
|
A: Node = {
|
||||||
|
parent: null
|
||||||
|
child: B
|
||||||
|
}
|
||||||
|
|
||||||
|
B: Node = {
|
||||||
|
parent: A
|
||||||
|
child: null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Cela permet de représenter un graphe avec deux liens au lieu d'un seul et d'avoir une information de **hiérarchie**.
|
||||||
|
On le représente donc comme cela: `A -> B`. Donc par définition, un graphe orienté.
|
||||||
|
|
||||||
|
|
||||||
|
## Liste
|
||||||
|
|
||||||
|
Un noeud dans un graphe peut avoir plusieurs voisins/enfants. On peut simplement représenter cela par une *liste* de pointeurs.
|
||||||
|
|
||||||
|
Reprenons l'exemple précédent :
|
||||||
|
|
||||||
|
```
|
||||||
|
type Node = {
|
||||||
|
parent: Node
|
||||||
|
children: Node[]
|
||||||
|
}
|
||||||
|
|
||||||
|
A: Node {
|
||||||
|
parent: null,
|
||||||
|
children: [
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
B: Node {
|
||||||
|
parent: A
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
|
||||||
|
C: Node {
|
||||||
|
parent: A
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Ici, A contient donc deux enfants dans une liste: B et C.
|
||||||
|
On peut représenter cela de la manière suivante:
|
||||||
|
|
||||||
|
```
|
||||||
|
A -> B
|
||||||
|
A -> C
|
||||||
|
```
|
||||||
|
|
||||||
|
A partir d'ici vous pouvez voir ce qu'on appelle un *arbre*. Cette structure de données d'arbre est la base fondamentale dont repose les Conteneurs.
|
||||||
|
|
||||||
|
Pour des examples réels, vous pouvez voir qu'un livre peut être représenté comme des arbres :
|
||||||
|
|
||||||
|
> Un livre contiennent des pages, et des pages contiennent des lignes, des lignes contiennent des lettres, etc.
|
||||||
|
|
||||||
|
|
||||||
|
## Dictionnaire
|
||||||
|
|
||||||
|
### Contexte
|
||||||
|
|
||||||
|
Normalement l'arbre devrait être suffisant pour développer SVGLayoutDesigner mais on va s'en rendre compte que ce n'est pas la meilleure solution.
|
||||||
|
|
||||||
|
Depuis le début du projet, la structure utilisée était celle d'un arbre avec relation parent-enfant comme précédemment montré.
|
||||||
|
|
||||||
|
Mon on a remarqué tardivement que cela commençait à avoir un coût très important sur toutes les opérations d'arbres. En effet, car toutes les opérations d'arbre repose sur une fonction principale: la recherche.
|
||||||
|
|
||||||
|
Pour trouver un noeud dans un arbre, il faut parcours l'arbre grâce à des algorithme de parcours d'arbre appelé Depth-First Search et Breath-First Search, qui sont d'excellent algorithme de recherche dans un graphe (oui! car ça ne se limite pas aux arbres).
|
||||||
|
|
||||||
|
Cependant, cela possède un coût au pire cas de O(N). Il est possible que le noeud que le cherche se trouve tout à la fin de l'arbre et ces deux algorithmes n'aident pas du tout dans ce cas là. Imaginez 1000 conteneurs, et on veut juste changer la position d'un seul conteneur. Cela veut dire qu'il faudrait parcourir les 1000 conteneurs !
|
||||||
|
|
||||||
|
Et c'est ce qui c'est passé jusqu'au commit `9f4796d0` (10 oct 2022). Faites une recherche globale de `FindContainerById`, cela montrera toutes les opérations impactées par l'arbre. Au jour de l'écriture de ce document, il y a environ 60 fonctions utilisant cette méthode utilisée dans divers opérations actives ou passives. C'est très COUTEUX !
|
||||||
|
|
||||||
|
Réduisons cela à O(1), le meilleur cas. Pour cela nous allons utiliser ce qu'on appelle un dictionnaire ou aussi appelé aussi HashMap.
|
||||||
|
|
||||||
|
|
||||||
|
### Qu'est-ce qu'un dictionnaire ?
|
||||||
|
|
||||||
|
> Un dictionnaire est une structure de données qui implémente un tableau associatif. C'est un type de données abstrait qui permet l'association de clé-valeur.
|
||||||
|
|
||||||
|
Exemples :
|
||||||
|
|
||||||
|
```
|
||||||
|
Dict {
|
||||||
|
key: value
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
Dict = [
|
||||||
|
[key, value],
|
||||||
|
...
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mise en pratique
|
||||||
|
|
||||||
|
On veut accéder aux conteneurs rapidement, nous allons sauvegarder chaque conteneur dans le dictionnaire. Donc dès que l'ont crée un conteneur, on ajoute son id pour clé et le conteneur en tant que valeur. Dès qu'on le supprime, on supprime sa clé.
|
||||||
|
|
||||||
|
Cependant pour éviter la duplication de données, il faut aussi changer comment on représente l'arbre. Il n'est plus nécessaire de sauvegarder la référence de l'enfant en tant qu'enfant, on peut juste utiliser son id.
|
||||||
|
|
||||||
|
Ainsi on obtient la structure suivante utilisée dans le projet :
|
||||||
|
|
||||||
|
```
|
||||||
|
type Conteneur = {
|
||||||
|
parent: string
|
||||||
|
children: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const dict = new Map<string, Conteneur>();
|
||||||
|
|
||||||
|
dict = {
|
||||||
|
"conteneur1": Conteneur {
|
||||||
|
parent: null,
|
||||||
|
children: [
|
||||||
|
"conteneur2",
|
||||||
|
"conteneur3"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"conteneur3": Conteneur {
|
||||||
|
parent: "conteneur1",
|
||||||
|
children: []
|
||||||
|
},
|
||||||
|
"conteneur2": Conteneur {
|
||||||
|
parent: "conteneur1",
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Ainsi, `FindContainerById` utilisant précédemment depth-first search, peut être refactoré par une seule ligne :
|
||||||
|
|
||||||
|
```ts
|
||||||
|
function FindContainerById(
|
||||||
|
containers: Map<string, Conteneur>,
|
||||||
|
id: string
|
||||||
|
): Conteneur {
|
||||||
|
return containers.get(id)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Et maintenant déplacer un seul conteneur ne coûte plus aussi cher pas vrai ?
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
Pourquoi est-ce important d'utiliser un dictionnaire dans notre cas ?
|
||||||
|
|
||||||
|
> Cela permet d'accéder un conteneur directement par une clé par exemple son id
|
||||||
|
|
||||||
|
|
||||||
|
Pourquoi ne pas l'utiliser tout le temps ?
|
||||||
|
|
||||||
|
> Car ce n'est pas très intuitif, on aime voir les arbres comme des arbres et pas comme des listes. Le coût est parfois suffisamment mineur pour ignorer cela.
|
||||||
|
|
||||||
|
|
||||||
|
Pourquoi ne pas utiliser l'arbre avec le dictionnaire en même temps ?
|
||||||
|
|
||||||
|
> Car dans notre projet, la sérialisation des données ne permet pas d'avoir deux instances à deux endroits différents. C'est pourquoi nous utilisons un *replacer*, pour supprimer les références de parents. Mais il serait difficile de faire cela pour tous les enfants, il est plus simple de supprimer entièrement l'arbre et de juste conserver le dictionnaire. Et puis, pourquoi dupliquer les données alors que l'on l'accéder avec un coût minimal O(1) avec juste le dictionnaire sans aucun coût supplémentaire ?
|
||||||
|
|
||||||
|
|
||||||
|
Et si je veux itérer sur tout les chassis ?
|
||||||
|
|
||||||
|
> Depth-first search et Breath-first search sont toujours valables. Il faut juste adapter légèrement l'algorithme pour qu'il lit le dictionnaire à chaque fois que l'on veut accéder aux enfants. Voir `MakeDFSIterator` ou `MakeBFSIterator` dans `src/utils/itertools.ts` pour l'exemple.
|
|
@ -26,18 +26,18 @@ Il y deux manières de récupérer les builds du projets:
|
||||||
|
|
||||||
Customiser le build du projet permet de modifier les urls de l'API et de personnaliser des fonctionnalités.
|
Customiser le build du projet permet de modifier les urls de l'API et de personnaliser des fonctionnalités.
|
||||||
|
|
||||||
## Configurer les options de build
|
## Configurer l'API
|
||||||
|
|
||||||
Il y a deux fichiers principaux à configurer :
|
Il y a deux fichiers principaux à configurer :
|
||||||
- `.env.production.local`: pour configurer les URLs d'API
|
- `src/assets/svgld-settings.js`: pour configurer les URLs d'API
|
||||||
- `src/utils/default.ts`: pour configurer les fonctionnalités
|
- `src/utils/default.ts`: pour configurer les fonctionnalités
|
||||||
|
|
||||||
Copiez `.env.production` vers `.env.production.local` et modifiez-le comme bon vous semble :
|
Modifiez `public/svgld-settings.js`
|
||||||
|
|
||||||
```
|
```js
|
||||||
VITE_API_FETCH_URL=https://localhost/SmartMenuiserieTemplate/Service.svc/GetSVGLayoutConfiguration
|
export const API_FETCH_URL = 'http://localhost:5000';
|
||||||
VITE_API_SET_CONTAINER_LIST_URL=https://localhost/SmartMenuiserieTemplate/Service.svc/SetContainerList
|
export const API_SET_CONTAINER_LIST_URL = 'http://localhost:5000/SetContainerList';
|
||||||
VITE_API_GET_FEEDBACK_URL=https://localhost/SmartMenuiserieTemplate/Service.svc/GetFeedback
|
export const API_GET_FEEDBACK_URL = 'http://localhost:5000/GetFeedback';
|
||||||
```
|
```
|
||||||
|
|
||||||
Vous pouvez modifiez `src/utils/default.ts` mais ne le committez pas.
|
Vous pouvez modifiez `src/utils/default.ts` mais ne le committez pas.
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/svgld-settings.js"></script>
|
||||||
<script type="module" src="./src/main.tsx"></script>
|
<script type="module" src="./src/main.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
4
public/svgld-settings.d.ts
vendored
Normal file
4
public/svgld-settings.d.ts
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
export declare const API_FETCH_URL: string;
|
||||||
|
export declare const API_SET_CONTAINER_LIST_URL: string;
|
||||||
|
export declare const API_GET_FEEDBACK_URL: string;
|
3
public/svgld-settings.js
Normal file
3
public/svgld-settings.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export const API_FETCH_URL = 'http://localhost:5000';
|
||||||
|
export const API_SET_CONTAINER_LIST_URL = 'http://localhost:5000/SetContainerList';
|
||||||
|
export const API_GET_FEEDBACK_URL = 'http://localhost:5000/GetFeedback';
|
|
@ -9,6 +9,10 @@ const getCircularReplacer = () => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (key === 'containers') {
|
||||||
|
return Array.from(value.entries());
|
||||||
|
}
|
||||||
|
|
||||||
if (key === 'symbols') {
|
if (key === 'symbols') {
|
||||||
return Array.from(value.entries());
|
return Array.from(value.entries());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
import { describe, it, expect } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { API_FETCH_URL } from '../../../public/svgld-settings';
|
||||||
import { AddMethod } from '../../Enums/AddMethod';
|
import { AddMethod } from '../../Enums/AddMethod';
|
||||||
import { Orientation } from '../../Enums/Orientation';
|
import { Orientation } from '../../Enums/Orientation';
|
||||||
import { Position } from '../../Enums/Position';
|
import { Position } from '../../Enums/Position';
|
||||||
|
@ -10,7 +11,6 @@ import { IAvailableSymbol } from '../../Interfaces/IAvailableSymbol';
|
||||||
import { ICategory } from '../../Interfaces/ICategory';
|
import { ICategory } from '../../Interfaces/ICategory';
|
||||||
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
||||||
import { IContainerModel, ContainerModel } from '../../Interfaces/IContainerModel';
|
import { IContainerModel, ContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { IEditorState } from '../../Interfaces/IEditorState';
|
|
||||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||||
import { IPattern } from '../../Interfaces/IPattern';
|
import { IPattern } from '../../Interfaces/IPattern';
|
||||||
import { DEFAULT_MAINCONTAINER_PROPS, GetDefaultContainerProps } from '../../utils/default';
|
import { DEFAULT_MAINCONTAINER_PROPS, GetDefaultContainerProps } from '../../utils/default';
|
||||||
|
@ -21,9 +21,9 @@ const CHARP_WEB_API_RESOURCE_URL = 'SVGLD';
|
||||||
const CSHARP_WEB_API_URL = CSHARP_WEB_API_BASE_URL + CHARP_WEB_API_RESOURCE_URL + '/';
|
const CSHARP_WEB_API_URL = CSHARP_WEB_API_BASE_URL + CHARP_WEB_API_RESOURCE_URL + '/';
|
||||||
|
|
||||||
// TODO: Migrate this test to SVGLDWebAPI rather than using test-server/
|
// TODO: Migrate this test to SVGLDWebAPI rather than using test-server/
|
||||||
describe.concurrent('Test server test', () => {
|
describe.concurrent('Test server test', async() => {
|
||||||
it('Load environment', () => {
|
it('Load environment', () => {
|
||||||
const url = import.meta.env.VITE_API_FETCH_URL;
|
const url = API_FETCH_URL;
|
||||||
expect(url).toBe('http://localhost:5000');
|
expect(url).toBe('http://localhost:5000');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -103,9 +103,11 @@ describe.concurrent('Models test suite', () => {
|
||||||
DEFAULT_MAINCONTAINER_PROPS
|
DEFAULT_MAINCONTAINER_PROPS
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const containers = new Map<string, IContainerModel>();
|
||||||
const historyState: IHistoryState = {
|
const historyState: IHistoryState = {
|
||||||
lastAction: 'string',
|
lastAction: 'string',
|
||||||
mainContainer,
|
mainContainer: mainContainer.properties.id,
|
||||||
|
containers,
|
||||||
selectedContainerId: '3',
|
selectedContainerId: '3',
|
||||||
typeCounters: {
|
typeCounters: {
|
||||||
main: 1
|
main: 1
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { API_FETCH_URL, API_SET_CONTAINER_LIST_URL } from '../../../public/svgld-settings';
|
||||||
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
||||||
import { ISetContainerListRequest } from '../../Interfaces/ISetContainerListRequest';
|
import { ISetContainerListRequest } from '../../Interfaces/ISetContainerListRequest';
|
||||||
import { ISetContainerListResponse } from '../../Interfaces/ISetContainerListResponse';
|
import { ISetContainerListResponse } from '../../Interfaces/ISetContainerListResponse';
|
||||||
|
@ -8,7 +9,7 @@ import { GetCircularReplacerKeepDataStructure } from '../../utils/saveload';
|
||||||
* @returns {Configation} The model of the configuration for the application
|
* @returns {Configation} The model of the configuration for the application
|
||||||
*/
|
*/
|
||||||
export async function FetchConfiguration(): Promise<IConfiguration> {
|
export async function FetchConfiguration(): Promise<IConfiguration> {
|
||||||
const url = import.meta.env.VITE_API_FETCH_URL;
|
const url = API_FETCH_URL;
|
||||||
// The test library cannot use the Fetch API
|
// The test library cannot use the Fetch API
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||||
|
@ -34,7 +35,7 @@ export async function FetchConfiguration(): Promise<IConfiguration> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function SetContainerList(request: ISetContainerListRequest): Promise<ISetContainerListResponse> {
|
export async function SetContainerList(request: ISetContainerListRequest): Promise<ISetContainerListResponse> {
|
||||||
const url = import.meta.env.VITE_API_SET_CONTAINER_LIST_URL;
|
const url = API_SET_CONTAINER_LIST_URL;
|
||||||
const dataParsed = JSON.stringify(request, GetCircularReplacerKeepDataStructure());
|
const dataParsed = JSON.stringify(request, GetCircularReplacerKeepDataStructure());
|
||||||
// The test library cannot use the Fetch API
|
// The test library cannot use the Fetch API
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react';
|
import React, { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react';
|
||||||
import { events as EVENTS } from '../../Events/AppEvents';
|
import { events as EVENTS } from '../../Events/AppEvents';
|
||||||
import { MainMenu } from '../MainMenu/MainMenu';
|
import { MainMenu } from '../MainMenu/MainMenu';
|
||||||
import { ContainerModel } from '../../Interfaces/IContainerModel';
|
import { ContainerModel, IContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { Editor } from '../Editor/Editor';
|
import { Editor } from '../Editor/Editor';
|
||||||
import { IEditorState } from '../../Interfaces/IEditorState';
|
import { IEditorState } from '../../Interfaces/IEditorState';
|
||||||
import { LoadState } from './Actions/Load';
|
import { LoadState } from './Actions/Load';
|
||||||
|
@ -81,12 +81,15 @@ export function App(props: IAppProps): JSX.Element {
|
||||||
null,
|
null,
|
||||||
DEFAULT_MAINCONTAINER_PROPS
|
DEFAULT_MAINCONTAINER_PROPS
|
||||||
);
|
);
|
||||||
|
const containers = new Map<string, IContainerModel>();
|
||||||
|
containers.set(defaultMainContainer.properties.id, defaultMainContainer);
|
||||||
|
|
||||||
const [editorState, setEditorState] = useState<IEditorState>({
|
const [editorState, setEditorState] = useState<IEditorState>({
|
||||||
configuration: DEFAULT_CONFIG,
|
configuration: DEFAULT_CONFIG,
|
||||||
history: [{
|
history: [{
|
||||||
lastAction: '',
|
lastAction: '',
|
||||||
mainContainer: defaultMainContainer,
|
mainContainer: defaultMainContainer.properties.id,
|
||||||
|
containers,
|
||||||
selectedContainerId: defaultMainContainer.properties.id,
|
selectedContainerId: defaultMainContainer.properties.id,
|
||||||
typeCounters: {},
|
typeCounters: {},
|
||||||
symbols: new Map(),
|
symbols: new Map(),
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Orientation } from '../../Enums/Orientation';
|
||||||
import { Position } from '../../Enums/Position';
|
import { Position } from '../../Enums/Position';
|
||||||
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { SHOW_SELF_DIMENSIONS, SHOW_BORROWER_DIMENSIONS, SHOW_CHILDREN_DIMENSIONS } from '../../utils/default';
|
import { SHOW_SELF_DIMENSIONS, SHOW_BORROWER_DIMENSIONS, SHOW_CHILDREN_DIMENSIONS } from '../../utils/default';
|
||||||
import { MakeRecursionDFSIterator, Pairwise } from '../../utils/itertools';
|
import { FindContainerById, MakeRecursionDFSIterator, Pairwise } from '../../utils/itertools';
|
||||||
import { TransformX, TransformY } from '../../utils/svg';
|
import { TransformX, TransformY } from '../../utils/svg';
|
||||||
import { RenderDimension } from './Dimension';
|
import { RenderDimension } from './Dimension';
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ const MODULE_STROKE_WIDTH = 1;
|
||||||
|
|
||||||
export function AddDimensions(
|
export function AddDimensions(
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
container: IContainerModel,
|
container: IContainerModel,
|
||||||
dimMapped: number[],
|
dimMapped: number[],
|
||||||
currentTransform: [number, number],
|
currentTransform: [number, number],
|
||||||
|
@ -24,7 +25,8 @@ export function AddDimensions(
|
||||||
container.properties.showSelfDimensions,
|
container.properties.showSelfDimensions,
|
||||||
AddHorizontalSelfDimension,
|
AddHorizontalSelfDimension,
|
||||||
AddVerticalSelfDimension,
|
AddVerticalSelfDimension,
|
||||||
[container,
|
[
|
||||||
|
container,
|
||||||
currentTransform,
|
currentTransform,
|
||||||
scale]
|
scale]
|
||||||
);
|
);
|
||||||
|
@ -37,7 +39,9 @@ export function AddDimensions(
|
||||||
container.properties.showDimensionWithMarks,
|
container.properties.showDimensionWithMarks,
|
||||||
AddHorizontalBorrowerDimension,
|
AddHorizontalBorrowerDimension,
|
||||||
AddVerticalBorrowerDimension,
|
AddVerticalBorrowerDimension,
|
||||||
[container,
|
[
|
||||||
|
containers,
|
||||||
|
container,
|
||||||
depth,
|
depth,
|
||||||
currentTransform,
|
currentTransform,
|
||||||
scale]
|
scale]
|
||||||
|
@ -51,7 +55,9 @@ export function AddDimensions(
|
||||||
container.properties.showChildrenDimensions,
|
container.properties.showChildrenDimensions,
|
||||||
AddHorizontalChildrenDimension,
|
AddHorizontalChildrenDimension,
|
||||||
AddVerticalChildrenDimension,
|
AddVerticalChildrenDimension,
|
||||||
[container,
|
[
|
||||||
|
containers,
|
||||||
|
container,
|
||||||
currentTransform,
|
currentTransform,
|
||||||
scale]
|
scale]
|
||||||
);
|
);
|
||||||
|
@ -94,19 +100,30 @@ function ActionByPosition(
|
||||||
function AddHorizontalChildrenDimension(
|
function AddHorizontalChildrenDimension(
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
yDim: number,
|
yDim: number,
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
container: IContainerModel,
|
container: IContainerModel,
|
||||||
currentTransform: [number, number],
|
currentTransform: [number, number],
|
||||||
scale: number
|
scale: number
|
||||||
): void {
|
): void {
|
||||||
const childrenId = `dim-y${yDim.toFixed(0)}-children-${container.properties.id}`;
|
const childrenId = `dim-y${yDim.toFixed(0)}-children-${container.properties.id}`;
|
||||||
|
|
||||||
const lastChild = container.children[container.children.length - 1];
|
const lastChildId = container.children[container.children.length - 1];
|
||||||
|
const lastChild = FindContainerById(containers, lastChildId);
|
||||||
|
|
||||||
|
if (lastChild === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let xChildrenStart = TransformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.positionReference);
|
let xChildrenStart = TransformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.positionReference);
|
||||||
let xChildrenEnd = TransformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.positionReference);
|
let xChildrenEnd = TransformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.positionReference);
|
||||||
|
|
||||||
// Find the min and max
|
// Find the min and max
|
||||||
for (let i = container.children.length - 2; i >= 0; i--) {
|
for (let i = container.children.length - 2; i >= 0; i--) {
|
||||||
const child = container.children[i];
|
const childId = container.children[i];
|
||||||
|
const child = FindContainerById(containers, childId);
|
||||||
|
|
||||||
|
if (child === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const left = TransformX(child.properties.x, child.properties.width, child.properties.positionReference);
|
const left = TransformX(child.properties.x, child.properties.width, child.properties.positionReference);
|
||||||
if (left < xChildrenStart) {
|
if (left < xChildrenStart) {
|
||||||
xChildrenStart = left;
|
xChildrenStart = left;
|
||||||
|
@ -142,19 +159,32 @@ function AddHorizontalChildrenDimension(
|
||||||
function AddVerticalChildrenDimension(
|
function AddVerticalChildrenDimension(
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
xDim: number,
|
xDim: number,
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
container: IContainerModel,
|
container: IContainerModel,
|
||||||
currentTransform: [number, number],
|
currentTransform: [number, number],
|
||||||
scale: number
|
scale: number
|
||||||
): void {
|
): void {
|
||||||
const childrenId = `dim-x${xDim.toFixed(0)}-children-${container.properties.id}`;
|
const childrenId = `dim-x${xDim.toFixed(0)}-children-${container.properties.id}`;
|
||||||
|
|
||||||
const lastChild = container.children[container.children.length - 1];
|
const lastChildId = container.children[container.children.length - 1];
|
||||||
|
const lastChild = FindContainerById(containers, lastChildId);
|
||||||
|
|
||||||
|
if (lastChild === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let yChildrenStart = TransformY(lastChild.properties.y, lastChild.properties.height, lastChild.properties.positionReference);
|
let yChildrenStart = TransformY(lastChild.properties.y, lastChild.properties.height, lastChild.properties.positionReference);
|
||||||
let yChildrenEnd = TransformY(lastChild.properties.y, lastChild.properties.height, lastChild.properties.positionReference);
|
let yChildrenEnd = TransformY(lastChild.properties.y, lastChild.properties.height, lastChild.properties.positionReference);
|
||||||
|
|
||||||
// Find the min and max
|
// Find the min and max
|
||||||
for (let i = container.children.length - 2; i >= 0; i--) {
|
for (let i = container.children.length - 2; i >= 0; i--) {
|
||||||
const child = container.children[i];
|
const childId = container.children[i];
|
||||||
|
const child = FindContainerById(containers, childId);
|
||||||
|
|
||||||
|
if (child === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const top = TransformY(child.properties.y, child.properties.height, child.properties.positionReference);
|
const top = TransformY(child.properties.y, child.properties.height, child.properties.positionReference);
|
||||||
if (top < yChildrenStart) {
|
if (top < yChildrenStart) {
|
||||||
yChildrenStart = top;
|
yChildrenStart = top;
|
||||||
|
@ -191,12 +221,13 @@ function AddVerticalChildrenDimension(
|
||||||
function AddHorizontalBorrowerDimension(
|
function AddHorizontalBorrowerDimension(
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
yDim: number,
|
yDim: number,
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
container: IContainerModel,
|
container: IContainerModel,
|
||||||
depth: number,
|
depth: number,
|
||||||
currentTransform: [number, number],
|
currentTransform: [number, number],
|
||||||
scale: number
|
scale: number
|
||||||
): void {
|
): void {
|
||||||
const it = MakeRecursionDFSIterator(container, depth, currentTransform);
|
const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform);
|
||||||
const marks = []; // list of vertical lines for the dimension
|
const marks = []; // list of vertical lines for the dimension
|
||||||
for (const {
|
for (const {
|
||||||
container: childContainer, currentTransform: childCurrentTransform
|
container: childContainer, currentTransform: childCurrentTransform
|
||||||
|
@ -243,12 +274,13 @@ function AddHorizontalBorrowerDimension(
|
||||||
function AddVerticalBorrowerDimension(
|
function AddVerticalBorrowerDimension(
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
xDim: number,
|
xDim: number,
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
container: IContainerModel,
|
container: IContainerModel,
|
||||||
depth: number,
|
depth: number,
|
||||||
currentTransform: [number, number],
|
currentTransform: [number, number],
|
||||||
scale: number
|
scale: number
|
||||||
): void {
|
): void {
|
||||||
const it = MakeRecursionDFSIterator(container, depth, currentTransform);
|
const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform);
|
||||||
const marks = []; // list of vertical lines for the dimension
|
const marks = []; // list of vertical lines for the dimension
|
||||||
for (const {
|
for (const {
|
||||||
container: childContainer, currentTransform: childCurrentTransform
|
container: childContainer, currentTransform: childCurrentTransform
|
||||||
|
|
|
@ -69,12 +69,11 @@ export function AddContainers(
|
||||||
const history = GetCurrentHistory(fullHistory, historyCurrentStep);
|
const history = GetCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
const current = history[history.length - 1];
|
const current = history[history.length - 1];
|
||||||
|
|
||||||
// Deep clone the main container for the history
|
const containers = structuredClone(current.containers);
|
||||||
const clone: IContainerModel = structuredClone(current.mainContainer);
|
|
||||||
|
|
||||||
// Find the parent in the clone
|
// Find the parent in the clone
|
||||||
const parentClone: IContainerModel | undefined = FindContainerById(
|
const parentClone: IContainerModel | undefined = FindContainerById(
|
||||||
clone, parentId
|
containers, parentId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (parentClone === null || parentClone === undefined) {
|
if (parentClone === null || parentClone === undefined) {
|
||||||
|
@ -90,14 +89,15 @@ export function AddContainers(
|
||||||
// Iterate over the containers
|
// Iterate over the containers
|
||||||
availableContainers.forEach((availableContainer, typeIndex) => {
|
availableContainers.forEach((availableContainer, typeIndex) => {
|
||||||
// Get the preset properties from the API
|
// Get the preset properties from the API
|
||||||
AddNewContainerToParent(availableContainer, configuration, parentClone, index, typeIndex, newCounters, current.symbols, containerIds);
|
AddNewContainerToParent(availableContainer, configuration, containers, parentClone, index, typeIndex, newCounters, current.symbols, containerIds);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update the state
|
// Update the state
|
||||||
history.push({
|
history.push({
|
||||||
lastAction: `Add [${containerIds.join(', ')}] in ${parentClone.properties.id}`,
|
lastAction: `Add [${containerIds.join(', ')}] in ${parentClone.properties.id}`,
|
||||||
mainContainer: clone,
|
mainContainer: current.mainContainer,
|
||||||
selectedContainerId: parentClone.properties.id,
|
selectedContainerId: parentClone.properties.id,
|
||||||
|
containers,
|
||||||
typeCounters: newCounters,
|
typeCounters: newCounters,
|
||||||
symbols: structuredClone(current.symbols),
|
symbols: structuredClone(current.symbols),
|
||||||
selectedSymbolId: current.selectedSymbolId
|
selectedSymbolId: current.selectedSymbolId
|
||||||
|
@ -109,6 +109,7 @@ export function AddContainers(
|
||||||
function AddNewContainerToParent(
|
function AddNewContainerToParent(
|
||||||
availableContainer: IAvailableContainer,
|
availableContainer: IAvailableContainer,
|
||||||
configuration: IConfiguration,
|
configuration: IConfiguration,
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
parentClone: IContainerModel,
|
parentClone: IContainerModel,
|
||||||
index: number,
|
index: number,
|
||||||
typeIndex: number,
|
typeIndex: number,
|
||||||
|
@ -143,7 +144,7 @@ function AddNewContainerToParent(
|
||||||
({ x, y, width, height } = ApplyMargin(x, y, width, height, left, bottom, top, right));
|
({ x, y, width, height } = ApplyMargin(x, y, width, height, left, bottom, top, right));
|
||||||
|
|
||||||
// Apply an add method (append or insert/replace)
|
// Apply an add method (append or insert/replace)
|
||||||
({ x, y } = ApplyAddMethod(index + typeIndex, containerConfig, parentClone, x, y));
|
({ x, y } = ApplyAddMethod(containers, index + typeIndex, containerConfig, parentClone, x, y));
|
||||||
|
|
||||||
// Set the counter of the object type in order to assign an unique id
|
// Set the counter of the object type in order to assign an unique id
|
||||||
UpdateCounters(newCounters, type);
|
UpdateCounters(newCounters, type);
|
||||||
|
@ -170,22 +171,25 @@ function AddNewContainerToParent(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Register the container in the hashmap
|
||||||
|
containers.set(newContainer.properties.id, newContainer);
|
||||||
|
|
||||||
// Add it to the parent
|
// Add it to the parent
|
||||||
if (index === parentClone.children.length) {
|
if (index === parentClone.children.length) {
|
||||||
parentClone.children.push(newContainer);
|
parentClone.children.push(newContainer.properties.id);
|
||||||
} else {
|
} else {
|
||||||
parentClone.children.splice(index, 0, newContainer);
|
parentClone.children.splice(index, 0, newContainer.properties.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort the parent children by x
|
// Sort the parent children by x
|
||||||
SortChildren(parentClone);
|
SortChildren(containers, parentClone);
|
||||||
|
|
||||||
/// Handle behaviors here ///
|
/// Handle behaviors here ///
|
||||||
// Apply the behaviors (flex, rigid, anchor)
|
// Apply the behaviors (flex, rigid, anchor)
|
||||||
ApplyBehaviors(newContainer, symbols);
|
ApplyBehaviors(containers, newContainer, symbols);
|
||||||
|
|
||||||
// Then, apply the behaviors on its siblings (mostly for flex)
|
// Then, apply the behaviors on its siblings (mostly for flex)
|
||||||
ApplyBehaviorsOnSiblingsChildren(newContainer, symbols);
|
ApplyBehaviorsOnSiblingsChildren(containers, newContainer, symbols);
|
||||||
|
|
||||||
// Initialize default children of the container
|
// Initialize default children of the container
|
||||||
if (initChilds) {
|
if (initChilds) {
|
||||||
|
@ -194,6 +198,7 @@ function AddNewContainerToParent(
|
||||||
newContainer,
|
newContainer,
|
||||||
configuration,
|
configuration,
|
||||||
containerConfig,
|
containerConfig,
|
||||||
|
containers,
|
||||||
newCounters,
|
newCounters,
|
||||||
symbols
|
symbols
|
||||||
);
|
);
|
||||||
|
@ -201,6 +206,7 @@ function AddNewContainerToParent(
|
||||||
InitializeChildrenWithPattern(
|
InitializeChildrenWithPattern(
|
||||||
newContainer,
|
newContainer,
|
||||||
configuration,
|
configuration,
|
||||||
|
containers,
|
||||||
containerConfig,
|
containerConfig,
|
||||||
newCounters,
|
newCounters,
|
||||||
symbols
|
symbols
|
||||||
|
@ -258,6 +264,7 @@ function InitializeDefaultChild(
|
||||||
newContainer: ContainerModel,
|
newContainer: ContainerModel,
|
||||||
configuration: IConfiguration,
|
configuration: IConfiguration,
|
||||||
containerConfig: IAvailableContainer,
|
containerConfig: IAvailableContainer,
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
newCounters: Record<string, number>,
|
newCounters: Record<string, number>,
|
||||||
symbols: Map<string, ISymbolModel>
|
symbols: Map<string, ISymbolModel>
|
||||||
): void {
|
): void {
|
||||||
|
@ -276,6 +283,7 @@ function InitializeDefaultChild(
|
||||||
AddNewContainerToParent(
|
AddNewContainerToParent(
|
||||||
currentConfig,
|
currentConfig,
|
||||||
configuration,
|
configuration,
|
||||||
|
containers,
|
||||||
parent,
|
parent,
|
||||||
0, 0,
|
0, 0,
|
||||||
newCounters,
|
newCounters,
|
||||||
|
@ -286,6 +294,7 @@ function InitializeDefaultChild(
|
||||||
function InitializeChildrenWithPattern(
|
function InitializeChildrenWithPattern(
|
||||||
newContainer: ContainerModel,
|
newContainer: ContainerModel,
|
||||||
configuration: IConfiguration,
|
configuration: IConfiguration,
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
containerConfig: IAvailableContainer,
|
containerConfig: IAvailableContainer,
|
||||||
newCounters: Record<string, number>,
|
newCounters: Record<string, number>,
|
||||||
symbols: Map<string, ISymbolModel>
|
symbols: Map<string, ISymbolModel>
|
||||||
|
@ -307,6 +316,7 @@ function InitializeChildrenWithPattern(
|
||||||
AddNewContainerToParent(
|
AddNewContainerToParent(
|
||||||
container,
|
container,
|
||||||
configuration,
|
configuration,
|
||||||
|
containers,
|
||||||
newContainer,
|
newContainer,
|
||||||
0, 0,
|
0, 0,
|
||||||
newCounters,
|
newCounters,
|
||||||
|
@ -334,6 +344,7 @@ function InitializeChildrenWithPattern(
|
||||||
AddNewContainerToParent(
|
AddNewContainerToParent(
|
||||||
containerConfig,
|
containerConfig,
|
||||||
configuration,
|
configuration,
|
||||||
|
containers,
|
||||||
node.parent,
|
node.parent,
|
||||||
0, 0,
|
0, 0,
|
||||||
newCounters,
|
newCounters,
|
||||||
|
@ -350,9 +361,10 @@ function InitializeChildrenWithPattern(
|
||||||
console.warn(`[InitializeChildrenFromPattern] IAvailableContainer from pattern was not found in the configuration: ${pattern.wrapper}.
|
console.warn(`[InitializeChildrenFromPattern] IAvailableContainer from pattern was not found in the configuration: ${pattern.wrapper}.
|
||||||
Process will ignore the container.`);
|
Process will ignore the container.`);
|
||||||
} else {
|
} else {
|
||||||
parent = AddNewContainerToParent(
|
const newChildContainer = AddNewContainerToParent(
|
||||||
container,
|
container,
|
||||||
configuration,
|
configuration,
|
||||||
|
containers,
|
||||||
parent,
|
parent,
|
||||||
0, 0,
|
0, 0,
|
||||||
newCounters,
|
newCounters,
|
||||||
|
@ -360,6 +372,9 @@ function InitializeChildrenWithPattern(
|
||||||
undefined,
|
undefined,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// iterate
|
||||||
|
parent = newChildContainer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,6 +413,7 @@ interface Node {
|
||||||
* @returns New offset
|
* @returns New offset
|
||||||
*/
|
*/
|
||||||
function ApplyAddMethod(
|
function ApplyAddMethod(
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
index: number,
|
index: number,
|
||||||
containerConfig: IAvailableContainer,
|
containerConfig: IAvailableContainer,
|
||||||
parent: IContainerModel,
|
parent: IContainerModel,
|
||||||
|
@ -410,8 +426,8 @@ function ApplyAddMethod(
|
||||||
containerConfig.AddMethod === AddMethod.Append
|
containerConfig.AddMethod === AddMethod.Append
|
||||||
)) {
|
)) {
|
||||||
// Append method (default)
|
// Append method (default)
|
||||||
const lastChild: IContainerModel | undefined = parent.children
|
const lastChildId: string = parent.children[index - 1];
|
||||||
.at(index - 1);
|
const lastChild = FindContainerById(containers, lastChildId);
|
||||||
|
|
||||||
if (lastChild !== undefined) {
|
if (lastChild !== undefined) {
|
||||||
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
|
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
|
||||||
|
|
|
@ -24,7 +24,8 @@ export function SelectContainer(
|
||||||
|
|
||||||
history.push({
|
history.push({
|
||||||
lastAction: `Select ${containerId}`,
|
lastAction: `Select ${containerId}`,
|
||||||
mainContainer: structuredClone(current.mainContainer),
|
mainContainer: current.mainContainer,
|
||||||
|
containers: structuredClone(current.containers),
|
||||||
selectedContainerId: containerId,
|
selectedContainerId: containerId,
|
||||||
typeCounters: Object.assign({}, current.typeCounters),
|
typeCounters: Object.assign({}, current.typeCounters),
|
||||||
symbols: structuredClone(current.symbols),
|
symbols: structuredClone(current.symbols),
|
||||||
|
@ -48,8 +49,9 @@ export function DeleteContainer(
|
||||||
const history = GetCurrentHistory(fullHistory, historyCurrentStep);
|
const history = GetCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
const current = history[history.length - 1];
|
const current = history[history.length - 1];
|
||||||
|
|
||||||
const mainContainerClone: IContainerModel = structuredClone(current.mainContainer);
|
const containers = structuredClone(current.containers);
|
||||||
const container = FindContainerById(mainContainerClone, containerId);
|
const mainContainerClone: IContainerModel | undefined = FindContainerById(containers, current.mainContainer);
|
||||||
|
const container = FindContainerById(containers, containerId);
|
||||||
|
|
||||||
if (container === undefined) {
|
if (container === undefined) {
|
||||||
throw new Error(`[DeleteContainer] Tried to delete a container that is not present in the main container: ${containerId}`);
|
throw new Error(`[DeleteContainer] Tried to delete a container that is not present in the main container: ${containerId}`);
|
||||||
|
@ -71,21 +73,22 @@ export function DeleteContainer(
|
||||||
}
|
}
|
||||||
|
|
||||||
const newSymbols = structuredClone(current.symbols);
|
const newSymbols = structuredClone(current.symbols);
|
||||||
UnlinkContainerFromSymbols(newSymbols, container);
|
UnlinkContainerFromSymbols(containers, newSymbols, container);
|
||||||
|
|
||||||
const index = container.parent.children.indexOf(container);
|
const index = container.parent.children.indexOf(container.properties.id);
|
||||||
if (index > -1) {
|
const success = containers.delete(container.properties.id);
|
||||||
|
if (index > -1 && success) {
|
||||||
container.parent.children.splice(index, 1);
|
container.parent.children.splice(index, 1);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('[DeleteContainer] Could not find container among parent\'s children');
|
throw new Error('[DeleteContainer] Could not find container among parent\'s children');
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplyBehaviorsOnSiblings(container, current.symbols);
|
ApplyBehaviorsOnSiblings(containers, container, current.symbols);
|
||||||
|
|
||||||
// Select the previous container
|
// Select the previous container
|
||||||
// or select the one above
|
// or select the one above
|
||||||
const selectedContainerId = GetSelectedContainerOnDelete(
|
const selectedContainerId = GetSelectedContainerOnDelete(
|
||||||
mainContainerClone,
|
containers,
|
||||||
current.selectedContainerId,
|
current.selectedContainerId,
|
||||||
container.parent,
|
container.parent,
|
||||||
index
|
index
|
||||||
|
@ -93,7 +96,8 @@ export function DeleteContainer(
|
||||||
|
|
||||||
history.push({
|
history.push({
|
||||||
lastAction: `Delete ${containerId}`,
|
lastAction: `Delete ${containerId}`,
|
||||||
mainContainer: mainContainerClone,
|
mainContainer: current.mainContainer,
|
||||||
|
containers,
|
||||||
selectedContainerId,
|
selectedContainerId,
|
||||||
typeCounters: Object.assign({}, current.typeCounters),
|
typeCounters: Object.assign({}, current.typeCounters),
|
||||||
symbols: newSymbols,
|
symbols: newSymbols,
|
||||||
|
@ -115,16 +119,15 @@ export function DeleteContainer(
|
||||||
* @returns {IContainerModel} Next selected container
|
* @returns {IContainerModel} Next selected container
|
||||||
*/
|
*/
|
||||||
function GetSelectedContainerOnDelete(
|
function GetSelectedContainerOnDelete(
|
||||||
mainContainerClone: IContainerModel,
|
containers: Map<string, IContainerModel>,
|
||||||
selectedContainerId: string,
|
selectedContainerId: string,
|
||||||
parent: IContainerModel,
|
parent: IContainerModel,
|
||||||
index: number
|
index: number
|
||||||
): string {
|
): string {
|
||||||
const newSelectedContainer = FindContainerById(mainContainerClone, selectedContainerId) ??
|
const newSelectedContainerId = FindContainerById(containers, selectedContainerId)?.properties.id ??
|
||||||
parent.children.at(index) ??
|
parent.children.at(index) ??
|
||||||
parent.children.at(index - 1) ??
|
parent.children.at(index - 1) ??
|
||||||
parent;
|
parent.properties.id;
|
||||||
const newSelectedContainerId = newSelectedContainer.properties.id;
|
|
||||||
return newSelectedContainerId;
|
return newSelectedContainerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,8 +137,12 @@ function GetSelectedContainerOnDelete(
|
||||||
* @param symbols Symbols to update
|
* @param symbols Symbols to update
|
||||||
* @param container Container to unlink
|
* @param container Container to unlink
|
||||||
*/
|
*/
|
||||||
function UnlinkContainerFromSymbols(symbols: Map<string, ISymbolModel>, container: IContainerModel): void {
|
function UnlinkContainerFromSymbols(
|
||||||
const it = MakeDFSIterator(container);
|
containers: Map<string, IContainerModel>,
|
||||||
|
symbols: Map<string, ISymbolModel>,
|
||||||
|
container: IContainerModel
|
||||||
|
): void {
|
||||||
|
const it = MakeDFSIterator(container, containers);
|
||||||
for (const child of it) {
|
for (const child of it) {
|
||||||
const symbol = symbols.get(child.properties.linkedSymbolId);
|
const symbol = symbols.get(child.properties.linkedSymbolId);
|
||||||
if (symbol === undefined) {
|
if (symbol === undefined) {
|
||||||
|
@ -167,18 +174,19 @@ export function OnPropertyChange(
|
||||||
throw new Error('[OnPropertyChange] Property was changed before selecting a Container');
|
throw new Error('[OnPropertyChange] Property was changed before selecting a Container');
|
||||||
}
|
}
|
||||||
|
|
||||||
const mainContainerClone: IContainerModel = structuredClone(current.mainContainer);
|
const containers = structuredClone(current.containers);
|
||||||
const container: ContainerModel | undefined = FindContainerById(mainContainerClone, selected.properties.id);
|
const container: ContainerModel | undefined = FindContainerById(containers, selected.properties.id);
|
||||||
|
|
||||||
if (container === null || container === undefined) {
|
if (container === null || container === undefined) {
|
||||||
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
||||||
}
|
}
|
||||||
|
|
||||||
SetContainer(container, key, value, type, current.symbols);
|
SetContainer(containers, container, key, value, type, current.symbols);
|
||||||
|
|
||||||
history.push({
|
history.push({
|
||||||
lastAction: `Change ${key} of ${container.properties.id}`,
|
lastAction: `Change ${key} of ${container.properties.id}`,
|
||||||
mainContainer: mainContainerClone,
|
mainContainer: current.mainContainer,
|
||||||
|
containers,
|
||||||
selectedContainerId: container.properties.id,
|
selectedContainerId: container.properties.id,
|
||||||
typeCounters: Object.assign({}, current.typeCounters),
|
typeCounters: Object.assign({}, current.typeCounters),
|
||||||
symbols: structuredClone(current.symbols),
|
symbols: structuredClone(current.symbols),
|
||||||
|
@ -189,20 +197,30 @@ export function OnPropertyChange(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sort the parent children by x
|
* Sort the parent children by x
|
||||||
* @param parentClone The clone used for the sort
|
* @param parent The clone used for the sort
|
||||||
* @returns void
|
* @returns void
|
||||||
*/
|
*/
|
||||||
export function SortChildren(parentClone: IContainerModel | null | undefined): void {
|
export function SortChildren(
|
||||||
if (parentClone === null || parentClone === undefined) {
|
containers: Map<string, IContainerModel>,
|
||||||
|
parent: IContainerModel | null | undefined
|
||||||
|
): void {
|
||||||
|
if (parent === null || parent === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isHorizontal = parentClone.properties.orientation === Orientation.Horizontal;
|
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
|
||||||
const children = parentClone.children;
|
const children = parent.children;
|
||||||
|
|
||||||
if (!isHorizontal) {
|
if (!isHorizontal) {
|
||||||
parentClone.children.sort(
|
parent.children.sort(
|
||||||
(a, b) => {
|
(aId, bId) => {
|
||||||
|
const a = FindContainerById(containers, aId);
|
||||||
|
const b = FindContainerById(containers, bId);
|
||||||
|
|
||||||
|
if (a === undefined || b === undefined) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const yA = TransformY(a.properties.y, a.properties.height, a.properties.positionReference);
|
const yA = TransformY(a.properties.y, a.properties.height, a.properties.positionReference);
|
||||||
const yB = TransformY(b.properties.y, b.properties.height, b.properties.positionReference);
|
const yB = TransformY(b.properties.y, b.properties.height, b.properties.positionReference);
|
||||||
if (yA < yB) {
|
if (yA < yB) {
|
||||||
|
@ -212,16 +230,23 @@ export function SortChildren(parentClone: IContainerModel | null | undefined): v
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// xA = xB
|
// xA = xB
|
||||||
const indexA = children.indexOf(a);
|
const indexA = children.indexOf(aId);
|
||||||
const indexB = children.indexOf(b);
|
const indexB = children.indexOf(bId);
|
||||||
return indexA - indexB;
|
return indexA - indexB;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
parentClone.children.sort(
|
parent.children.sort(
|
||||||
(a, b) => {
|
(aId, bId) => {
|
||||||
|
const a = FindContainerById(containers, aId);
|
||||||
|
const b = FindContainerById(containers, bId);
|
||||||
|
|
||||||
|
if (a === undefined || b === undefined) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const xA = TransformX(a.properties.x, a.properties.width, a.properties.positionReference);
|
const xA = TransformX(a.properties.x, a.properties.width, a.properties.positionReference);
|
||||||
const xB = TransformX(b.properties.x, b.properties.width, b.properties.positionReference);
|
const xB = TransformX(b.properties.x, b.properties.width, b.properties.positionReference);
|
||||||
if (xA < xB) {
|
if (xA < xB) {
|
||||||
|
@ -231,8 +256,8 @@ export function SortChildren(parentClone: IContainerModel | null | undefined): v
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// xA = xB
|
// xA = xB
|
||||||
const indexA = children.indexOf(a);
|
const indexA = children.indexOf(aId);
|
||||||
const indexB = children.indexOf(b);
|
const indexB = children.indexOf(bId);
|
||||||
return indexA - indexB;
|
return indexA - indexB;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -247,6 +272,7 @@ export function SortChildren(parentClone: IContainerModel | null | undefined): v
|
||||||
* @param symbols Current list of symbols
|
* @param symbols Current list of symbols
|
||||||
*/
|
*/
|
||||||
function SetContainer(
|
function SetContainer(
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
container: ContainerModel,
|
container: ContainerModel,
|
||||||
key: string, value: string | number | boolean | number[],
|
key: string, value: string | number | boolean | number[],
|
||||||
type: PropertyType,
|
type: PropertyType,
|
||||||
|
@ -267,13 +293,13 @@ function SetContainer(
|
||||||
);
|
);
|
||||||
|
|
||||||
// sort the children list by their position
|
// sort the children list by their position
|
||||||
SortChildren(container.parent);
|
SortChildren(containers, container.parent);
|
||||||
|
|
||||||
// Apply special behaviors: rigid, flex, symbol, anchor
|
// Apply special behaviors: rigid, flex, symbol, anchor
|
||||||
ApplyBehaviors(container, symbols);
|
ApplyBehaviors(containers, container, symbols);
|
||||||
|
|
||||||
// Apply special behaviors on siblings
|
// Apply special behaviors on siblings
|
||||||
ApplyBehaviorsOnSiblingsChildren(container, symbols);
|
ApplyBehaviorsOnSiblingsChildren(containers, container, symbols);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,7 +21,7 @@ export function GetAction(
|
||||||
): (target: HTMLElement) => void {
|
): (target: HTMLElement) => void {
|
||||||
return (target: HTMLElement) => {
|
return (target: HTMLElement) => {
|
||||||
const id = target.id;
|
const id = target.id;
|
||||||
const container = FindContainerById(currentState.mainContainer, id);
|
const container = FindContainerById(currentState.containers, id);
|
||||||
|
|
||||||
if (container === undefined) {
|
if (container === undefined) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
|
@ -33,7 +33,7 @@ export function GetAction(
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
const { prev, next } = GetPreviousAndNextSiblings(container);
|
const { prev, next } = GetPreviousAndNextSiblings(currentState.containers, container);
|
||||||
|
|
||||||
const request: ISetContainerListRequest = {
|
const request: ISetContainerListRequest = {
|
||||||
Container: container,
|
Container: container,
|
||||||
|
@ -59,18 +59,18 @@ export function GetAction(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function GetPreviousAndNextSiblings(container: IContainerModel): { prev: IContainerModel | undefined, next: IContainerModel | undefined } {
|
function GetPreviousAndNextSiblings(containers: Map<string, IContainerModel>, container: IContainerModel): { prev: IContainerModel | undefined, next: IContainerModel | undefined } {
|
||||||
let prev;
|
let prev;
|
||||||
let next;
|
let next;
|
||||||
if (container.parent !== undefined &&
|
if (container.parent !== undefined &&
|
||||||
container.parent !== null &&
|
container.parent !== null &&
|
||||||
container.parent.children.length > 1) {
|
container.parent.children.length > 1) {
|
||||||
const index = container.parent.children.indexOf(container);
|
const index = container.parent.children.indexOf(container.properties.id);
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
prev = container.parent.children[index - 1];
|
prev = FindContainerById(containers, container.parent.children[index - 1]);
|
||||||
}
|
}
|
||||||
if (index < container.parent.children.length - 1) {
|
if (index < container.parent.children.length - 1) {
|
||||||
next = container.parent.children[index + 1];
|
next = FindContainerById(containers, container.parent.children[index + 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { prev, next };
|
return { prev, next };
|
||||||
|
@ -144,7 +144,7 @@ function HandleReplace(
|
||||||
throw new Error('[ReplaceContainer] Cannot replace a container that does not exists');
|
throw new Error('[ReplaceContainer] Cannot replace a container that does not exists');
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = selectedContainer.parent.children.indexOf(selectedContainer);
|
const index = selectedContainer.parent.children.indexOf(selectedContainer.properties.id);
|
||||||
|
|
||||||
const newHistoryAfterDelete = DeleteContainer(
|
const newHistoryAfterDelete = DeleteContainer(
|
||||||
selectedContainer.properties.id,
|
selectedContainer.properties.id,
|
||||||
|
|
|
@ -36,6 +36,7 @@ export function AddSymbol(
|
||||||
history.push({
|
history.push({
|
||||||
lastAction: `Add ${name}`,
|
lastAction: `Add ${name}`,
|
||||||
mainContainer: structuredClone(current.mainContainer),
|
mainContainer: structuredClone(current.mainContainer),
|
||||||
|
containers: structuredClone(current.containers),
|
||||||
selectedContainerId: current.selectedContainerId,
|
selectedContainerId: current.selectedContainerId,
|
||||||
typeCounters: newCounters,
|
typeCounters: newCounters,
|
||||||
symbols: newSymbols,
|
symbols: newSymbols,
|
||||||
|
@ -55,6 +56,7 @@ export function SelectSymbol(
|
||||||
history.push({
|
history.push({
|
||||||
lastAction: `Select ${symbolId}`,
|
lastAction: `Select ${symbolId}`,
|
||||||
mainContainer: structuredClone(current.mainContainer),
|
mainContainer: structuredClone(current.mainContainer),
|
||||||
|
containers: structuredClone(current.containers),
|
||||||
selectedContainerId: current.selectedContainerId,
|
selectedContainerId: current.selectedContainerId,
|
||||||
typeCounters: structuredClone(current.typeCounters),
|
typeCounters: structuredClone(current.typeCounters),
|
||||||
symbols: structuredClone(current.symbols),
|
symbols: structuredClone(current.symbols),
|
||||||
|
@ -78,15 +80,15 @@ export function DeleteSymbol(
|
||||||
throw new Error(`[DeleteSymbol] Could not find symbol in the current state!: ${symbolId}`);
|
throw new Error(`[DeleteSymbol] Could not find symbol in the current state!: ${symbolId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newMainContainer = structuredClone(current.mainContainer);
|
const containers = structuredClone(current.containers);
|
||||||
|
UnlinkSymbolFromContainers(containers, symbol);
|
||||||
UnlinkSymbolFromContainers(symbol, newMainContainer);
|
|
||||||
|
|
||||||
newSymbols.delete(symbolId);
|
newSymbols.delete(symbolId);
|
||||||
|
|
||||||
history.push({
|
history.push({
|
||||||
lastAction: `Select ${symbolId}`,
|
lastAction: `Select ${symbolId}`,
|
||||||
mainContainer: newMainContainer,
|
mainContainer: current.mainContainer,
|
||||||
|
containers,
|
||||||
selectedContainerId: current.selectedContainerId,
|
selectedContainerId: current.selectedContainerId,
|
||||||
typeCounters: structuredClone(current.typeCounters),
|
typeCounters: structuredClone(current.typeCounters),
|
||||||
symbols: newSymbols,
|
symbols: newSymbols,
|
||||||
|
@ -100,9 +102,9 @@ export function DeleteSymbol(
|
||||||
* @param symbol Symbol to remove
|
* @param symbol Symbol to remove
|
||||||
* @param root Container and its children to remove a symbol from
|
* @param root Container and its children to remove a symbol from
|
||||||
*/
|
*/
|
||||||
function UnlinkSymbolFromContainers(symbol: ISymbolModel, root: IContainerModel): void {
|
function UnlinkSymbolFromContainers(containers: Map<string, IContainerModel>, symbol: ISymbolModel): void {
|
||||||
symbol.linkedContainers.forEach((containerId) => {
|
symbol.linkedContainers.forEach((containerId) => {
|
||||||
const container = FindContainerById(root, containerId);
|
const container = FindContainerById(containers, containerId);
|
||||||
|
|
||||||
if (container === undefined) {
|
if (container === undefined) {
|
||||||
return;
|
return;
|
||||||
|
@ -140,22 +142,23 @@ export function OnPropertyChange(
|
||||||
|
|
||||||
(symbol as any)[key] = value;
|
(symbol as any)[key] = value;
|
||||||
|
|
||||||
const newMainContainer = structuredClone(current.mainContainer);
|
const containers = structuredClone(current.containers);
|
||||||
symbol.linkedContainers.forEach((containerId) => {
|
symbol.linkedContainers.forEach((containerId) => {
|
||||||
const container = FindContainerById(newMainContainer, containerId);
|
const container = FindContainerById(containers, containerId);
|
||||||
|
|
||||||
if (container === undefined) {
|
if (container === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplyBehaviors(container, newSymbols);
|
ApplyBehaviors(containers, container, newSymbols);
|
||||||
|
|
||||||
ApplyBehaviorsOnSiblingsChildren(container, newSymbols);
|
ApplyBehaviorsOnSiblingsChildren(containers, container, newSymbols);
|
||||||
});
|
});
|
||||||
|
|
||||||
history.push({
|
history.push({
|
||||||
lastAction: `Change ${key} of ${symbol.id}`,
|
lastAction: `Change ${key} of ${symbol.id}`,
|
||||||
mainContainer: newMainContainer,
|
mainContainer: current.mainContainer,
|
||||||
|
containers,
|
||||||
selectedContainerId: current.selectedContainerId,
|
selectedContainerId: current.selectedContainerId,
|
||||||
typeCounters: Object.assign({}, current.typeCounters),
|
typeCounters: Object.assign({}, current.typeCounters),
|
||||||
symbols: newSymbols,
|
symbols: newSymbols,
|
||||||
|
|
|
@ -16,16 +16,28 @@
|
||||||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { Orientation } from '../../../Enums/Orientation';
|
import { Orientation } from '../../../Enums/Orientation';
|
||||||
import { ConstraintBodyInsideUnallocatedWidth } from './RigidBodyBehaviors';
|
import { ConstraintBodyInsideUnallocatedWidth } from './RigidBodyBehaviors';
|
||||||
|
import { FindContainerById } from '../../../utils/itertools';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Impose the container position to its siblings
|
* Impose the container position to its siblings
|
||||||
* Apply the following modification to the overlapping rigid body container :
|
* Apply the following modification to the overlapping rigid body container :
|
||||||
* @param container Container to impose its position
|
* @param container Container to impose its position
|
||||||
*/
|
*/
|
||||||
export function ApplyAnchor(container: IContainerModel, parent: IContainerModel): IContainerModel {
|
export function ApplyAnchor(containers: Map<string, IContainerModel>, container: IContainerModel, parent: IContainerModel): IContainerModel {
|
||||||
const rigidBodies = parent.children.filter(
|
const rigidBodies: IContainerModel[] = [];
|
||||||
child => !child.properties.isAnchor
|
parent.children.forEach(
|
||||||
);
|
childId => {
|
||||||
|
const child = FindContainerById(containers, childId);
|
||||||
|
|
||||||
|
if (child === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child.properties.isAnchor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rigidBodies.push(child);
|
||||||
|
});
|
||||||
|
|
||||||
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
|
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
|
||||||
const overlappingContainers = isHorizontal
|
const overlappingContainers = isHorizontal
|
||||||
|
@ -33,7 +45,7 @@ export function ApplyAnchor(container: IContainerModel, parent: IContainerModel)
|
||||||
: GetVerticallyOverlappingContainers(container, rigidBodies);
|
: GetVerticallyOverlappingContainers(container, rigidBodies);
|
||||||
|
|
||||||
for (const overlappingContainer of overlappingContainers) {
|
for (const overlappingContainer of overlappingContainers) {
|
||||||
ConstraintBodyInsideUnallocatedWidth(overlappingContainer);
|
ConstraintBodyInsideUnallocatedWidth(containers, overlappingContainer);
|
||||||
}
|
}
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
|
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
|
||||||
import { APPLY_BEHAVIORS_ON_CHILDREN, ENABLE_RIGID, ENABLE_SWAP } from '../../../utils/default';
|
import { APPLY_BEHAVIORS_ON_CHILDREN, ENABLE_RIGID, ENABLE_SWAP } from '../../../utils/default';
|
||||||
|
import { FindContainerById, MakeChildrenIterator } from '../../../utils/itertools';
|
||||||
import { ApplyAnchor, GetOverlappingContainers } from './AnchorBehaviors';
|
import { ApplyAnchor, GetOverlappingContainers } from './AnchorBehaviors';
|
||||||
import { Flex } from './FlexBehaviors';
|
import { Flex } from './FlexBehaviors';
|
||||||
import { ApplyRigidBody } from './RigidBodyBehaviors';
|
import { ApplyRigidBody } from './RigidBodyBehaviors';
|
||||||
|
@ -13,7 +14,7 @@ import { ApplySymbol } from './SymbolBehaviors';
|
||||||
* @param container Container to recalculate its positions
|
* @param container Container to recalculate its positions
|
||||||
* @returns Updated container
|
* @returns Updated container
|
||||||
*/
|
*/
|
||||||
export function ApplyBehaviors(container: IContainerModel, symbols: Map<string, ISymbolModel>): IContainerModel {
|
export function ApplyBehaviors(containers: Map<string, IContainerModel>, container: IContainerModel, symbols: Map<string, ISymbolModel>): IContainerModel {
|
||||||
try {
|
try {
|
||||||
const symbol = symbols.get(container.properties.linkedSymbolId);
|
const symbol = symbols.get(container.properties.linkedSymbolId);
|
||||||
if (container.properties.linkedSymbolId !== '' && symbol !== undefined) {
|
if (container.properties.linkedSymbolId !== '' && symbol !== undefined) {
|
||||||
|
@ -24,24 +25,24 @@ export function ApplyBehaviors(container: IContainerModel, symbols: Map<string,
|
||||||
const parent = container.parent;
|
const parent = container.parent;
|
||||||
|
|
||||||
if (container.properties.isAnchor) {
|
if (container.properties.isAnchor) {
|
||||||
ApplyAnchor(container, parent);
|
ApplyAnchor(containers, container, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ENABLE_SWAP) {
|
if (ENABLE_SWAP) {
|
||||||
ApplySwap(container, parent);
|
ApplySwap(containers, container, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
Flex(container, parent);
|
Flex(containers, container, parent);
|
||||||
|
|
||||||
if (ENABLE_RIGID) {
|
if (ENABLE_RIGID) {
|
||||||
ApplyRigidBody(container, parent);
|
ApplyRigidBody(containers, container, parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (APPLY_BEHAVIORS_ON_CHILDREN) {
|
if (APPLY_BEHAVIORS_ON_CHILDREN) {
|
||||||
// Apply DFS by recursion
|
// Apply DFS by recursion
|
||||||
for (const child of container.children) {
|
for (const child of MakeChildrenIterator(containers, container.children)) {
|
||||||
ApplyBehaviors(child, symbols);
|
ApplyBehaviors(containers, child, symbols);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -64,23 +65,32 @@ export function ApplyBehaviors(container: IContainerModel, symbols: Map<string,
|
||||||
* @param symbols
|
* @param symbols
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function ApplyBehaviorsOnSiblingsChildren(newContainer: IContainerModel, symbols: Map<string, ISymbolModel>): void {
|
export function ApplyBehaviorsOnSiblingsChildren(
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
|
newContainer: IContainerModel,
|
||||||
|
symbols: Map<string, ISymbolModel>): void {
|
||||||
if (newContainer.parent === null || newContainer.parent === undefined) {
|
if (newContainer.parent === null || newContainer.parent === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
newContainer.parent.children
|
newContainer.parent.children
|
||||||
.forEach((container: IContainerModel) => {
|
.forEach((containerId: string) => {
|
||||||
if (container.parent != null) {
|
const container = FindContainerById(containers, containerId);
|
||||||
UpdateWarning(container, container.parent);
|
|
||||||
|
if (container === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (container.parent !== null) {
|
||||||
|
UpdateWarning(containers, container, container.parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container === newContainer) {
|
if (container === newContainer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const child of container.children) {
|
for (const child of MakeChildrenIterator(containers, container.children)) {
|
||||||
ApplyBehaviors(child, symbols);
|
ApplyBehaviors(containers, child, symbols);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -91,30 +101,47 @@ export function ApplyBehaviorsOnSiblingsChildren(newContainer: IContainerModel,
|
||||||
* @param symbols
|
* @param symbols
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function ApplyBehaviorsOnSiblings(newContainer: IContainerModel, symbols: Map<string, ISymbolModel>): void {
|
export function ApplyBehaviorsOnSiblings(containers: Map<string, IContainerModel>, newContainer: IContainerModel, symbols: Map<string, ISymbolModel>): void {
|
||||||
if (newContainer.parent === null || newContainer.parent === undefined) {
|
if (newContainer.parent === null || newContainer.parent === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
newContainer.parent.children
|
newContainer.parent.children
|
||||||
.forEach((container: IContainerModel) => {
|
.forEach((containerId: string) => {
|
||||||
ApplyBehaviors(container, symbols);
|
const container = FindContainerById(containers, containerId);
|
||||||
|
|
||||||
|
if (container === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyBehaviors(containers, container, symbols);
|
||||||
|
|
||||||
if (container.parent != null) {
|
if (container.parent != null) {
|
||||||
UpdateWarning(container, container.parent);
|
UpdateWarning(containers, container, container.parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container === newContainer) {
|
if (container === newContainer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const child of container.children) {
|
for (const child of MakeChildrenIterator(containers, container.children)) {
|
||||||
ApplyBehaviors(child, symbols);
|
ApplyBehaviors(containers, child, symbols);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function UpdateWarning(container: IContainerModel, parent: IContainerModel): void {
|
function UpdateWarning(containers: Map<string, IContainerModel>, container: IContainerModel, parent: IContainerModel): void {
|
||||||
const overlappingContainers = GetOverlappingContainers(container, parent.children);
|
const targetContainers: IContainerModel[] = [];
|
||||||
|
|
||||||
|
parent.children.forEach((child) => {
|
||||||
|
const targetContainer = FindContainerById(containers, child);
|
||||||
|
|
||||||
|
if (targetContainer === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetContainers.push(targetContainer);
|
||||||
|
});
|
||||||
|
const overlappingContainers = GetOverlappingContainers(container, targetContainers);
|
||||||
if (overlappingContainers.length > 0) {
|
if (overlappingContainers.length > 0) {
|
||||||
container.properties.warning = `There are overlapping containers: ${overlappingContainers.map(c => c.properties.id).join(' ')}`;
|
container.properties.warning = `There are overlapping containers: ${overlappingContainers.map(c => c.properties.id).join(' ')}`;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { Orientation } from '../../../Enums/Orientation';
|
import { Orientation } from '../../../Enums/Orientation';
|
||||||
import { Simplex } from '../../../utils/simplex';
|
import { Simplex } from '../../../utils/simplex';
|
||||||
import { ApplyWidthMargin, ApplyXMargin } from '../../../utils/svg';
|
import { ApplyWidthMargin, ApplyXMargin } from '../../../utils/svg';
|
||||||
|
import { MakeChildrenIterator } from '../../../utils/itertools';
|
||||||
|
|
||||||
interface IFlexibleGroup {
|
interface IFlexibleGroup {
|
||||||
group: IContainerModel[]
|
group: IContainerModel[]
|
||||||
|
@ -13,13 +14,13 @@ interface IFlexibleGroup {
|
||||||
* Flex the container and its siblings (mutate)
|
* Flex the container and its siblings (mutate)
|
||||||
* @returns Flexed container
|
* @returns Flexed container
|
||||||
*/
|
*/
|
||||||
export function Flex(container: IContainerModel, parent: IContainerModel): void {
|
export function Flex(containers: Map<string, IContainerModel>, container: IContainerModel, parent: IContainerModel): void {
|
||||||
const isVertical = parent.properties.orientation === Orientation.Vertical;
|
const isVertical = parent.properties.orientation === Orientation.Vertical;
|
||||||
|
|
||||||
if (isVertical) {
|
if (isVertical) {
|
||||||
const wantedWidth = Math.min(container.properties.maxWidth, parent.properties.width);
|
const wantedWidth = Math.min(container.properties.maxWidth, parent.properties.width);
|
||||||
container.properties.width = ApplyWidthMargin(wantedWidth, container.properties.margin.left, container.properties.margin.right);
|
container.properties.width = ApplyWidthMargin(wantedWidth, container.properties.margin.left, container.properties.margin.right);
|
||||||
const flexibleGroups = GetVerticalFlexibleGroups(parent);
|
const flexibleGroups = GetVerticalFlexibleGroups(containers, parent);
|
||||||
for (const flexibleGroup of flexibleGroups) {
|
for (const flexibleGroup of flexibleGroups) {
|
||||||
FlexGroupVertically(flexibleGroup);
|
FlexGroupVertically(flexibleGroup);
|
||||||
}
|
}
|
||||||
|
@ -28,7 +29,7 @@ export function Flex(container: IContainerModel, parent: IContainerModel): void
|
||||||
|
|
||||||
const wantedHeight = Math.min(container.properties.maxHeight, parent.properties.height);
|
const wantedHeight = Math.min(container.properties.maxHeight, parent.properties.height);
|
||||||
container.properties.height = ApplyWidthMargin(wantedHeight, container.properties.margin.top, container.properties.margin.bottom);
|
container.properties.height = ApplyWidthMargin(wantedHeight, container.properties.margin.top, container.properties.margin.bottom);
|
||||||
const flexibleGroups = GetHorizontalFlexibleGroups(parent);
|
const flexibleGroups = GetHorizontalFlexibleGroups(containers, parent);
|
||||||
for (const flexibleGroup of flexibleGroups) {
|
for (const flexibleGroup of flexibleGroups) {
|
||||||
FlexGroupHorizontally(flexibleGroup);
|
FlexGroupHorizontally(flexibleGroup);
|
||||||
}
|
}
|
||||||
|
@ -39,12 +40,12 @@ export function Flex(container: IContainerModel, parent: IContainerModel): void
|
||||||
* @param parent Parent in which the flexible children will be set in groups
|
* @param parent Parent in which the flexible children will be set in groups
|
||||||
* @returns a list of groups of flexible containers
|
* @returns a list of groups of flexible containers
|
||||||
*/
|
*/
|
||||||
export function GetHorizontalFlexibleGroups(parent: IContainerModel): IFlexibleGroup[] {
|
export function GetHorizontalFlexibleGroups(containers: Map<string, IContainerModel>, parent: IContainerModel): IFlexibleGroup[] {
|
||||||
const flexibleGroups: IFlexibleGroup[] = [];
|
const flexibleGroups: IFlexibleGroup[] = [];
|
||||||
let group: IContainerModel[] = [];
|
let group: IContainerModel[] = [];
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
let size = 0;
|
let size = 0;
|
||||||
for (const child of parent.children) {
|
for (const child of MakeChildrenIterator(containers, parent.children)) {
|
||||||
if (child.properties.isAnchor) {
|
if (child.properties.isAnchor) {
|
||||||
size = child.properties.x - offset;
|
size = child.properties.x - offset;
|
||||||
const flexibleGroup: IFlexibleGroup = {
|
const flexibleGroup: IFlexibleGroup = {
|
||||||
|
@ -77,12 +78,15 @@ export function GetHorizontalFlexibleGroups(parent: IContainerModel): IFlexibleG
|
||||||
* @param parent Parent in which the flexible children will be set in groups
|
* @param parent Parent in which the flexible children will be set in groups
|
||||||
* @returns a list of groups of flexible containers
|
* @returns a list of groups of flexible containers
|
||||||
*/
|
*/
|
||||||
export function GetVerticalFlexibleGroups(parent: IContainerModel): IFlexibleGroup[] {
|
export function GetVerticalFlexibleGroups(
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
|
parent: IContainerModel
|
||||||
|
): IFlexibleGroup[] {
|
||||||
const flexibleGroups: IFlexibleGroup[] = [];
|
const flexibleGroups: IFlexibleGroup[] = [];
|
||||||
let group: IContainerModel[] = [];
|
let group: IContainerModel[] = [];
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
let size = 0;
|
let size = 0;
|
||||||
for (const child of parent.children) {
|
for (const child of MakeChildrenIterator(containers, parent.children)) {
|
||||||
if (child.properties.isAnchor) {
|
if (child.properties.isAnchor) {
|
||||||
size = child.properties.y - offset;
|
size = child.properties.y - offset;
|
||||||
const flexibleGroup: IFlexibleGroup = {
|
const flexibleGroup: IFlexibleGroup = {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { Orientation } from '../../../Enums/Orientation';
|
import { Orientation } from '../../../Enums/Orientation';
|
||||||
import { ReversePairwise } from '../../../utils/itertools';
|
import { MakeChildrenIterator, ReversePairwise } from '../../../utils/itertools';
|
||||||
import { Flex } from './FlexBehaviors';
|
import { Flex } from './FlexBehaviors';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -8,12 +8,17 @@ import { Flex } from './FlexBehaviors';
|
||||||
* @param container
|
* @param container
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function ApplyPush(container: IContainerModel, parent: IContainerModel): IContainerModel {
|
export function ApplyPush(
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
|
container: IContainerModel,
|
||||||
|
parent: IContainerModel
|
||||||
|
): IContainerModel {
|
||||||
if (parent.children.length <= 1) {
|
if (parent.children.length <= 1) {
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
const children = parent.children;
|
const children: IContainerModel[] = [...MakeChildrenIterator(containers, parent.children)];
|
||||||
|
|
||||||
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
|
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
|
||||||
|
|
||||||
if (isHorizontal) {
|
if (isHorizontal) {
|
||||||
|
@ -22,7 +27,7 @@ export function ApplyPush(container: IContainerModel, parent: IContainerModel):
|
||||||
PushContainersVertically(container, children);
|
PushContainersVertically(container, children);
|
||||||
}
|
}
|
||||||
|
|
||||||
Flex(container, parent);
|
Flex(containers, container, parent);
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { ISizePointer } from '../../../Interfaces/ISizePointer';
|
import { ISizePointer } from '../../../Interfaces/ISizePointer';
|
||||||
import { Orientation } from '../../../Enums/Orientation';
|
import { Orientation } from '../../../Enums/Orientation';
|
||||||
import { ENABLE_HARD_RIGID } from '../../../utils/default';
|
import { ENABLE_HARD_RIGID } from '../../../utils/default';
|
||||||
|
import { MakeChildrenIterator } from '../../../utils/itertools';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "Transform the container into a rigid body"
|
* "Transform the container into a rigid body"
|
||||||
|
@ -21,13 +22,14 @@ import { ENABLE_HARD_RIGID } from '../../../utils/default';
|
||||||
* @returns A rigid body container
|
* @returns A rigid body container
|
||||||
*/
|
*/
|
||||||
export function ApplyRigidBody(
|
export function ApplyRigidBody(
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
container: IContainerModel,
|
container: IContainerModel,
|
||||||
parent: IContainerModel
|
parent: IContainerModel
|
||||||
): IContainerModel {
|
): IContainerModel {
|
||||||
container = ConstraintBodyInsideParent(container, parent);
|
container = ConstraintBodyInsideParent(container, parent);
|
||||||
|
|
||||||
if (ENABLE_HARD_RIGID) {
|
if (ENABLE_HARD_RIGID) {
|
||||||
container = ConstraintBodyInsideUnallocatedWidth(container);
|
container = ConstraintBodyInsideUnallocatedWidth(containers, container);
|
||||||
}
|
}
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
|
@ -117,6 +119,7 @@ function ConstraintBodyInsideSpace(
|
||||||
* @returns Updated container
|
* @returns Updated container
|
||||||
*/
|
*/
|
||||||
export function ConstraintBodyInsideUnallocatedWidth(
|
export function ConstraintBodyInsideUnallocatedWidth(
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
container: IContainerModel
|
container: IContainerModel
|
||||||
): IContainerModel {
|
): IContainerModel {
|
||||||
if (container.parent === null || container.parent === undefined) {
|
if (container.parent === null || container.parent === undefined) {
|
||||||
|
@ -126,10 +129,11 @@ export function ConstraintBodyInsideUnallocatedWidth(
|
||||||
// Get the available spaces of the parent
|
// Get the available spaces of the parent
|
||||||
const isHorizontal =
|
const isHorizontal =
|
||||||
container.parent.properties.orientation === Orientation.Horizontal;
|
container.parent.properties.orientation === Orientation.Horizontal;
|
||||||
|
const children: IContainerModel[] = [...MakeChildrenIterator(containers, container.parent.children)];
|
||||||
const availableWidths = GetAvailableWidths(
|
const availableWidths = GetAvailableWidths(
|
||||||
0,
|
0,
|
||||||
container.parent.properties.width,
|
container.parent.properties.width,
|
||||||
container.parent.children,
|
children,
|
||||||
container,
|
container,
|
||||||
isHorizontal
|
isHorizontal
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,9 +5,13 @@
|
||||||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { Orientation } from '../../../Enums/Orientation';
|
import { Orientation } from '../../../Enums/Orientation';
|
||||||
import { GetHorizontallyOverlappingContainers, GetVerticallyOverlappingContainers } from './AnchorBehaviors';
|
import { GetHorizontallyOverlappingContainers, GetVerticallyOverlappingContainers } from './AnchorBehaviors';
|
||||||
|
import { MakeChildrenIterator } from '../../../utils/itertools';
|
||||||
|
|
||||||
export function ApplySwap(container: IContainerModel, parent: IContainerModel): void {
|
export function ApplySwap(
|
||||||
const children = parent.children;
|
containers: Map<string, IContainerModel>,
|
||||||
|
container: IContainerModel,
|
||||||
|
parent: IContainerModel): void {
|
||||||
|
const children = [...MakeChildrenIterator(containers, parent.children)];
|
||||||
|
|
||||||
const isVertical = parent.properties.orientation === Orientation.Vertical;
|
const isVertical = parent.properties.orientation === Orientation.Vertical;
|
||||||
if (isVertical) {
|
if (isVertical) {
|
||||||
|
|
|
@ -274,7 +274,7 @@ export function Editor(props: IEditorProps): JSX.Element {
|
||||||
// Render
|
// Render
|
||||||
const configuration = props.configuration;
|
const configuration = props.configuration;
|
||||||
const current = GetCurrentHistoryState(history, historyCurrentStep);
|
const current = GetCurrentHistoryState(history, historyCurrentStep);
|
||||||
const selected = FindContainerById(current.mainContainer, current.selectedContainerId);
|
const selected = FindContainerById(current.containers, current.selectedContainerId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={editorRef} className="Editor font-sans h-full">
|
<div ref={editorRef} className="Editor font-sans h-full">
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { PropertyType } from '../../Enums/PropertyType';
|
||||||
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
|
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
|
||||||
|
|
||||||
interface IElementsListProps {
|
interface IElementsListProps {
|
||||||
|
containers: Map<string, IContainerModel>
|
||||||
mainContainer: IContainerModel
|
mainContainer: IContainerModel
|
||||||
symbols: Map<string, ISymbolModel>
|
symbols: Map<string, ISymbolModel>
|
||||||
selectedContainer: IContainerModel | undefined
|
selectedContainer: IContainerModel | undefined
|
||||||
|
@ -59,6 +60,7 @@ function HandleDragOver(
|
||||||
|
|
||||||
function HandleOnDrop(
|
function HandleOnDrop(
|
||||||
event: React.DragEvent,
|
event: React.DragEvent,
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
mainContainer: IContainerModel,
|
mainContainer: IContainerModel,
|
||||||
addContainer: (index: number, type: string, parent: string) => void
|
addContainer: (index: number, type: string, parent: string) => void
|
||||||
): void {
|
): void {
|
||||||
|
@ -68,7 +70,7 @@ function HandleOnDrop(
|
||||||
RemoveBorderClasses(target);
|
RemoveBorderClasses(target);
|
||||||
|
|
||||||
const targetContainer: IContainerModel | undefined = FindContainerById(
|
const targetContainer: IContainerModel | undefined = FindContainerById(
|
||||||
mainContainer,
|
containers,
|
||||||
target.id
|
target.id
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -95,7 +97,7 @@ function HandleOnDrop(
|
||||||
|
|
||||||
// locate the hitboxes
|
// locate the hitboxes
|
||||||
if (y < 12) {
|
if (y < 12) {
|
||||||
const index = targetContainer.parent.children.indexOf(targetContainer);
|
const index = targetContainer.parent.children.indexOf(targetContainer.properties.id);
|
||||||
addContainer(
|
addContainer(
|
||||||
index,
|
index,
|
||||||
type,
|
type,
|
||||||
|
@ -107,7 +109,7 @@ function HandleOnDrop(
|
||||||
type,
|
type,
|
||||||
targetContainer.properties.id);
|
targetContainer.properties.id);
|
||||||
} else {
|
} else {
|
||||||
const index = targetContainer.parent.children.indexOf(targetContainer);
|
const index = targetContainer.parent.children.indexOf(targetContainer.properties.id);
|
||||||
addContainer(
|
addContainer(
|
||||||
index + 1,
|
index + 1,
|
||||||
type,
|
type,
|
||||||
|
@ -119,10 +121,10 @@ function HandleOnDrop(
|
||||||
export function ElementsList(props: IElementsListProps): JSX.Element {
|
export function ElementsList(props: IElementsListProps): JSX.Element {
|
||||||
// States
|
// States
|
||||||
const divRef = React.useRef<HTMLDivElement>(null);
|
const divRef = React.useRef<HTMLDivElement>(null);
|
||||||
const [width, height] = useSize(divRef);
|
const [, height] = useSize(divRef);
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
const it = MakeRecursionDFSIterator(props.mainContainer, 0, [0, 0], true);
|
const it = MakeRecursionDFSIterator(props.mainContainer, props.containers, 0, [0, 0], true);
|
||||||
const containers = [...it];
|
const containers = [...it];
|
||||||
function Row({
|
function Row({
|
||||||
index, style
|
index, style
|
||||||
|
@ -154,7 +156,7 @@ export function ElementsList(props: IElementsListProps): JSX.Element {
|
||||||
style={style}
|
style={style}
|
||||||
title={container.properties.warning}
|
title={container.properties.warning}
|
||||||
onClick={() => props.selectContainer(container.properties.id)}
|
onClick={() => props.selectContainer(container.properties.id)}
|
||||||
onDrop={(event) => HandleOnDrop(event, props.mainContainer, props.addContainer)}
|
onDrop={(event) => HandleOnDrop(event, props.containers, props.mainContainer, props.addContainer)}
|
||||||
onDragOver={(event) => HandleDragOver(event, props.mainContainer)}
|
onDragOver={(event) => HandleDragOver(event, props.mainContainer)}
|
||||||
onDragLeave={(event) => HandleDragLeave(event)}
|
onDragLeave={(event) => HandleDragLeave(event)}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,183 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
import { FixedSizeList as List } from 'react-window';
|
|
||||||
import { Properties } from '../ContainerProperties/ContainerProperties';
|
|
||||||
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
|
||||||
import { FindContainerById, MakeRecursionDFSIterator } from '../../utils/itertools';
|
|
||||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
|
||||||
import { PropertyType } from '../../Enums/PropertyType';
|
|
||||||
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
|
|
||||||
|
|
||||||
interface IElementsProps {
|
|
||||||
mainContainer: IContainerModel
|
|
||||||
symbols: Map<string, ISymbolModel>
|
|
||||||
selectedContainer: IContainerModel | undefined
|
|
||||||
onPropertyChange: (
|
|
||||||
key: string,
|
|
||||||
value: string | number | boolean | number[],
|
|
||||||
type?: PropertyType
|
|
||||||
) => void
|
|
||||||
selectContainer: (containerId: string) => void
|
|
||||||
addContainer: (index: number, type: string, parent: string) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
function RemoveBorderClasses(target: HTMLButtonElement, exception: string = ''): void {
|
|
||||||
const bordersClasses = ['border-t-8', 'border-8', 'border-b-8'].filter(className => className !== exception);
|
|
||||||
target.classList.remove(...bordersClasses);
|
|
||||||
}
|
|
||||||
|
|
||||||
function HandleDragLeave(event: React.DragEvent): void {
|
|
||||||
const target: HTMLButtonElement = event.target as HTMLButtonElement;
|
|
||||||
RemoveBorderClasses(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
function HandleDragOver(
|
|
||||||
event: React.DragEvent,
|
|
||||||
mainContainer: IContainerModel
|
|
||||||
): void {
|
|
||||||
event.preventDefault();
|
|
||||||
const target: HTMLButtonElement = event.target as HTMLButtonElement;
|
|
||||||
const rect = target.getBoundingClientRect();
|
|
||||||
const y = event.clientY - rect.top; // y position within the element.
|
|
||||||
|
|
||||||
if (target.id === mainContainer.properties.id) {
|
|
||||||
target.classList.add('border-8');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y < 12) {
|
|
||||||
RemoveBorderClasses(target, 'border-t-8');
|
|
||||||
target.classList.add('border-t-8');
|
|
||||||
} else if (y < 24) {
|
|
||||||
RemoveBorderClasses(target, 'border-8');
|
|
||||||
target.classList.add('border-8');
|
|
||||||
} else {
|
|
||||||
RemoveBorderClasses(target, 'border-b-8');
|
|
||||||
target.classList.add('border-b-8');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function HandleOnDrop(
|
|
||||||
event: React.DragEvent,
|
|
||||||
mainContainer: IContainerModel,
|
|
||||||
addContainer: (index: number, type: string, parent: string) => void
|
|
||||||
): void {
|
|
||||||
event.preventDefault();
|
|
||||||
const type = event.dataTransfer.getData('type');
|
|
||||||
const target: HTMLButtonElement = event.target as HTMLButtonElement;
|
|
||||||
RemoveBorderClasses(target);
|
|
||||||
|
|
||||||
const targetContainer: IContainerModel | undefined = FindContainerById(
|
|
||||||
mainContainer,
|
|
||||||
target.id
|
|
||||||
);
|
|
||||||
|
|
||||||
if (targetContainer === undefined) {
|
|
||||||
throw new Error('[handleOnDrop] Tried to drop onto a unknown container!');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetContainer === mainContainer) {
|
|
||||||
// if the container is the root, only add type as child
|
|
||||||
addContainer(
|
|
||||||
targetContainer.children.length,
|
|
||||||
type,
|
|
||||||
targetContainer.properties.id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetContainer.parent === null ||
|
|
||||||
targetContainer.parent === undefined) {
|
|
||||||
throw new Error('[handleDrop] Tried to drop into a child container without a parent!');
|
|
||||||
}
|
|
||||||
|
|
||||||
const rect = target.getBoundingClientRect();
|
|
||||||
const y = event.clientY - rect.top; // y position within the element.
|
|
||||||
|
|
||||||
// locate the hitboxes
|
|
||||||
if (y < 12) {
|
|
||||||
const index = targetContainer.parent.children.indexOf(targetContainer);
|
|
||||||
addContainer(
|
|
||||||
index,
|
|
||||||
type,
|
|
||||||
targetContainer.parent.properties.id
|
|
||||||
);
|
|
||||||
} else if (y < 24) {
|
|
||||||
addContainer(
|
|
||||||
targetContainer.children.length,
|
|
||||||
type,
|
|
||||||
targetContainer.properties.id);
|
|
||||||
} else {
|
|
||||||
const index = targetContainer.parent.children.indexOf(targetContainer);
|
|
||||||
addContainer(
|
|
||||||
index + 1,
|
|
||||||
type,
|
|
||||||
targetContainer.parent.properties.id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Elements(props: IElementsProps): JSX.Element {
|
|
||||||
// Render
|
|
||||||
const it = MakeRecursionDFSIterator(props.mainContainer, 0, [0, 0], true);
|
|
||||||
const containers = [...it];
|
|
||||||
function Row({
|
|
||||||
index, style
|
|
||||||
}: {
|
|
||||||
index: number
|
|
||||||
style: React.CSSProperties
|
|
||||||
}): JSX.Element {
|
|
||||||
const { container, depth } = containers[index];
|
|
||||||
const key = container.properties.id.toString();
|
|
||||||
const tabs = '|\t'.repeat(depth);
|
|
||||||
const text = container.properties.displayedText === key
|
|
||||||
? `${key}`
|
|
||||||
: `${container.properties.displayedText}`;
|
|
||||||
|
|
||||||
const isSelected = props.selectedContainer !== undefined &&
|
|
||||||
props.selectedContainer !== null &&
|
|
||||||
props.selectedContainer.properties.id === container.properties.id;
|
|
||||||
|
|
||||||
const selectedClass: string = isSelected
|
|
||||||
? 'border-l-4 bg-blue-500 shadow-lg shadow-blue-500/60 hover:bg-blue-600 hover:shadow-blue-500 text-slate-50'
|
|
||||||
: 'bg-slate-300/60 hover:bg-slate-400 hover:shadow-slate-400';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button type="button"
|
|
||||||
className={`transition-all w-full border-blue-500 hover:shadow-lg elements-sidebar-row whitespace-pre
|
|
||||||
text-left text-sm font-medium inline-flex ${container.properties.type} ${selectedClass}`}
|
|
||||||
id={key}
|
|
||||||
key={key}
|
|
||||||
style={style}
|
|
||||||
title={container.properties.warning}
|
|
||||||
onClick={() => props.selectContainer(container.properties.id)}
|
|
||||||
onDrop={(event) => HandleOnDrop(event, props.mainContainer, props.addContainer)}
|
|
||||||
onDragOver={(event) => HandleDragOver(event, props.mainContainer)}
|
|
||||||
onDragLeave={(event) => HandleDragLeave(event)}
|
|
||||||
>
|
|
||||||
{tabs}
|
|
||||||
{text}
|
|
||||||
{container.properties.warning.length > 0 &&
|
|
||||||
<ExclamationTriangleIcon className='w-8'/>
|
|
||||||
}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<List
|
|
||||||
className="List divide-y divide-black overflow-y-auto"
|
|
||||||
itemCount={containers.length}
|
|
||||||
itemSize={35}
|
|
||||||
height={192}
|
|
||||||
width={'100%'}
|
|
||||||
>
|
|
||||||
{Row}
|
|
||||||
</List>
|
|
||||||
<Properties
|
|
||||||
properties={props.selectedContainer?.properties}
|
|
||||||
symbols={props.symbols}
|
|
||||||
onChange={props.onPropertyChange}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { TrashIcon } from '@heroicons/react/24/outline';
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { FixedSizeList as List } from 'react-window';
|
import { FixedSizeList as List } from 'react-window';
|
||||||
|
import { API_GET_FEEDBACK_URL } from '../../../public/svgld-settings';
|
||||||
import { MessageType } from '../../Enums/MessageType';
|
import { MessageType } from '../../Enums/MessageType';
|
||||||
import { IGetFeedbackRequest } from '../../Interfaces/IGetFeedbackRequest';
|
import { IGetFeedbackRequest } from '../../Interfaces/IGetFeedbackRequest';
|
||||||
import { IGetFeedbackResponse } from '../../Interfaces/IGetFeedbackResponse';
|
import { IGetFeedbackResponse } from '../../Interfaces/IGetFeedbackResponse';
|
||||||
|
@ -25,7 +25,7 @@ function UseWorker(
|
||||||
// use webworker for the stringify to avoid freezing
|
// use webworker for the stringify to avoid freezing
|
||||||
myWorker.postMessage({
|
myWorker.postMessage({
|
||||||
state,
|
state,
|
||||||
url: import.meta.env.VITE_API_GET_FEEDBACK_URL
|
url: API_GET_FEEDBACK_URL
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
@ -49,7 +49,7 @@ function UseAsync(
|
||||||
ApplicationState: state
|
ApplicationState: state
|
||||||
};
|
};
|
||||||
const dataParsed = JSON.stringify(request, GetCircularReplacerKeepDataStructure());
|
const dataParsed = JSON.stringify(request, GetCircularReplacerKeepDataStructure());
|
||||||
fetch(import.meta.env.VITE_API_GET_FEEDBACK_URL, {
|
fetch(API_GET_FEEDBACK_URL, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: new Headers({
|
headers: new Headers({
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
|
|
@ -4,8 +4,10 @@ import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { IContainerProperties } from '../../../Interfaces/IContainerProperties';
|
import { IContainerProperties } from '../../../Interfaces/IContainerProperties';
|
||||||
import { Camelize } from '../../../utils/stringtools';
|
import { Camelize } from '../../../utils/stringtools';
|
||||||
import { SHOW_TEXT } from '../../../utils/default';
|
import { SHOW_TEXT } from '../../../utils/default';
|
||||||
|
import { FindContainerById } from '../../../utils/itertools';
|
||||||
|
|
||||||
interface IContainerProps {
|
interface IContainerProps {
|
||||||
|
containers: Map<string, IContainerModel>
|
||||||
model: IContainerModel
|
model: IContainerModel
|
||||||
depth: number
|
depth: number
|
||||||
scale: number
|
scale: number
|
||||||
|
@ -18,13 +20,22 @@ interface IContainerProps {
|
||||||
*/
|
*/
|
||||||
export function Container(props: IContainerProps): JSX.Element {
|
export function Container(props: IContainerProps): JSX.Element {
|
||||||
const containersElements = props.model.children.map(
|
const containersElements = props.model.children.map(
|
||||||
child => <Container
|
childId => {
|
||||||
key={`container-${child.properties.id}`}
|
const child = FindContainerById(props.containers, childId);
|
||||||
model={child}
|
|
||||||
depth={props.depth + 1}
|
if (child === undefined) {
|
||||||
scale={props.scale}
|
return <></>;
|
||||||
selectContainer={props.selectContainer}
|
}
|
||||||
/>);
|
|
||||||
|
return <Container
|
||||||
|
key={`container-${child.properties.id}`}
|
||||||
|
containers={props.containers}
|
||||||
|
model={child}
|
||||||
|
depth={props.depth + 1}
|
||||||
|
scale={props.scale}
|
||||||
|
selectContainer={props.selectContainer}
|
||||||
|
/>;
|
||||||
|
});
|
||||||
|
|
||||||
const width: number = props.model.properties.width;
|
const width: number = props.model.properties.width;
|
||||||
const height: number = props.model.properties.height;
|
const height: number = props.model.properties.height;
|
||||||
|
|
|
@ -1,17 +1,22 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { ContainerModel } from '../../../Interfaces/IContainerModel';
|
import { ContainerModel, IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { DIMENSION_MARGIN } from '../../../utils/default';
|
import { DIMENSION_MARGIN } from '../../../utils/default';
|
||||||
import { GetAbsolutePosition, MakeBFSIterator } from '../../../utils/itertools';
|
import { GetAbsolutePosition, MakeBFSIterator } from '../../../utils/itertools';
|
||||||
import { TransformX } from '../../../utils/svg';
|
import { TransformX } from '../../../utils/svg';
|
||||||
import { Dimension } from './Dimension';
|
import { Dimension } from './Dimension';
|
||||||
|
|
||||||
interface IDimensionLayerProps {
|
interface IDimensionLayerProps {
|
||||||
|
containers: Map<string, IContainerModel>
|
||||||
roots: ContainerModel | ContainerModel[] | null
|
roots: ContainerModel | ContainerModel[] | null
|
||||||
scale?: number
|
scale?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
function GetDimensionsNodes(root: ContainerModel, scale: number): React.ReactNode[] {
|
function GetDimensionsNodes(
|
||||||
const it = MakeBFSIterator(root);
|
containers: Map<string, IContainerModel>,
|
||||||
|
root: ContainerModel,
|
||||||
|
scale: number
|
||||||
|
): React.ReactNode[] {
|
||||||
|
const it = MakeBFSIterator(root, containers);
|
||||||
const dimensions: React.ReactNode[] = [];
|
const dimensions: React.ReactNode[] = [];
|
||||||
let currentDepth = 0;
|
let currentDepth = 0;
|
||||||
let min = Infinity;
|
let min = Infinity;
|
||||||
|
@ -53,10 +58,10 @@ export function DepthDimensionLayer(props: IDimensionLayerProps): JSX.Element {
|
||||||
const scale = props.scale ?? 1;
|
const scale = props.scale ?? 1;
|
||||||
if (Array.isArray(props.roots)) {
|
if (Array.isArray(props.roots)) {
|
||||||
props.roots.forEach(child => {
|
props.roots.forEach(child => {
|
||||||
dimensions.concat(GetDimensionsNodes(child, scale));
|
dimensions.concat(GetDimensionsNodes(props.containers, child, scale));
|
||||||
});
|
});
|
||||||
} else if (props.roots !== null) {
|
} else if (props.roots !== null) {
|
||||||
dimensions = GetDimensionsNodes(props.roots, scale);
|
dimensions = GetDimensionsNodes(props.containers, props.roots, scale);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<g>
|
<g>
|
||||||
|
|
|
@ -3,11 +3,12 @@ import { Orientation } from '../../../Enums/Orientation';
|
||||||
import { Position } from '../../../Enums/Position';
|
import { Position } from '../../../Enums/Position';
|
||||||
import { ContainerModel, IContainerModel } from '../../../Interfaces/IContainerModel';
|
import { ContainerModel, IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { DIMENSION_MARGIN, SHOW_BORROWER_DIMENSIONS, SHOW_CHILDREN_DIMENSIONS, SHOW_SELF_DIMENSIONS } from '../../../utils/default';
|
import { DIMENSION_MARGIN, SHOW_BORROWER_DIMENSIONS, SHOW_CHILDREN_DIMENSIONS, SHOW_SELF_DIMENSIONS } from '../../../utils/default';
|
||||||
import { MakeRecursionDFSIterator, Pairwise } from '../../../utils/itertools';
|
import { FindContainerById, MakeRecursionDFSIterator, Pairwise } from '../../../utils/itertools';
|
||||||
import { TransformX, TransformY } from '../../../utils/svg';
|
import { TransformX, TransformY } from '../../../utils/svg';
|
||||||
import { Dimension } from './Dimension';
|
import { Dimension } from './Dimension';
|
||||||
|
|
||||||
interface IDimensionLayerProps {
|
interface IDimensionLayerProps {
|
||||||
|
containers: Map<string, IContainerModel>
|
||||||
root: ContainerModel
|
root: ContainerModel
|
||||||
scale: number
|
scale: number
|
||||||
}
|
}
|
||||||
|
@ -51,8 +52,8 @@ function ActionByPosition(
|
||||||
* @param param0 Object with the root container and the scale of the svg
|
* @param param0 Object with the root container and the scale of the svg
|
||||||
* @returns A list of dimensions
|
* @returns A list of dimensions
|
||||||
*/
|
*/
|
||||||
function Dimensions({ root, scale }: IDimensionLayerProps): React.ReactNode[] {
|
function Dimensions({ containers, root, scale }: IDimensionLayerProps): React.ReactNode[] {
|
||||||
const it = MakeRecursionDFSIterator(root, 0, [0, 0]);
|
const it = MakeRecursionDFSIterator(root, containers, 0, [0, 0]);
|
||||||
const dimensions: React.ReactNode[] = [];
|
const dimensions: React.ReactNode[] = [];
|
||||||
const topDim = root.properties.y;
|
const topDim = root.properties.y;
|
||||||
const leftDim = root.properties.x;
|
const leftDim = root.properties.x;
|
||||||
|
@ -75,7 +76,8 @@ function Dimensions({ root, scale }: IDimensionLayerProps): React.ReactNode[] {
|
||||||
container.properties.showSelfDimensions,
|
container.properties.showSelfDimensions,
|
||||||
AddHorizontalSelfDimension,
|
AddHorizontalSelfDimension,
|
||||||
AddVerticalSelfDimension,
|
AddVerticalSelfDimension,
|
||||||
[container,
|
[
|
||||||
|
container,
|
||||||
currentTransform,
|
currentTransform,
|
||||||
dimensions,
|
dimensions,
|
||||||
scale]
|
scale]
|
||||||
|
@ -88,7 +90,9 @@ function Dimensions({ root, scale }: IDimensionLayerProps): React.ReactNode[] {
|
||||||
container.properties.showDimensionWithMarks,
|
container.properties.showDimensionWithMarks,
|
||||||
AddHorizontalBorrowerDimension,
|
AddHorizontalBorrowerDimension,
|
||||||
AddVerticalBorrowerDimension,
|
AddVerticalBorrowerDimension,
|
||||||
[container,
|
[
|
||||||
|
containers,
|
||||||
|
container,
|
||||||
depth,
|
depth,
|
||||||
currentTransform,
|
currentTransform,
|
||||||
dimensions,
|
dimensions,
|
||||||
|
@ -102,7 +106,9 @@ function Dimensions({ root, scale }: IDimensionLayerProps): React.ReactNode[] {
|
||||||
container.properties.showChildrenDimensions,
|
container.properties.showChildrenDimensions,
|
||||||
AddHorizontalChildrenDimension,
|
AddHorizontalChildrenDimension,
|
||||||
AddVerticalChildrenDimension,
|
AddVerticalChildrenDimension,
|
||||||
[container,
|
[
|
||||||
|
containers,
|
||||||
|
container,
|
||||||
currentTransform,
|
currentTransform,
|
||||||
dimensions,
|
dimensions,
|
||||||
scale]
|
scale]
|
||||||
|
@ -130,6 +136,7 @@ export function DimensionLayer(props: IDimensionLayerProps): JSX.Element {
|
||||||
|
|
||||||
function AddHorizontalChildrenDimension(
|
function AddHorizontalChildrenDimension(
|
||||||
yDim: number,
|
yDim: number,
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
container: IContainerModel,
|
container: IContainerModel,
|
||||||
currentTransform: [number, number],
|
currentTransform: [number, number],
|
||||||
dimensions: React.ReactNode[],
|
dimensions: React.ReactNode[],
|
||||||
|
@ -137,13 +144,25 @@ function AddHorizontalChildrenDimension(
|
||||||
): void {
|
): void {
|
||||||
const childrenId = `dim-y${yDim.toFixed(0)}-children-${container.properties.id}`;
|
const childrenId = `dim-y${yDim.toFixed(0)}-children-${container.properties.id}`;
|
||||||
|
|
||||||
const lastChild = container.children[container.children.length - 1];
|
const lastChildId = container.children[container.children.length - 1];
|
||||||
|
const lastChild = FindContainerById(containers, lastChildId);
|
||||||
|
|
||||||
|
if (lastChild === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let xChildrenStart = TransformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.positionReference);
|
let xChildrenStart = TransformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.positionReference);
|
||||||
let xChildrenEnd = TransformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.positionReference);
|
let xChildrenEnd = TransformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.positionReference);
|
||||||
|
|
||||||
// Find the min and max
|
// Find the min and max
|
||||||
for (let i = container.children.length - 2; i >= 0; i--) {
|
for (let i = container.children.length - 2; i >= 0; i--) {
|
||||||
const child = container.children[i];
|
const childId = container.children[i];
|
||||||
|
const child = FindContainerById(containers, childId);
|
||||||
|
|
||||||
|
if (child === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const left = TransformX(child.properties.x, child.properties.width, child.properties.positionReference);
|
const left = TransformX(child.properties.x, child.properties.width, child.properties.positionReference);
|
||||||
if (left < xChildrenStart) {
|
if (left < xChildrenStart) {
|
||||||
xChildrenStart = left;
|
xChildrenStart = left;
|
||||||
|
@ -178,6 +197,7 @@ function AddHorizontalChildrenDimension(
|
||||||
|
|
||||||
function AddVerticalChildrenDimension(
|
function AddVerticalChildrenDimension(
|
||||||
xDim: number,
|
xDim: number,
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
container: IContainerModel,
|
container: IContainerModel,
|
||||||
currentTransform: [number, number],
|
currentTransform: [number, number],
|
||||||
dimensions: React.ReactNode[],
|
dimensions: React.ReactNode[],
|
||||||
|
@ -185,13 +205,25 @@ function AddVerticalChildrenDimension(
|
||||||
): void {
|
): void {
|
||||||
const childrenId = `dim-x${xDim.toFixed(0)}-children-${container.properties.id}`;
|
const childrenId = `dim-x${xDim.toFixed(0)}-children-${container.properties.id}`;
|
||||||
|
|
||||||
const lastChild = container.children[container.children.length - 1];
|
const lastChildId = container.children[container.children.length - 1];
|
||||||
|
const lastChild = FindContainerById(containers, lastChildId);
|
||||||
|
|
||||||
|
if (lastChild === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let yChildrenStart = TransformY(lastChild.properties.y, lastChild.properties.height, lastChild.properties.positionReference);
|
let yChildrenStart = TransformY(lastChild.properties.y, lastChild.properties.height, lastChild.properties.positionReference);
|
||||||
let yChildrenEnd = TransformY(lastChild.properties.y, lastChild.properties.height, lastChild.properties.positionReference);
|
let yChildrenEnd = TransformY(lastChild.properties.y, lastChild.properties.height, lastChild.properties.positionReference);
|
||||||
|
|
||||||
// Find the min and max
|
// Find the min and max
|
||||||
for (let i = container.children.length - 2; i >= 0; i--) {
|
for (let i = container.children.length - 2; i >= 0; i--) {
|
||||||
const child = container.children[i];
|
const childId = container.children[i];
|
||||||
|
const child = FindContainerById(containers, childId);
|
||||||
|
|
||||||
|
if (child === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const top = TransformY(child.properties.y, child.properties.height, child.properties.positionReference);
|
const top = TransformY(child.properties.y, child.properties.height, child.properties.positionReference);
|
||||||
if (top < yChildrenStart) {
|
if (top < yChildrenStart) {
|
||||||
yChildrenStart = top;
|
yChildrenStart = top;
|
||||||
|
@ -227,13 +259,14 @@ function AddVerticalChildrenDimension(
|
||||||
|
|
||||||
function AddHorizontalBorrowerDimension(
|
function AddHorizontalBorrowerDimension(
|
||||||
yDim: number,
|
yDim: number,
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
container: IContainerModel,
|
container: IContainerModel,
|
||||||
depth: number,
|
depth: number,
|
||||||
currentTransform: [number, number],
|
currentTransform: [number, number],
|
||||||
dimensions: React.ReactNode[],
|
dimensions: React.ReactNode[],
|
||||||
scale: number
|
scale: number
|
||||||
): void {
|
): void {
|
||||||
const it = MakeRecursionDFSIterator(container, depth, currentTransform);
|
const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform);
|
||||||
const marks = []; // list of vertical lines for the dimension
|
const marks = []; // list of vertical lines for the dimension
|
||||||
for (const {
|
for (const {
|
||||||
container: childContainer, currentTransform: childCurrentTransform
|
container: childContainer, currentTransform: childCurrentTransform
|
||||||
|
@ -279,13 +312,14 @@ function AddHorizontalBorrowerDimension(
|
||||||
|
|
||||||
function AddVerticalBorrowerDimension(
|
function AddVerticalBorrowerDimension(
|
||||||
xDim: number,
|
xDim: number,
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
container: IContainerModel,
|
container: IContainerModel,
|
||||||
depth: number,
|
depth: number,
|
||||||
currentTransform: [number, number],
|
currentTransform: [number, number],
|
||||||
dimensions: React.ReactNode[],
|
dimensions: React.ReactNode[],
|
||||||
scale: number
|
scale: number
|
||||||
): void {
|
): void {
|
||||||
const it = MakeRecursionDFSIterator(container, depth, currentTransform);
|
const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform);
|
||||||
const marks = []; // list of vertical lines for the dimension
|
const marks = []; // list of vertical lines for the dimension
|
||||||
for (const {
|
for (const {
|
||||||
container: childContainer, currentTransform: childCurrentTransform
|
container: childContainer, currentTransform: childCurrentTransform
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { ReactSVGPanZoom, Tool, TOOL_PAN, Value } from 'react-svg-pan-zoom';
|
import { ReactSVGPanZoom, Tool, TOOL_PAN, Value } from 'react-svg-pan-zoom';
|
||||||
import { Container } from './Elements/Container';
|
import { Container } from './Elements/Container';
|
||||||
import { ContainerModel } from '../../Interfaces/IContainerModel';
|
import { ContainerModel, IContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { Selector } from './Elements/Selector/Selector';
|
import { Selector } from './Elements/Selector/Selector';
|
||||||
import { DepthDimensionLayer } from './Elements/DepthDimensionLayer';
|
import { DepthDimensionLayer } from './Elements/DepthDimensionLayer';
|
||||||
import { MAX_FRAMERATE, SHOW_DIMENSIONS_PER_DEPTH } from '../../utils/default';
|
import { MAX_FRAMERATE, SHOW_DIMENSIONS_PER_DEPTH } from '../../utils/default';
|
||||||
|
@ -15,17 +15,13 @@ interface ISVGProps {
|
||||||
viewerHeight: number
|
viewerHeight: number
|
||||||
width: number
|
width: number
|
||||||
height: number
|
height: number
|
||||||
|
containers: Map<string, IContainerModel>
|
||||||
children: ContainerModel
|
children: ContainerModel
|
||||||
selected?: ContainerModel
|
selected?: ContainerModel
|
||||||
symbols: Map<string, ISymbolModel>
|
symbols: Map<string, ISymbolModel>
|
||||||
selectContainer: (containerId: string) => void
|
selectContainer: (containerId: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Viewer {
|
|
||||||
viewerWidth: number
|
|
||||||
viewerHeight: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ID = 'svg';
|
export const ID = 'svg';
|
||||||
|
|
||||||
export function SVG(props: ISVGProps): JSX.Element {
|
export function SVG(props: ISVGProps): JSX.Element {
|
||||||
|
@ -55,6 +51,7 @@ export function SVG(props: ISVGProps): JSX.Element {
|
||||||
let children: React.ReactNode | React.ReactNode[] = [];
|
let children: React.ReactNode | React.ReactNode[] = [];
|
||||||
children = <Container
|
children = <Container
|
||||||
key={`container-${props.children.properties.id}`}
|
key={`container-${props.children.properties.id}`}
|
||||||
|
containers={props.containers}
|
||||||
model={props.children}
|
model={props.children}
|
||||||
depth={0}
|
depth={0}
|
||||||
scale={scale}
|
scale={scale}
|
||||||
|
@ -97,9 +94,9 @@ export function SVG(props: ISVGProps): JSX.Element {
|
||||||
<svg {...properties}>
|
<svg {...properties}>
|
||||||
{children}
|
{children}
|
||||||
{SHOW_DIMENSIONS_PER_DEPTH
|
{SHOW_DIMENSIONS_PER_DEPTH
|
||||||
? <DepthDimensionLayer scale={scale} roots={props.children} />
|
? <DepthDimensionLayer containers={props.containers} scale={scale} roots={props.children} />
|
||||||
: null}
|
: null}
|
||||||
<DimensionLayer scale={scale} root={props.children} />
|
<DimensionLayer containers={props.containers} scale={scale} root={props.children} />
|
||||||
<SymbolLayer scale={scale} symbols={props.symbols} />
|
<SymbolLayer scale={scale} symbols={props.symbols} />
|
||||||
<Selector scale={scale} selected={props.selected} /> {/* leave this at the end so it can be removed during the svg export */}
|
<Selector scale={scale} selected={props.selected} /> {/* leave this at the end so it can be removed during the svg export */}
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { Settings } from '../Settings/Settings';
|
||||||
import { IMessage } from '../../Interfaces/IMessage';
|
import { IMessage } from '../../Interfaces/IMessage';
|
||||||
import { DISABLE_API } from '../../utils/default';
|
import { DISABLE_API } from '../../utils/default';
|
||||||
import { UseWorker, UseAsync } from './UseWorker';
|
import { UseWorker, UseAsync } from './UseWorker';
|
||||||
|
import { FindContainerById } from '../../utils/itertools';
|
||||||
|
|
||||||
export interface IUIProps {
|
export interface IUIProps {
|
||||||
selectedContainer: IContainerModel | undefined
|
selectedContainer: IContainerModel | undefined
|
||||||
|
@ -89,6 +90,12 @@ export function UI(props: IUIProps): JSX.Element {
|
||||||
let leftChildren: JSX.Element = (<></>);
|
let leftChildren: JSX.Element = (<></>);
|
||||||
let rightChildren: JSX.Element = (<></>);
|
let rightChildren: JSX.Element = (<></>);
|
||||||
|
|
||||||
|
const mainContainer = FindContainerById(props.current.containers, props.current.mainContainer)
|
||||||
|
|
||||||
|
if (mainContainer === undefined) {
|
||||||
|
throw new Error('Tried to initialized UI but there is no main container!');
|
||||||
|
}
|
||||||
|
|
||||||
switch (selectedSidebar) {
|
switch (selectedSidebar) {
|
||||||
case SidebarType.Components:
|
case SidebarType.Components:
|
||||||
leftSidebarTitle = 'Components';
|
leftSidebarTitle = 'Components';
|
||||||
|
@ -100,7 +107,8 @@ export function UI(props: IUIProps): JSX.Element {
|
||||||
/>;
|
/>;
|
||||||
rightSidebarTitle = 'Elements';
|
rightSidebarTitle = 'Elements';
|
||||||
rightChildren = <ElementsList
|
rightChildren = <ElementsList
|
||||||
mainContainer={props.current.mainContainer}
|
containers={props.current.containers}
|
||||||
|
mainContainer={mainContainer}
|
||||||
symbols={props.current.symbols}
|
symbols={props.current.symbols}
|
||||||
selectedContainer={props.selectedContainer}
|
selectedContainer={props.selectedContainer}
|
||||||
onPropertyChange={props.onPropertyChange}
|
onPropertyChange={props.onPropertyChange}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { IGetFeedbackRequest } from '../../Interfaces/IGetFeedbackRequest';
|
||||||
import { IGetFeedbackResponse } from '../../Interfaces/IGetFeedbackResponse';
|
import { IGetFeedbackResponse } from '../../Interfaces/IGetFeedbackResponse';
|
||||||
import { IMessage } from '../../Interfaces/IMessage';
|
import { IMessage } from '../../Interfaces/IMessage';
|
||||||
import { GetCircularReplacerKeepDataStructure } from '../../utils/saveload';
|
import { GetCircularReplacerKeepDataStructure } from '../../utils/saveload';
|
||||||
|
import { API_GET_FEEDBACK_URL } from '../../../public/svgld-settings';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||||
const myWorker = window.Worker && new Worker('workers/message_worker.js');
|
const myWorker = window.Worker && new Worker('workers/message_worker.js');
|
||||||
|
@ -15,7 +16,7 @@ export function UseWorker(
|
||||||
// use webworker for the stringify to avoid freezing
|
// use webworker for the stringify to avoid freezing
|
||||||
myWorker.postMessage({
|
myWorker.postMessage({
|
||||||
state,
|
state,
|
||||||
url: import.meta.env.VITE_API_GET_FEEDBACK_URL
|
url: API_GET_FEEDBACK_URL
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
@ -37,7 +38,7 @@ export function UseAsync(
|
||||||
ApplicationState: state
|
ApplicationState: state
|
||||||
};
|
};
|
||||||
const dataParsed = JSON.stringify(request, GetCircularReplacerKeepDataStructure());
|
const dataParsed = JSON.stringify(request, GetCircularReplacerKeepDataStructure());
|
||||||
fetch(import.meta.env.VITE_API_GET_FEEDBACK_URL, {
|
fetch(API_GET_FEEDBACK_URL, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: new Headers({
|
headers: new Headers({
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||||
import { IPoint } from '../../Interfaces/IPoint';
|
import { IPoint } from '../../Interfaces/IPoint';
|
||||||
import { DIMENSION_MARGIN, USE_EXPERIMENTAL_CANVAS_API } from '../../utils/default';
|
import { DIMENSION_MARGIN, USE_EXPERIMENTAL_CANVAS_API } from '../../utils/default';
|
||||||
import { MakeRecursionDFSIterator } from '../../utils/itertools';
|
import { FindContainerById, MakeRecursionDFSIterator } from '../../utils/itertools';
|
||||||
import { BAR_WIDTH } from '../Bar/Bar';
|
import { BAR_WIDTH } from '../Bar/Bar';
|
||||||
import { Canvas } from '../Canvas/Canvas';
|
import { Canvas } from '../Canvas/Canvas';
|
||||||
import { AddDimensions } from '../Canvas/DimensionLayer';
|
import { AddDimensions } from '../Canvas/DimensionLayer';
|
||||||
|
@ -96,22 +96,32 @@ export function Viewer({
|
||||||
viewerHeight: window.innerHeight
|
viewerHeight: window.innerHeight
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mainContainer = FindContainerById(current.containers, current.mainContainer);
|
||||||
|
|
||||||
|
if (mainContainer === undefined) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
UseSVGAutoResizerOnWindowResize(isLeftSidebarOpen, isRightSidebarOpen, setViewer);
|
UseSVGAutoResizerOnWindowResize(isLeftSidebarOpen, isRightSidebarOpen, setViewer);
|
||||||
UseSVGAutoResizerOnSidebar(isLeftSidebarOpen, isRightSidebarOpen, setViewer);
|
UseSVGAutoResizerOnSidebar(isLeftSidebarOpen, isRightSidebarOpen, setViewer);
|
||||||
|
|
||||||
if (USE_EXPERIMENTAL_CANVAS_API) {
|
if (USE_EXPERIMENTAL_CANVAS_API) {
|
||||||
function Draw(ctx: CanvasRenderingContext2D, frameCount: number, scale: number, translatePos: IPoint): void {
|
function Draw(ctx: CanvasRenderingContext2D, frameCount: number, scale: number, translatePos: IPoint): void {
|
||||||
const topDim = current.mainContainer.properties.y;
|
if (mainContainer === undefined) {
|
||||||
const leftDim = current.mainContainer.properties.x;
|
return;
|
||||||
const rightDim = current.mainContainer.properties.x + current.mainContainer.properties.width;
|
}
|
||||||
const bottomDim = current.mainContainer.properties.y + current.mainContainer.properties.height;
|
|
||||||
|
const topDim = mainContainer.properties.y;
|
||||||
|
const leftDim = mainContainer.properties.x;
|
||||||
|
const rightDim = mainContainer.properties.x + mainContainer.properties.width;
|
||||||
|
const bottomDim = mainContainer.properties.y + mainContainer.properties.height;
|
||||||
|
|
||||||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.setTransform(scale, 0, 0, scale, translatePos.x, translatePos.y);
|
ctx.setTransform(scale, 0, 0, scale, translatePos.x, translatePos.y);
|
||||||
ctx.fillStyle = '#000000';
|
ctx.fillStyle = '#000000';
|
||||||
|
|
||||||
const it = MakeRecursionDFSIterator(current.mainContainer, 0, [0, 0]);
|
const it = MakeRecursionDFSIterator(mainContainer, current.containers, 0, [0, 0]);
|
||||||
for (const { container, depth, currentTransform } of it) {
|
for (const { container, depth, currentTransform } of it) {
|
||||||
const [x, y] = [
|
const [x, y] = [
|
||||||
container.properties.x + currentTransform[0],
|
container.properties.x + currentTransform[0],
|
||||||
|
@ -130,6 +140,7 @@ export function Viewer({
|
||||||
rightDim,
|
rightDim,
|
||||||
depth,
|
depth,
|
||||||
scale,
|
scale,
|
||||||
|
current.containers,
|
||||||
container,
|
container,
|
||||||
currentTransform
|
currentTransform
|
||||||
);
|
);
|
||||||
|
@ -160,13 +171,14 @@ export function Viewer({
|
||||||
className={marginClasses}
|
className={marginClasses}
|
||||||
viewerWidth={viewer.viewerWidth}
|
viewerWidth={viewer.viewerWidth}
|
||||||
viewerHeight={viewer.viewerHeight}
|
viewerHeight={viewer.viewerHeight}
|
||||||
width={current.mainContainer?.properties.width}
|
width={mainContainer.properties.width}
|
||||||
height={current.mainContainer?.properties.height}
|
height={mainContainer.properties.height}
|
||||||
|
containers={current.containers}
|
||||||
selected={selectedContainer}
|
selected={selectedContainer}
|
||||||
symbols={current.symbols}
|
symbols={current.symbols}
|
||||||
selectContainer={selectContainer}
|
selectContainer={selectContainer}
|
||||||
>
|
>
|
||||||
{current.mainContainer}
|
{mainContainer}
|
||||||
</SVG>
|
</SVG>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -188,6 +200,7 @@ function RenderDimensions(
|
||||||
rightDim: number,
|
rightDim: number,
|
||||||
depth: number,
|
depth: number,
|
||||||
scale: number,
|
scale: number,
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
container: IContainerModel,
|
container: IContainerModel,
|
||||||
currentTransform: [number, number]
|
currentTransform: [number, number]
|
||||||
): void {
|
): void {
|
||||||
|
@ -197,7 +210,7 @@ function RenderDimensions(
|
||||||
const containerBottomDim = bottomDim + (DIMENSION_MARGIN * (depth + 1)) / scale;
|
const containerBottomDim = bottomDim + (DIMENSION_MARGIN * (depth + 1)) / scale;
|
||||||
const containerRightDim = rightDim + (DIMENSION_MARGIN * (depth + 1)) / scale;
|
const containerRightDim = rightDim + (DIMENSION_MARGIN * (depth + 1)) / scale;
|
||||||
const dimMapped = [containerLeftDim, containerBottomDim, containerTopDim, containerRightDim];
|
const dimMapped = [containerLeftDim, containerBottomDim, containerTopDim, containerRightDim];
|
||||||
AddDimensions(ctx, container, dimMapped, currentTransform, scale, depth);
|
AddDimensions(ctx, containers, container, dimMapped, currentTransform, scale, depth);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -174,7 +174,7 @@ function AppendContainer(root: Element | Document,
|
||||||
const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep);
|
const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep);
|
||||||
const currentState = history[editorState.historyCurrentStep];
|
const currentState = history[editorState.historyCurrentStep];
|
||||||
|
|
||||||
const parent = FindContainerById(currentState.mainContainer, parentId);
|
const parent = FindContainerById(currentState.containers, parentId);
|
||||||
|
|
||||||
const newHistory = AddContainerAction(
|
const newHistory = AddContainerAction(
|
||||||
parent?.children.length ?? 0,
|
parent?.children.length ?? 0,
|
||||||
|
@ -203,7 +203,7 @@ function AppendContainerToSelectedContainer(root: Element | Document,
|
||||||
const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep);
|
const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep);
|
||||||
const currentState = history[editorState.historyCurrentStep];
|
const currentState = history[editorState.historyCurrentStep];
|
||||||
|
|
||||||
const selected = FindContainerById(currentState.mainContainer, currentState.selectedContainerId);
|
const selected = FindContainerById(currentState.containers, currentState.selectedContainerId);
|
||||||
|
|
||||||
const newHistory = AddContainerToSelectedContainerAction(
|
const newHistory = AddContainerToSelectedContainerAction(
|
||||||
type,
|
type,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { IContainerProperties } from './IContainerProperties';
|
import { IContainerProperties } from './IContainerProperties';
|
||||||
|
|
||||||
export interface IContainerModel {
|
export interface IContainerModel {
|
||||||
children: IContainerModel[]
|
children: string[]
|
||||||
parent: IContainerModel | null
|
parent: IContainerModel | null
|
||||||
properties: IContainerProperties
|
properties: IContainerProperties
|
||||||
userData: Record<string, string | number>
|
userData: Record<string, string | number>
|
||||||
|
@ -12,7 +12,7 @@ export interface IContainerModel {
|
||||||
* Do not add methods since they will be lost during serialization
|
* Do not add methods since they will be lost during serialization
|
||||||
*/
|
*/
|
||||||
export class ContainerModel implements IContainerModel {
|
export class ContainerModel implements IContainerModel {
|
||||||
public children: IContainerModel[];
|
public children: string[];
|
||||||
public parent: IContainerModel | null;
|
public parent: IContainerModel | null;
|
||||||
public properties: IContainerProperties;
|
public properties: IContainerProperties;
|
||||||
public userData: Record<string, string | number>;
|
public userData: Record<string, string | number>;
|
||||||
|
@ -20,7 +20,7 @@ export class ContainerModel implements IContainerModel {
|
||||||
constructor(
|
constructor(
|
||||||
parent: IContainerModel | null,
|
parent: IContainerModel | null,
|
||||||
properties: IContainerProperties,
|
properties: IContainerProperties,
|
||||||
children: IContainerModel[] = [],
|
children: string[] = [],
|
||||||
userData = {}) {
|
userData = {}) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
|
|
|
@ -6,11 +6,12 @@ export interface IHistoryState {
|
||||||
lastAction: string
|
lastAction: string
|
||||||
|
|
||||||
/** Reference to the main container */
|
/** Reference to the main container */
|
||||||
mainContainer: IContainerModel
|
mainContainer: string
|
||||||
|
|
||||||
// TODO: Add hashmap<string, IContainerModel> to optimize FincContainerById from worst O(n) to O(1)
|
// TODO: Add hashmap<string, IContainerModel> to optimize FincContainerById from worst O(n) to O(1)
|
||||||
// TODO: this hashmap will not be serialized, modify it in the replacer and reviver in saveload.ts + worker.js
|
// TODO: this hashmap will not be serialized, modify it in the replacer and reviver in saveload.ts + worker.js
|
||||||
// TODO: Update addContainers and deleteContainer to update the hashmap
|
// TODO: Update addContainers and deleteContainer to update the hashmap
|
||||||
|
containers: Map<string, IContainerModel>
|
||||||
|
|
||||||
/** Id of the selected container */
|
/** Id of the selected container */
|
||||||
selectedContainerId: string
|
selectedContainerId: string
|
||||||
|
|
|
@ -124,6 +124,8 @@ export function GetDefaultEditorState(configuration: IConfiguration): IEditorSta
|
||||||
null,
|
null,
|
||||||
mainContainerConfig
|
mainContainerConfig
|
||||||
);
|
);
|
||||||
|
const containers = new Map<string, IContainerModel>();
|
||||||
|
containers.set(mainContainer.properties.id, mainContainer);
|
||||||
|
|
||||||
const typeCounters = {};
|
const typeCounters = {};
|
||||||
(typeCounters as any)[mainContainer.properties.type] = 0;
|
(typeCounters as any)[mainContainer.properties.type] = 0;
|
||||||
|
@ -133,7 +135,8 @@ export function GetDefaultEditorState(configuration: IConfiguration): IEditorSta
|
||||||
history: [
|
history: [
|
||||||
{
|
{
|
||||||
lastAction: '',
|
lastAction: '',
|
||||||
mainContainer,
|
mainContainer: mainContainer.properties.id,
|
||||||
|
containers,
|
||||||
selectedContainerId: mainContainer.properties.id,
|
selectedContainerId: mainContainer.properties.id,
|
||||||
typeCounters,
|
typeCounters,
|
||||||
symbols: new Map(),
|
symbols: new Map(),
|
||||||
|
@ -175,6 +178,13 @@ export const DEFAULT_CONFIG: IConfiguration = {
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_CONTAINER_STYLE = {
|
||||||
|
stroke: 'black',
|
||||||
|
fillOpacity: 1,
|
||||||
|
fill: 'white',
|
||||||
|
strokeWidth: 2
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default Main container properties
|
* Default Main container properties
|
||||||
*/
|
*/
|
||||||
|
@ -203,10 +213,7 @@ export const DEFAULT_MAINCONTAINER_PROPS: IContainerProperties = {
|
||||||
showDimensionWithMarks: [Position.Down, Position.Right],
|
showDimensionWithMarks: [Position.Down, Position.Right],
|
||||||
markPosition: [],
|
markPosition: [],
|
||||||
warning: '',
|
warning: '',
|
||||||
style: {
|
style: DEFAULT_CONTAINER_STYLE
|
||||||
stroke: 'black',
|
|
||||||
fillOpacity: 0
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -256,7 +263,7 @@ export function GetDefaultContainerProps(type: string,
|
||||||
showDimensionWithMarks: containerConfig.ShowDimensionWithMarks ?? [],
|
showDimensionWithMarks: containerConfig.ShowDimensionWithMarks ?? [],
|
||||||
warning: '',
|
warning: '',
|
||||||
customSVG: containerConfig.CustomSVG,
|
customSVG: containerConfig.CustomSVG,
|
||||||
style: structuredClone(containerConfig.Style),
|
style: Object.assign(structuredClone(DEFAULT_CONTAINER_STYLE), structuredClone(containerConfig.Style)),
|
||||||
userData: structuredClone(containerConfig.UserData)
|
userData: structuredClone(containerConfig.UserData)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,22 @@
|
||||||
import { IContainerModel } from '../Interfaces/IContainerModel';
|
import { IContainerModel } from '../Interfaces/IContainerModel';
|
||||||
|
|
||||||
|
export function * MakeChildrenIterator(containers: Map<string, IContainerModel>, childrenIds: string[]): Generator<IContainerModel, void, unknown> {
|
||||||
|
for (const childId of childrenIds) {
|
||||||
|
const child = FindContainerById(containers, childId);
|
||||||
|
|
||||||
|
if (child === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield child;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a Generator iterating of over the children depth-first
|
* Returns a Generator iterating of over the children depth-first
|
||||||
*/
|
*/
|
||||||
export function * MakeDFSIterator(root: IContainerModel, enableHideChildrenInTreeview = false): Generator<IContainerModel, void, unknown> {
|
export function * MakeDFSIterator(root: IContainerModel, containers: Map<string, IContainerModel>, enableHideChildrenInTreeview = false): Generator<IContainerModel, void, unknown> {
|
||||||
const queue: IContainerModel[] = [root];
|
const queue: IContainerModel[] = [root];
|
||||||
const visited = new Set<IContainerModel>(queue);
|
const visited = new Set<IContainerModel>(queue);
|
||||||
while (queue.length > 0) {
|
while (queue.length > 0) {
|
||||||
|
@ -16,8 +29,9 @@ export function * MakeDFSIterator(root: IContainerModel, enableHideChildrenInTre
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = container.children.length - 1; i >= 0; i--) {
|
for (let i = container.children.length - 1; i >= 0; i--) {
|
||||||
const child = container.children[i];
|
const childId = container.children[i];
|
||||||
if (visited.has(child)) {
|
const child = FindContainerById(containers, childId);
|
||||||
|
if (child === undefined || visited.has(child)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
visited.add(child);
|
visited.add(child);
|
||||||
|
@ -38,7 +52,7 @@ export interface ContainerAndDepthAndTransform extends ContainerAndDepth {
|
||||||
/**
|
/**
|
||||||
* Returns a Generator iterating of over the children depth-first
|
* Returns a Generator iterating of over the children depth-first
|
||||||
*/
|
*/
|
||||||
export function * MakeBFSIterator(root: IContainerModel): Generator<ContainerAndDepth, void, unknown> {
|
export function * MakeBFSIterator(root: IContainerModel, containers: Map<string, IContainerModel>): Generator<ContainerAndDepth, void, unknown> {
|
||||||
const queue: IContainerModel[] = [root];
|
const queue: IContainerModel[] = [root];
|
||||||
let depth = 0;
|
let depth = 0;
|
||||||
while (queue.length > 0) {
|
while (queue.length > 0) {
|
||||||
|
@ -51,7 +65,13 @@ export function * MakeBFSIterator(root: IContainerModel): Generator<ContainerAnd
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let i = container.children.length - 1; i >= 0; i--) {
|
for (let i = container.children.length - 1; i >= 0; i--) {
|
||||||
const child = container.children[i];
|
const childId = container.children[i];
|
||||||
|
const child = FindContainerById(containers, childId);
|
||||||
|
|
||||||
|
if (child === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
queue.push(child);
|
queue.push(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,6 +81,7 @@ export function * MakeBFSIterator(root: IContainerModel): Generator<ContainerAnd
|
||||||
|
|
||||||
export function * MakeRecursionDFSIterator(
|
export function * MakeRecursionDFSIterator(
|
||||||
root: IContainerModel | null,
|
root: IContainerModel | null,
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
depth: number,
|
depth: number,
|
||||||
currentTransform: [number, number],
|
currentTransform: [number, number],
|
||||||
enableHideChildrenInTreeview: boolean = false
|
enableHideChildrenInTreeview: boolean = false
|
||||||
|
@ -79,9 +100,16 @@ export function * MakeRecursionDFSIterator(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const container of root.children) {
|
for (const containerId of root.children) {
|
||||||
|
const container = FindContainerById(containers, containerId);
|
||||||
|
|
||||||
|
if (container === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
yield * MakeRecursionDFSIterator(
|
yield * MakeRecursionDFSIterator(
|
||||||
container,
|
container,
|
||||||
|
containers,
|
||||||
depth + 1,
|
depth + 1,
|
||||||
[
|
[
|
||||||
currentTransform[0] + root.properties.x,
|
currentTransform[0] + root.properties.x,
|
||||||
|
@ -185,8 +213,15 @@ export function ApplyParentTransform(
|
||||||
return [x, y];
|
return [x, y];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FindContainerById(root: IContainerModel, id: string): IContainerModel | undefined {
|
/**
|
||||||
const it = MakeDFSIterator(root);
|
* Returns the container by id
|
||||||
|
* @deprecated Please use FindContainerById
|
||||||
|
* @param root Root of the container tree
|
||||||
|
* @param id Id of the container to find
|
||||||
|
* @returns The container found or undefined if not found
|
||||||
|
*/
|
||||||
|
export function FindContainerByIdDFS(root: IContainerModel, containers: Map<string, IContainerModel>, id: string): IContainerModel | undefined {
|
||||||
|
const it = MakeDFSIterator(root, containers);
|
||||||
for (const container of it) {
|
for (const container of it) {
|
||||||
if (container.properties.id === id) {
|
if (container.properties.id === id) {
|
||||||
return container;
|
return container;
|
||||||
|
@ -195,6 +230,17 @@ export function FindContainerById(root: IContainerModel, id: string): IContainer
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the container by id
|
||||||
|
* For now, does the same as containers.get(id)
|
||||||
|
* @param containers Map of containers
|
||||||
|
* @param id id of container
|
||||||
|
* @returns Container by id
|
||||||
|
*/
|
||||||
|
export function FindContainerById(containers: Map<string, IContainerModel>, id: string): IContainerModel | undefined {
|
||||||
|
return containers.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
export interface IPair<T> {
|
export interface IPair<T> {
|
||||||
cur: T
|
cur: T
|
||||||
next: T
|
next: T
|
||||||
|
|
|
@ -32,15 +32,22 @@ export function ReviveState(state: IHistoryState): void {
|
||||||
for (const symbol of state.symbols.values()) {
|
for (const symbol of state.symbols.values()) {
|
||||||
symbol.linkedContainers = new Set(symbol.linkedContainers);
|
symbol.linkedContainers = new Set(symbol.linkedContainers);
|
||||||
}
|
}
|
||||||
|
state.containers = new Map(state.containers);
|
||||||
|
|
||||||
const it = MakeDFSIterator(state.mainContainer);
|
const root = FindContainerById(state.containers, state.mainContainer);
|
||||||
|
|
||||||
|
if (root === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const it = MakeDFSIterator(root, state.containers);
|
||||||
for (const container of it) {
|
for (const container of it) {
|
||||||
const parentId = container.properties.parentId;
|
const parentId = container.properties.parentId;
|
||||||
if (parentId === null) {
|
if (parentId === null) {
|
||||||
container.parent = null;
|
container.parent = null;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const parent = FindContainerById(state.mainContainer, parentId);
|
const parent = FindContainerById(state.containers, parentId);
|
||||||
if (parent === undefined) {
|
if (parent === undefined) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -54,6 +61,10 @@ export function GetCircularReplacer(): (key: any, value: object | Map<string, an
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (key === 'containers') {
|
||||||
|
return Array.from((value as Map<string, any>).entries());
|
||||||
|
}
|
||||||
|
|
||||||
if (key === 'symbols') {
|
if (key === 'symbols') {
|
||||||
return Array.from((value as Map<string, any>).entries());
|
return Array.from((value as Map<string, any>).entries());
|
||||||
}
|
}
|
||||||
|
|
3
src/vite-env.d.ts
vendored
3
src/vite-env.d.ts
vendored
|
@ -1,9 +1,6 @@
|
||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {
|
||||||
readonly VITE_API_FETCH_URL: string
|
|
||||||
readonly VITE_API_SET_CONTAINER_LIST_URL: string
|
|
||||||
readonly VITE_API_GET_FEEDBACK_URL: string
|
|
||||||
// more env variables...
|
// more env variables...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx"
|
"jsx": "react-jsx"
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src", "public/svgld-settings.d.ts", "public/svgld-settings.ts"],
|
||||||
"exclude": ["test-server"],
|
"exclude": ["test-server"],
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue