Merge branch 'master' into eslint-fixes

This commit is contained in:
Eric NGUYEN 2023-02-23 13:04:19 +01:00
commit f1c97b25d1
38 changed files with 1456 additions and 480 deletions

View file

@ -3,6 +3,7 @@
"private": true,
"version": "v1.0.0",
"type": "module",
"postinstall": "npx patch-package",
"scripts": {
"d": "mprocs",
"dev": "vite",
@ -22,10 +23,11 @@
"interweave": "^13.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-svg-pan-zoom": "^3.11.0",
"react-svg-pan-zoom": "^3.12.1",
"react-window": "^1.8.8",
"sweetalert2": "^11.7.1",
"sweetalert2-react-content": "^5.0.7"
"sweetalert2-react-content": "^5.0.7",
"transformation-matrix": "^2.14.0"
},
"devDependencies": {
"@testing-library/dom": "^8.20.0",
@ -60,5 +62,10 @@
"typescript": "^4.9.5",
"vite": "^4.1.1",
"vitest": "^0.28.4"
},
"pnpm": {
"patchedDependencies": {
"@types/react-svg-pan-zoom@3.3.5": "patches/@types__react-svg-pan-zoom@3.3.5.patch"
}
}
}

View file

@ -0,0 +1,17 @@
diff --git a/node_modules/@types/react-svg-pan-zoom/index.d.ts b/node_modules/@types/react-svg-pan-zoom/index.d.ts
index a57d545..83ace9f 100644
--- a/node_modules/@types/react-svg-pan-zoom/index.d.ts
+++ b/node_modules/@types/react-svg-pan-zoom/index.d.ts
@@ -256,7 +256,11 @@ export function zoom(value: Value, SVGPointX: number, SVGPointY: number, scaleFa
export function fitSelection(
value: Value, selectionSVGPointX: number, selectionSVGPointY: number, selectionWidth: number, selectionHeight: number): Value;
-export function fitToViewer(value: Value): Value;
+export function fitToViewer(
+ value: Value,
+ SVGAlignX?: typeof ALIGN_CENTER | typeof ALIGN_LEFT | typeof ALIGN_RIGHT | undefined,
+ SVGAlignY?: typeof ALIGN_CENTER | typeof ALIGN_TOP | typeof ALIGN_BOTTOM | undefined
+): Value;
export function zoomOnViewerCenter(value: Value, scaleFactor: number): Value;

View file

@ -0,0 +1,17 @@
diff --git a/index.d.ts b/index.d.ts
index a57d545d33b2798024b9762d3d3513e58a38e19d..83ace9fc85b7354e128948402a50e00083eacd8c 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -256,7 +256,11 @@ export function zoom(value: Value, SVGPointX: number, SVGPointY: number, scaleFa
export function fitSelection(
value: Value, selectionSVGPointX: number, selectionSVGPointY: number, selectionWidth: number, selectionHeight: number): Value;
-export function fitToViewer(value: Value): Value;
+export function fitToViewer(
+ value: Value,
+ SVGAlignX?: typeof ALIGN_CENTER | typeof ALIGN_LEFT | typeof ALIGN_RIGHT | undefined,
+ SVGAlignY?: typeof ALIGN_CENTER | typeof ALIGN_TOP | typeof ALIGN_BOTTOM | undefined
+): Value;
export function zoomOnViewerCenter(value: Value, scaleFactor: number): Value;

23
pnpm-lock.yaml generated
View file

@ -1,5 +1,10 @@
lockfileVersion: 5.4
patchedDependencies:
'@types/react-svg-pan-zoom@3.3.5':
hash: kv3ctd73j5hnzcxdc2ceiq5wuy
path: patches/@types__react-svg-pan-zoom@3.3.5.patch
specifiers:
'@heroicons/react': ^2.0.14
'@react-hook/size': ^2.1.2
@ -33,12 +38,13 @@ specifiers:
postcss: ^8.4.21
react: ^18.2.0
react-dom: ^18.2.0
react-svg-pan-zoom: ^3.11.0
react-svg-pan-zoom: ^3.12.1
react-window: ^1.8.8
sass: ^1.58.0
sweetalert2: ^11.7.1
sweetalert2-react-content: ^5.0.7
tailwindcss: ^3.2.4
transformation-matrix: ^2.14.0
typescript: ^4.9.5
vite: ^4.1.1
vitest: ^0.28.4
@ -49,10 +55,11 @@ dependencies:
interweave: 13.0.0_react@18.2.0
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
react-svg-pan-zoom: 3.11.0_react@18.2.0
react-svg-pan-zoom: 3.12.1
react-window: 1.8.8_biqbaboplfbrettd7655fr4n2y
sweetalert2: 11.7.1
sweetalert2-react-content: 5.0.7_5cbezu6w3tvev2ldv5vdmnpfca
transformation-matrix: 2.14.0
devDependencies:
'@testing-library/dom': 8.20.0
@ -61,7 +68,7 @@ devDependencies:
'@testing-library/user-event': 14.4.3_yxlyej73nftwmh2fiao7paxmlm
'@types/react': 18.0.27
'@types/react-dom': 18.0.10
'@types/react-svg-pan-zoom': 3.3.5
'@types/react-svg-pan-zoom': 3.3.5_kv3ctd73j5hnzcxdc2ceiq5wuy
'@types/react-window': 1.8.5
'@typescript-eslint/eslint-plugin': 5.51.0_b635kmla6dsb4frxfihkw4m47e
'@typescript-eslint/parser': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64
@ -927,11 +934,12 @@ packages:
'@types/react': 18.0.27
dev: true
/@types/react-svg-pan-zoom/3.3.5:
/@types/react-svg-pan-zoom/3.3.5_kv3ctd73j5hnzcxdc2ceiq5wuy:
resolution: {integrity: sha512-W8GRFCDy7raSDr5OXGjSyvX5KmdWlIQfv0NLa1jfAYVUO4ClVbgorWeAAom7nY3Pl+4h9blXE1Bnu2CW1iMEvQ==}
dependencies:
'@types/react': 18.0.27
dev: true
patched: true
/@types/react-window/1.8.5:
resolution: {integrity: sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==}
@ -3453,13 +3461,10 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/react-svg-pan-zoom/3.11.0_react@18.2.0:
resolution: {integrity: sha512-xK2tpfp4YksHOfyMZH5zXP52ARLSBgkoJgWNJmJ1B+6O1tkuf23TQp7Q4m9GG5IRSK5KWO0JEGEWlNYG9+iiug==}
peerDependencies:
react: '>=17.0.0'
/react-svg-pan-zoom/3.12.1:
resolution: {integrity: sha512-ug1LHCN5qed56C64xFypr/ClajuMFkig1OKvwJrIgGeSyHOjWM7XGgSgeP3IfHAkNw8QEc6a31ggZRpTijWYRw==}
dependencies:
prop-types: 15.8.1
react: 18.2.0
transformation-matrix: 2.14.0
dev: false

View file

@ -1,16 +1,85 @@
import React, { useEffect, useRef } from 'react';
import { IPoint } from '../../Interfaces/IPoint';
import { type IPoint } from '../../Interfaces/IPoint';
import { BAR_WIDTH } from '../Bar/Bar';
import { SelectorMode } from '../SVG/SVG';
import { type DrawParams } from '../Viewer/Viewer';
import { RenderContainers, RenderSymbols } from './Renderer';
import { RenderContainerSelector } from './SelectorContainer';
import { RenderSymbolSelector } from './SelectorSymbol';
interface ICanvasProps {
className?: string
width: number
height: number
draw: (context: CanvasRenderingContext2D, frameCount: number, scale: number, translatePos: IPoint) => void
className?: string
style?: React.CSSProperties
drawParams: DrawParams
};
function UseCanvas(draw: (context: CanvasRenderingContext2D, frameCount: number, scale: number, translatePos: IPoint) => void): React.RefObject<HTMLCanvasElement> {
function Draw(
ctx: CanvasRenderingContext2D,
frameCount: number,
scale: number,
translatePos: IPoint,
{
mainContainer,
selectorMode,
selectedContainer,
selectedSymbol,
containers,
symbols
}: DrawParams
): void {
if (mainContainer === undefined) {
return;
}
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.save();
ctx.setTransform(scale, 0, 0, scale, translatePos.x, translatePos.y);
ctx.fillStyle = '#000000';
// Draw containers and symbol dimensions
RenderContainers(
ctx,
mainContainer,
containers,
leftDim, bottomDim, topDim, rightDim, scale);
// Draw symbols and symbol dimensions
RenderSymbols(ctx, symbols, scale);
// Draw selector
switch (selectorMode) {
case SelectorMode.Containers:
RenderContainerSelector(ctx, frameCount, {
containers,
scale,
selected: selectedContainer
});
break;
case SelectorMode.Symbols:
RenderSymbolSelector(ctx, frameCount, {
symbols,
scale,
selected: selectedSymbol
});
break;
}
ctx.restore();
}
function UseCanvas(
draw: (context: CanvasRenderingContext2D,
frameCount: number,
scale: number,
translatePos: IPoint) => void
): React.RefObject<HTMLCanvasElement> {
const canvasRef = useRef<HTMLCanvasElement>(null);
const frameCount = useRef(0);
const translatePos = useRef({
@ -123,8 +192,21 @@ interface Viewer {
viewerHeight: number
}
export function Canvas({ width, height, draw, style, className }: ICanvasProps): JSX.Element {
const canvasRef = UseCanvas(draw);
export function Canvas({
className,
width,
height,
style,
drawParams
}: ICanvasProps): JSX.Element {
const canvasRef = UseCanvas((
...CanvasProps
) => {
Draw(
...CanvasProps,
drawParams
);
});
const [{ viewerWidth, viewerHeight }, setViewer] = React.useState<Viewer>({
viewerWidth: width,

View file

@ -0,0 +1,18 @@
import { type IContainerModel } from '../../Interfaces/IContainerModel';
export function RenderContainer(
ctx: CanvasRenderingContext2D,
container: IContainerModel,
x: number,
y: number
): void {
ctx.save();
ctx.strokeStyle = container.properties.style?.stroke ?? '#000000';
ctx.fillStyle = container.properties.style?.fill ?? '#000000';
ctx.lineWidth = Number(container.properties.style?.strokeWidth ?? 1);
ctx.globalAlpha = Number(container.properties.style?.fillOpacity ?? 1);
ctx.fillRect(x, y, container.properties.width, container.properties.height);
ctx.globalAlpha = Number(container.properties.style?.strokeOpacity ?? 1);
ctx.strokeRect(x, y, container.properties.width, container.properties.height);
ctx.restore();
}

View file

@ -1,4 +1,5 @@
import { NOTCHES_LENGTH } from '../../utils/default';
import { IDimensionStyle } from '../SVG/Elements/Dimension';
interface IDimensionProps {
id: string
@ -7,7 +8,7 @@ interface IDimensionProps {
xEnd: number
yEnd: number
text: string
strokeWidth: number
style: IDimensionStyle
scale?: number
}
@ -26,8 +27,11 @@ function ApplyParametric(x0: number, t: number, vx: number): number {
export function RenderDimension(ctx: CanvasRenderingContext2D, props: IDimensionProps): void {
const scale = props.scale ?? 1;
const strokeStyle = 'black';
const lineWidth = 2 / scale;
const strokeStyle = props.style.color ?? 'black';
const lineWidth = (props.style.width ?? 2) / scale;
const dashArray: number[] = props.style.dashArray?.split(' ')
.flatMap(array => array.split(','))
.map(stringValue => parseInt(stringValue)) ?? [];
/// We need to find the points of the notches
// Get the vector of the line
@ -59,6 +63,7 @@ export function RenderDimension(ctx: CanvasRenderingContext2D, props: IDimension
ctx.lineWidth = lineWidth;
ctx.strokeStyle = strokeStyle;
ctx.fillStyle = strokeStyle;
ctx.setLineDash(dashArray);
ctx.moveTo(startTopX, startTopY);
ctx.lineTo(startBottomX, startBottomY);
ctx.stroke();
@ -68,6 +73,7 @@ export function RenderDimension(ctx: CanvasRenderingContext2D, props: IDimension
ctx.moveTo(endTopX, endTopY);
ctx.lineTo(endBottomX, endBottomY);
ctx.stroke();
ctx.setLineDash([]);
const textX = (props.xStart + props.xEnd) / 2;
const textY = (props.yStart + props.yEnd) / 2;
ctx.font = `${16 / scale}px Verdana`;

View file

@ -1,14 +1,19 @@
import { Orientation } from '../../Enums/Orientation';
import { Position } from '../../Enums/Position';
import { IContainerModel } from '../../Interfaces/IContainerModel';
import { SHOW_SELF_DIMENSIONS, SHOW_BORROWER_DIMENSIONS, SHOW_CHILDREN_DIMENSIONS } from '../../utils/default';
import { type IContainerModel } from '../../Interfaces/IContainerModel';
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import {
SHOW_SELF_DIMENSIONS,
SHOW_BORROWER_DIMENSIONS,
SHOW_CHILDREN_DIMENSIONS,
DIMENSION_MARGIN,
SHOW_SELF_MARGINS_DIMENSIONS, DEFAULT_DIMENSION_SYMBOL_STYLE
} from '../../utils/default';
import { FindContainerById, MakeRecursionDFSIterator, Pairwise } from '../../utils/itertools';
import { TransformX, TransformY } from '../../utils/svg';
import { RenderDimension } from './Dimension';
const MODULE_STROKE_WIDTH = 1;
export function AddDimensions(
export function AddContainerDimensions(
ctx: CanvasRenderingContext2D,
containers: Map<string, IContainerModel>,
container: IContainerModel,
@ -18,7 +23,8 @@ export function AddDimensions(
depth: number
): void {
ctx.beginPath();
if (SHOW_SELF_DIMENSIONS && container.properties.dimensionOptions.selfDimensions.positions.length > 0) {
if (SHOW_SELF_DIMENSIONS &&
container.properties.dimensionOptions.selfDimensions.positions.length > 0) {
ActionByPosition(
ctx,
dimMapped,
@ -32,7 +38,24 @@ export function AddDimensions(
);
}
if (SHOW_BORROWER_DIMENSIONS && container.properties.dimensionOptions.dimensionWithMarks.positions.length > 0) {
if (SHOW_SELF_MARGINS_DIMENSIONS &&
container.properties.dimensionOptions.selfMarginsDimensions.positions.length > 0) {
ActionByPosition(
ctx,
dimMapped,
container.properties.dimensionOptions.selfMarginsDimensions.positions,
AddHorizontalSelfMarginsDimension,
AddVerticalSelfMarginDimension,
[
container,
currentTransform,
scale
]
);
}
if (SHOW_BORROWER_DIMENSIONS &&
container.properties.dimensionOptions.dimensionWithMarks.positions.length > 0) {
ActionByPosition(
ctx,
dimMapped,
@ -48,7 +71,9 @@ export function AddDimensions(
);
}
if (SHOW_CHILDREN_DIMENSIONS && container.properties.dimensionOptions.childrenDimensions.positions.length > 0 && container.children.length >= 2) {
if (SHOW_CHILDREN_DIMENSIONS &&
container.properties.dimensionOptions.childrenDimensions.positions.length > 0 &&
container.children.length >= 2) {
ActionByPosition(
ctx,
dimMapped,
@ -64,8 +89,33 @@ export function AddDimensions(
}
}
export function AddSymbolDimensions(
ctx: CanvasRenderingContext2D,
symbol: ISymbolModel,
scale: number,
depth: number
): void {
if (symbol.isVertical) {
AddVerticalSymbolDimension(
ctx,
symbol,
scale,
depth
);
return;
}
AddHorizontalSymbolDimension(
ctx,
symbol,
scale,
depth
);
}
/**
* Fonction that call another function given the positions
* @param ctx
* @param dimMapped Position mapped depending on the Position enum in order:
* [0:left, 1:bottom, 2:up, 3:right]
* @param positions List of positions
@ -108,6 +158,7 @@ function AddHorizontalChildrenDimension(
scale: number
): void {
const childrenId = `dim-y${yDim.toFixed(0)}-children-${container.properties.id}`;
const style = container.properties.dimensionOptions.childrenDimensions;
const lastChildId = container.children[container.children.length - 1];
const lastChild = FindContainerById(containers, lastChildId);
@ -115,8 +166,14 @@ function AddHorizontalChildrenDimension(
if (lastChild === undefined) {
return;
}
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 xChildrenStart = 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
for (let i = container.children.length - 2; i >= 0; i--) {
@ -152,9 +209,9 @@ function AddHorizontalChildrenDimension(
xEnd: xChildrenEnd + offset,
yStart: yDim,
yEnd: yDim,
strokeWidth: MODULE_STROKE_WIDTH,
text: textChildren,
scale
scale,
style
});
}
@ -168,6 +225,7 @@ function AddVerticalChildrenDimension(
scale: number
): void {
const childrenId = `dim-x${xDim.toFixed(0)}-children-${container.properties.id}`;
const style = container.properties.dimensionOptions.childrenDimensions;
const lastChildId = container.children[container.children.length - 1];
const lastChild = FindContainerById(containers, lastChildId);
@ -176,7 +234,10 @@ function AddVerticalChildrenDimension(
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 = yChildrenStart;
// Find the min and max
@ -218,9 +279,9 @@ function AddVerticalChildrenDimension(
xEnd: xDim,
yStart: yChildrenStart + offset,
yEnd: yChildrenEnd + offset,
strokeWidth: MODULE_STROKE_WIDTH,
text: textChildren,
scale
scale,
style
});
}
@ -234,6 +295,7 @@ function AddHorizontalBorrowerDimension(
scale: number
): void {
const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform);
const style = container.properties.dimensionOptions.dimensionWithMarks;
const marks = []; // list of vertical lines for the dimension
for (const {
container: childContainer, currentTransform: childCurrentTransform
@ -274,9 +336,9 @@ function AddHorizontalBorrowerDimension(
xEnd: next,
yStart: yDim,
yEnd: yDim,
strokeWidth: MODULE_STROKE_WIDTH,
text: value.toFixed(0),
scale
scale,
style
});
count++;
}
@ -293,6 +355,7 @@ function AddVerticalBorrowerDimension(
scale: number
): void {
const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform);
const style = container.properties.dimensionOptions.dimensionWithMarks;
const marks = []; // list of vertical lines for the dimension
for (const {
container: childContainer, currentTransform: childCurrentTransform
@ -338,9 +401,9 @@ function AddVerticalBorrowerDimension(
xEnd: xDim,
yStart: cur,
yEnd: next,
strokeWidth: MODULE_STROKE_WIDTH,
text: value.toFixed(0),
scale
scale,
style
});
count++;
}
@ -354,6 +417,7 @@ function AddVerticalSelfDimension(
currentTransform: [number, number],
scale: number
): void {
const style = container.properties.dimensionOptions.selfDimensions;
const height = container.properties.height;
const idVert = `dim-x${xDim.toFixed(0)}-${container.properties.id}`;
let yStart = container.properties.y + currentTransform[1] + height;
@ -372,9 +436,9 @@ function AddVerticalSelfDimension(
xEnd: xDim,
yStart,
yEnd,
strokeWidth: MODULE_STROKE_WIDTH,
text: textVert,
scale
scale,
style
});
}
@ -385,6 +449,7 @@ function AddHorizontalSelfDimension(
currentTransform: [number, number],
scale: number
): void {
const style = container.properties.dimensionOptions.selfDimensions;
const width = container.properties.width;
const id = `dim-y${yDim.toFixed(0)}-${container.properties.id}`;
const xStart = container.properties.x + currentTransform[0];
@ -398,8 +463,180 @@ function AddHorizontalSelfDimension(
yStart: yDim,
xEnd,
yEnd: yDim,
strokeWidth: MODULE_STROKE_WIDTH,
text,
scale
scale,
style
});
}
function AddHorizontalSelfMarginsDimension(
ctx: CanvasRenderingContext2D,
yDim: number,
container: IContainerModel,
currentTransform: [number, number],
dimensions: React.ReactNode[],
scale: number
): void {
const style = container.properties.dimensionOptions.selfMarginsDimensions;
const left = container.properties.margin.left;
if (left != null) {
const id = `dim-y-margin-left${yDim.toFixed(0)}-${container.properties.id}`;
const xStart = container.properties.x + currentTransform[0] - left;
const xEnd = xStart + left;
const text = left
.toFixed(0)
.toString();
RenderDimension(ctx, {
id,
xStart,
yStart: yDim,
xEnd,
yEnd: yDim,
text,
scale,
style
});
}
const right = container.properties.margin.right;
if (right != null) {
const id = `dim-y-margin-right${yDim.toFixed(0)}-${container.properties.id}`;
const xStart = container.properties.x + container.properties.width + currentTransform[0];
const xEnd = xStart + right;
const text = right
.toFixed(0)
.toString();
RenderDimension(ctx, {
id,
xStart,
yStart: yDim,
xEnd,
yEnd: yDim,
text,
scale,
style
});
}
}
function AddVerticalSelfMarginDimension(
ctx: CanvasRenderingContext2D,
xDim: number,
isRight: boolean,
container: IContainerModel,
currentTransform: [number, number],
scale: number
): void {
const style = container.properties.dimensionOptions.selfMarginsDimensions;
const top = container.properties.margin.top;
if (top != null) {
const idVert = `dim-x-margin-top${xDim.toFixed(0)}-${container.properties.id}`;
let yStart = container.properties.y + currentTransform[1];
let yEnd = yStart - top;
const textVert = top
.toFixed(0)
.toString();
if (isRight) {
[yStart, yEnd] = [yEnd, yStart];
}
RenderDimension(ctx, {
id: idVert,
xStart: xDim,
yStart,
xEnd: xDim,
yEnd,
text: textVert,
scale,
style
});
}
const bottom = container.properties.margin.bottom;
if (bottom != null) {
const idVert = `dim-x-margin-bottom${xDim.toFixed(0)}-${container.properties.id}`;
let yStart = container.properties.y + container.properties.height + bottom + currentTransform[1];
let yEnd = yStart - bottom;
const textVert = bottom
.toFixed(0)
.toString();
if (isRight) {
[yStart, yEnd] = [yEnd, yStart];
}
RenderDimension(ctx, {
id: idVert,
xStart: xDim,
yStart,
xEnd: xDim,
yEnd,
text: textVert,
scale,
style
});
}
}
function AddHorizontalSymbolDimension(
ctx: CanvasRenderingContext2D,
symbol: ISymbolModel,
scale: number,
depth: number
): void {
const width = TransformX(symbol.offset, symbol.width, symbol.config.PositionReference);
if (width == null || width <= 0) {
return;
}
const id = `dim-y-margin-left${symbol.width.toFixed(0)}-${symbol.id}`;
const offset = (DIMENSION_MARGIN * (depth + 1)) / scale;
const text = width
.toFixed(0)
.toString();
RenderDimension(ctx, {
id,
xStart: 0,
yStart: -offset,
xEnd: width,
yEnd: -offset,
text,
scale,
style: DEFAULT_DIMENSION_SYMBOL_STYLE
});
}
function AddVerticalSymbolDimension(
ctx: CanvasRenderingContext2D,
symbol: ISymbolModel,
scale: number,
depth: number
): void {
const height = TransformY(symbol.offset, symbol.height, symbol.config.PositionReference);
if (height == null || height <= 0) {
return;
}
const id = `dim-y-margin-left${symbol.width.toFixed(0)}-${symbol.id}`;
const offset = (DIMENSION_MARGIN * (depth + 1)) / scale;
const text = height
.toFixed(0)
.toString();
RenderDimension(ctx, {
id,
xStart: -offset,
yStart: height,
xEnd: -offset,
yEnd: 0,
text,
scale,
style: DEFAULT_DIMENSION_SYMBOL_STYLE
});
}

View file

@ -0,0 +1,103 @@
import { type IContainerModel } from '../../Interfaces/IContainerModel';
import { type IHistoryState } from '../../Interfaces/IHistoryState';
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
import { DIMENSION_MARGIN } from '../../utils/default';
import { MakeRecursionDFSIterator } from '../../utils/itertools';
import { RenderContainer } from './Container';
import { AddContainerDimensions, AddSymbolDimensions } from './DimensionLayer';
import { RenderSymbol } from './Symbol';
export function RenderContainers(
ctx: CanvasRenderingContext2D,
root: IContainerModel,
containers: Map<string, IContainerModel>,
leftDim: number,
bottomDim: number,
topDim: number,
rightDim: number,
scale: number
): void {
const it = MakeRecursionDFSIterator(root, containers, 0, [0, 0]);
for (const { container, depth, currentTransform } of it) {
const [x, y] = [
container.properties.x + currentTransform[0],
container.properties.y + currentTransform[1]
];
// Draw container
RenderContainer(ctx, container, x, y);
// Draw dimensions
RenderContainerDimensions(
ctx,
leftDim,
bottomDim,
topDim,
rightDim,
depth,
scale,
containers,
container,
currentTransform
);
}
}
export function RenderContainerDimensions(
ctx: CanvasRenderingContext2D,
leftDim: number,
bottomDim: number,
topDim: number,
rightDim: number,
depth: number,
scale: number,
containers: Map<string, IContainerModel>,
container: IContainerModel,
currentTransform: [number, number]
): void {
ctx.save();
const depthOffset = (DIMENSION_MARGIN * (depth + 1)) / scale;
const containerLeftDim = leftDim - depthOffset;
const containerTopDim = topDim - depthOffset;
const containerBottomDim = bottomDim + depthOffset;
const containerRightDim = rightDim + depthOffset;
const dimMapped = [containerLeftDim, containerBottomDim, containerTopDim, containerRightDim];
AddContainerDimensions(ctx, containers, container, dimMapped, currentTransform, scale, depth);
ctx.restore();
}
export function RenderSymbols(
ctx: CanvasRenderingContext2D,
symbols: Map<string, ISymbolModel>,
scale: number
): void {
symbols.forEach((symbol: ISymbolModel) => {
RenderSymbol(ctx, symbol);
if (!symbol.showDimension) {
return;
}
// TODO: Implement DimensionManager
AddSymbolDimensions(ctx, symbol, scale, 0);
});
}
export function RenderSymbolDimensions(
ctx: CanvasRenderingContext2D,
depth: number,
scale: number,
containers: Map<string, IContainerModel>,
container: IContainerModel,
currentTransform: [number, number]
): void {
ctx.save();
const depthOffset = (DIMENSION_MARGIN * (depth + 1)) / scale;
const containerLeftDim = -depthOffset;
const containerTopDim = -depthOffset;
const containerBottomDim = depthOffset;
const containerRightDim = depthOffset;
const dimMapped = [containerLeftDim, containerBottomDim, containerTopDim, containerRightDim];
AddContainerDimensions(ctx, containers, container, dimMapped, currentTransform, scale, depth);
ctx.restore();
}

View file

@ -1,33 +1,26 @@
import { IContainerModel } from '../../Interfaces/IContainerModel';
import { SHOW_SELECTOR_TEXT } from '../../utils/default';
import { GetAbsolutePosition } from '../../utils/itertools';
import { RemoveMargin } from '../../utils/svg';
interface ISelectorProps {
containers: Map<string, IContainerModel>
selected?: IContainerModel
scale?: number
text: string
x: number
y: number
width: number
height: number
scale: number
}
export function RenderSelector(ctx: CanvasRenderingContext2D, frameCount: number, props: ISelectorProps): void {
if (props.selected === undefined || props.selected === null) {
return;
}
const scale = (props.scale ?? 1);
let [x, y] = GetAbsolutePosition(props.containers, props.selected);
let [width, height] = [
props.selected.properties.width,
props.selected.properties.height
];
({ x, y, width, height } = RemoveMargin(x, y, width, height,
props.selected.properties.margin.left,
props.selected.properties.margin.bottom,
props.selected.properties.margin.top,
props.selected.properties.margin.right
));
export function RenderSelector(
ctx: CanvasRenderingContext2D,
frameCount: number,
{
text,
x,
y,
width,
height,
scale
}: ISelectorProps
): void {
const xText = x + width / 2;
const yText = y + height / 2;
@ -42,7 +35,7 @@ export function RenderSelector(ctx: CanvasRenderingContext2D, frameCount: number
if (SHOW_SELECTOR_TEXT) {
ctx.font = `${16 / scale}px Verdana`;
ctx.textAlign = 'center';
ctx.fillText(props.selected.properties.displayedText, xText, yText);
ctx.fillText(text, xText, yText);
ctx.textAlign = 'left';
}
}

View file

@ -0,0 +1,48 @@
import { type IContainerModel } from '../../Interfaces/IContainerModel';
import { GetAbsolutePosition } from '../../utils/itertools';
import { RemoveMargin } from '../../utils/svg';
import { RenderSelector } from './Selector';
interface ISelectorProps {
containers: Map<string, IContainerModel>
selected?: IContainerModel
scale?: number
}
export function RenderContainerSelector(
ctx: CanvasRenderingContext2D,
frameCount: number,
props: ISelectorProps
): void {
if (props.selected === undefined || props.selected === null) {
return;
}
const scale = (props.scale ?? 1);
let [x, y] = GetAbsolutePosition(props.containers, props.selected);
let [width, height] = [
props.selected.properties.width,
props.selected.properties.height
];
({ x, y, width, height } = RemoveMargin(x, y, width, height,
props.selected.properties.margin.left,
props.selected.properties.margin.bottom,
props.selected.properties.margin.top,
props.selected.properties.margin.right
));
const text = props.selected.properties.displayedText;
RenderSelector(
ctx,
frameCount,
{
text,
x,
y,
width,
height,
scale
}
);
}

View file

@ -0,0 +1,47 @@
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import { SYMBOL_MARGIN } from '../../utils/default';
import { RenderSelector } from './Selector';
interface ISelectorProps {
symbols: Map<string, ISymbolModel>
selected?: ISymbolModel
}
export function RenderSymbolSelector(
ctx: CanvasRenderingContext2D,
frameCount: number,
props: ISelectorProps
): void {
if (props.selected === undefined || props.selected === null) {
return;
}
const [width, height] = [
props.selected.width,
props.selected.height
];
let x, y: number;
if (props.selected.isVertical) {
x = -SYMBOL_MARGIN - props.selected.width;
y = props.selected.offset;
} else {
x = props.selected.offset;
y = -SYMBOL_MARGIN - props.selected.height;
}
const text = props.selected.displayedText;
RenderSelector(
ctx,
frameCount,
{
text,
x,
y,
width,
height,
scale: 1
}
);
}

View file

@ -1,12 +1,11 @@
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
import { DIMENSION_MARGIN } from '../../utils/default';
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import { SYMBOL_MARGIN } from '../../utils/default';
const IMAGE_CACHE = new Map<string, HTMLImageElement>();
export function RenderSymbol(
symbol: ISymbolModel,
ctx: CanvasRenderingContext2D,
scale: number): void {
symbol: ISymbolModel): void {
const href = symbol.config.Image.Base64Image ?? symbol.config.Image.Url;
if (href === undefined) {
@ -19,27 +18,38 @@ export function RenderSymbol(
newImage.src = href;
IMAGE_CACHE.set(href, newImage);
newImage.onload = () => {
DrawImage(ctx, scale, newImage, symbol);
DrawImage(ctx, newImage, symbol);
};
return;
}
DrawImage(ctx, scale, image, symbol);
DrawImage(ctx, image, symbol);
}
function DrawImage(
ctx: CanvasRenderingContext2D,
scale: number,
image: HTMLImageElement,
symbol: ISymbolModel
): void {
let x, y: number;
if (symbol.isVertical) {
x = -SYMBOL_MARGIN - symbol.width;
y = symbol.offset;
} else {
x = symbol.offset;
y = -SYMBOL_MARGIN - symbol.height;
}
ctx.save();
ctx.fillStyle = '#000000';
const width = symbol.width;
const height = symbol.height;
ctx.drawImage(
image,
symbol.x,
-DIMENSION_MARGIN,
symbol.width,
symbol.height
x,
y,
width,
height
);
ctx.restore();
}

View file

@ -5,7 +5,8 @@ import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import {
SHOW_BORROWER_DIMENSIONS,
SHOW_CHILDREN_DIMENSIONS,
SHOW_SELF_DIMENSIONS, SHOW_SELF_MARGINS_DIMENSIONS
SHOW_SELF_DIMENSIONS,
SHOW_SELF_MARGINS_DIMENSIONS
} from '../../utils/default';
import {
ApplyWidthMargin,
@ -27,8 +28,12 @@ import { OrientationSelector } from '../RadioGroupButtons/OrientationSelector';
import { OrientationCheckboxes } from '../CheckboxGroupButtons/OrientationCheckboxes';
import { PositionCheckboxes } from '../CheckboxGroupButtons/PositionCheckboxes';
import { Category } from '../Category/Category';
import { Orientation } from '../../Enums/Orientation';
import { FindContainerById } from '../../utils/itertools';
import { type IContainerModel } from '../../Interfaces/IContainerModel';
interface IContainerFormProps {
containers: Map<string, IContainerModel>
properties: IContainerProperties
symbols: Map<string, ISymbolModel>
onChange: (key: string, value: string | number | boolean | number[], type?: PropertyType) => void
@ -36,6 +41,8 @@ interface IContainerFormProps {
export function ContainerForm(props: IContainerFormProps): JSX.Element {
const categoryHeight = 'h-11';
const parent = FindContainerById(props.containers, props.properties.parentId);
const isVertical = parent?.properties.orientation === Orientation.Vertical;
return (
<div className='grid grid-cols-1 gap-y-4 items-center'>
<TextInputGroup
@ -185,8 +192,14 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type='number'
min={props.properties.minWidth}
max={props.properties.maxWidth}
value={(RemoveWidthMargin(props.properties.width, props.properties.margin.left, props.properties.margin.right)).toString()}
onChange={(value) => { props.onChange('width', ApplyWidthMargin(Number(value), props.properties.margin.left, props.properties.margin.right)); }}
value={(RemoveWidthMargin(props.properties.width,
props.properties.margin.left,
props.properties.margin.right)).toString()}
onChange={(value) => {
props.onChange('width', ApplyWidthMargin(Number(value),
props.properties.margin.left,
props.properties.margin.right));
}}
isDisabled={props.properties.isFlex}/>
<TextInputGroup
id={`${props.properties.id}-maxWidth`}
@ -218,8 +231,14 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type='number'
min={props.properties.minHeight}
max={props.properties.maxHeight}
value={(RemoveWidthMargin(props.properties.height, props.properties.margin.top, props.properties.margin.bottom)).toString()}
onChange={(value) => { props.onChange('height', ApplyWidthMargin(Number(value), props.properties.margin.top, props.properties.margin.bottom)); }}
value={(RemoveWidthMargin(props.properties.height,
props.properties.margin.top,
props.properties.margin.bottom)).toString()}
onChange={(value) => {
props.onChange('height', ApplyWidthMargin(Number(value),
props.properties.margin.top,
props.properties.margin.bottom));
}}
isDisabled={props.properties.isFlex}
/>
<TextInputGroup
@ -332,7 +351,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
labelText={Text({ textId: '@ContainerAlignWithSymbol' })}
labelClassName=''
inputClassName=''
inputs={[...props.symbols.values()].map(symbol => ({
inputs={[...props.symbols.values()].filter(symbol => (symbol.isVertical === isVertical)).map(symbol => ({
key: symbol.id,
text: symbol.id,
value: symbol.id
@ -366,7 +385,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
labelClassName=''
inputClassName=''
type='color'
value={props.properties.dimensionOptions.selfDimensions.color}
value={props.properties.dimensionOptions.selfDimensions.color ?? '#000000'}
onChange={(e) => { props.onChange('color', e.target.value, PropertyType.SelfDimension); }}/>
<TextInputGroup
id={`${props.properties.id}-selfDimensions-width`}
@ -406,7 +425,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
labelClassName=''
inputClassName=''
type='color'
value={props.properties.dimensionOptions.selfMarginsDimensions.color}
value={props.properties.dimensionOptions.selfMarginsDimensions.color ?? '#000000'}
onChange={(e) => { props.onChange('color', e.target.value, PropertyType.SelfMarginDimension); }}/>
<TextInputGroup
id={`${props.properties.id}-selfMarginsDimensions-width`}
@ -446,7 +465,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
labelClassName=''
inputClassName=''
type='color'
value={props.properties.dimensionOptions.childrenDimensions.color}
value={props.properties.dimensionOptions.childrenDimensions.color ?? '#000000'}
onChange={(e) => { props.onChange('color', e.target.value, PropertyType.ChildrenDimensions); }}/>
<TextInputGroup
id={`${props.properties.id}-childrenDimensions-width`}
@ -496,7 +515,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
labelClassName=''
inputClassName=''
type='color'
value={props.properties.dimensionOptions.dimensionWithMarks.color}
value={props.properties.dimensionOptions.dimensionWithMarks.color ?? '#000000'}
onChange={(e) => { props.onChange('color', e.target.value, PropertyType.DimensionWithMarks); }}/>
<TextInputGroup
id={`${props.properties.id}-dimensionWithMarks-width`}
@ -531,16 +550,14 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
heightClass={`${categoryHeight}`}
>
<div className='grid grid-cols-5 gap-6 items-center prop-category-body'>
<TextInputGroup
id={`${props.properties.id}-stroke`}
<InputGroup
labelText={Text({ textId: '@StyleStroke' })}
inputKey='stroke'
inputKey={`${props.properties.id}-stroke`}
labelClassName='col-span-2'
inputClassName='col-span-3'
type='string'
value={props.properties.style.stroke ?? 'black'}
onChange={(value) => { props.onChange('stroke', value, PropertyType.Style); }}
/>
type='color'
value={props.properties.style.stroke ?? '#000000'}
onChange={(e) => { props.onChange('stroke', e.target.value, PropertyType.Style); }}/>
<InputGroup
labelKey={`${props.properties.id}-strokeOpacity`}
labelText={Text({ textId: '@StyleStrokeOpacity' })}
@ -564,16 +581,14 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
value={(props.properties.style.strokeWidth ?? 1).toString()}
onChange={(value) => { props.onChange('strokeWidth', Number(value), PropertyType.Style); }}
/>
<TextInputGroup
id={`${props.properties.id}-fill`}
<InputGroup
labelText={Text({ textId: '@StyleFill' })}
inputKey='fill'
inputKey={`${props.properties.id}-fill`}
labelClassName='col-span-2'
inputClassName='col-span-3'
type='string'
value={props.properties.style.fill ?? 'black'}
onChange={(value) => { props.onChange('fill', value, PropertyType.Style); }}
/>
type='color'
value={props.properties.style.fill ?? '#000000'}
onChange={(e) => { props.onChange('fill', e.target.value, PropertyType.Style); }}/>
<InputGroup
labelKey={`${props.properties.id}-fillOpacity`}
labelText={Text({ textId: '@StyleFillOpacity' })}

View file

@ -1,10 +1,12 @@
import React from 'react';
import { PropertyType } from '../../Enums/PropertyType';
import { IContainerProperties } from '../../Interfaces/IContainerProperties';
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
import { type PropertyType } from '../../Enums/PropertyType';
import { type IContainerProperties } from '../../Interfaces/IContainerProperties';
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import { ContainerForm } from './ContainerForm';
import { type IContainerModel } from '../../Interfaces/IContainerModel';
interface IPropertiesProps {
containers: Map<string, IContainerModel>
properties?: IContainerProperties
symbols: Map<string, ISymbolModel>
onChange: (key: string, value: string | number | boolean | number[], type?: PropertyType) => void
@ -18,6 +20,7 @@ export function ContainerProperties(props: IPropertiesProps): JSX.Element {
return (
<div className='h-full p-3 bg-slate-200 overflow-y-auto'>
<ContainerForm
containers={props.containers}
properties={props.properties}
symbols={props.symbols}
onChange={props.onChange} />

View file

@ -1,10 +1,10 @@
import { IConfiguration } from '../../../Interfaces/IConfiguration';
import { IContainerModel } from '../../../Interfaces/IContainerModel';
import { IHistoryState } from '../../../Interfaces/IHistoryState';
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { type IConfiguration } from '../../../Interfaces/IConfiguration';
import { type IContainerModel } from '../../../Interfaces/IContainerModel';
import { type IHistoryState } from '../../../Interfaces/IHistoryState';
import { type ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { GetDefaultSymbolModel } from '../../../utils/default';
import { FindContainerById } from '../../../utils/itertools';
import { RestoreX } from '../../../utils/svg';
import {RestoreX, RestoreY} from '../../../utils/svg';
import { ApplyBehaviors, ApplyBehaviorsOnSiblingsChildren } from '../Behaviors/Behaviors';
import { GetCurrentHistory, GetCurrentHistoryState, UpdateCounters } from '../Editor';
import { AddContainers } from './AddContainer';
@ -32,7 +32,12 @@ export function AddSymbol(
const newSymbols = structuredClone(current.symbols);
const newSymbol: ISymbolModel = GetDefaultSymbolModel(name, typeCounters, type, symbolConfig);
const containers = structuredClone(current.containers);
newSymbol.x = RestoreX(newSymbol.x, newSymbol.width, newSymbol.config.PositionReference);
if (newSymbol.isVertical) {
newSymbol.offset = RestoreY(newSymbol.offset, newSymbol.height, newSymbol.config.PositionReference);
} else {
newSymbol.offset = RestoreX(newSymbol.offset, newSymbol.width, newSymbol.config.PositionReference);
}
newSymbols.set(newSymbol.id, newSymbol);

View file

@ -1,16 +1,39 @@
import { IContainerModel } from '../../../Interfaces/IContainerModel';
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { type IContainerModel } from '../../../Interfaces/IContainerModel';
import { type ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { ApplyParentTransform, FindContainerById } from '../../../utils/itertools';
import { RestoreX, TransformX } from '../../../utils/svg';
import { RestoreX, RestoreY, TransformX, TransformY } from '../../../utils/svg';
export function ApplySymbol(containers: Map<string, IContainerModel>, container: IContainerModel, symbol: ISymbolModel): IContainerModel {
container.properties.x = TransformX(symbol.x, symbol.width, symbol.config.PositionReference);
container.properties.x = RestoreX(container.properties.x, container.properties.width, container.properties.positionReference);
const parent = FindContainerById(containers, container.properties.parentId);
let x = 0;
if (parent !== undefined && parent !== null) {
([x] = ApplyParentTransform(containers, parent, container.properties.x, 0));
export function ApplySymbol(containers: Map<string, IContainerModel>,
container: IContainerModel,
symbol: ISymbolModel): IContainerModel {
if (symbol.isVertical) {
container.properties.y = TransformY(symbol.offset,
symbol.height,
symbol.config.PositionReference);
container.properties.y = RestoreY(container.properties.y,
container.properties.height,
container.properties.positionReference);
const parent = FindContainerById(containers, container.properties.parentId);
let y = 0;
if (parent !== undefined && parent !== null) {
([,y] = ApplyParentTransform(containers, parent, 0, container.properties.y));
}
container.properties.y = y;
return container;
} else {
container.properties.x = TransformX(
symbol.offset,
symbol.width,
symbol.config.PositionReference);
container.properties.x = RestoreX(container.properties.x,
container.properties.width,
container.properties.positionReference);
const parent = FindContainerById(containers, container.properties.parentId);
let x = 0;
if (parent !== undefined && parent !== null) {
([x] = ApplyParentTransform(containers, parent, container.properties.x, 0));
}
container.properties.x = x;
return container;
}
container.properties.x = x;
return container;
}

View file

@ -1,21 +1,22 @@
import * as React from 'react';
import { useState } from 'react';
import useSize from '@react-hook/size';
import { FixedSizeList as List } from 'react-window';
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
import { ContainerProperties } from '../ContainerProperties/ContainerProperties';
import { IContainerModel } from '../../Interfaces/IContainerModel';
import { type IContainerModel } from '../../Interfaces/IContainerModel';
import { FindContainerById, MakeRecursionDFSIterator } from '../../utils/itertools';
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
import { PropertyType } from '../../Enums/PropertyType';
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import { type PropertyType } from '../../Enums/PropertyType';
import { ToggleSideBar } from '../Sidebar/ToggleSideBar/ToggleSideBar';
import { Text } from '../Text/Text';
import { ExtendedSidebar } from '../UI/UI';
interface IElementsSideBarProps {
interface IElementsSidebarProps {
containers: Map<string, IContainerModel>
mainContainer: IContainerModel
symbols: Map<string, ISymbolModel>
selectedContainer: IContainerModel | undefined
selectedExtendedSidebar: ExtendedSidebar
onPropertyChange: (
key: string,
value: string | number | boolean | number[],
@ -23,8 +24,7 @@ interface IElementsSideBarProps {
) => void
selectContainer: (containerId: string) => void
addContainer: (index: number, type: string, parent: string) => void
isExpanded: boolean
onExpandChange: () => void
onExpandChange: (value: ExtendedSidebar) => void
}
function RemoveBorderClasses(target: HTMLButtonElement, exception: string = ''): void {
@ -124,11 +124,10 @@ function HandleOnDrop(
}
}
export function ElementsSideBar(props: IElementsSideBarProps): JSX.Element {
export function ElementsSidebar(props: IElementsSidebarProps): JSX.Element {
// States
const divRef = React.useRef<HTMLDivElement>(null);
const [,height] = useSize(divRef);
const [showProperties, setShowProperties] = useState(props.isExpanded);
// Render
const it = MakeRecursionDFSIterator(props.mainContainer, props.containers, 0, [0, 0], true);
@ -167,18 +166,28 @@ export function ElementsSideBar(props: IElementsSideBarProps): JSX.Element {
return (
<div className='flex flex-row h-full w-full' >
{showProperties &&
<div className='flex flex-1 flex-col w-64 border-r-2 border-slate-400'>
<ContainerProperties
properties={props.selectedContainer?.properties}
symbols={props.symbols}
onChange={props.onPropertyChange}
/>
</div>
{props.selectedExtendedSidebar === ExtendedSidebar.Property &&
<div className='flex flex-1 flex-col w-64 border-r-2 border-slate-400'>
<ContainerProperties
containers ={props.containers}
properties={props.selectedContainer?.properties}
symbols={props.symbols}
onChange={props.onPropertyChange}
/>
</div>
}
<div className='flex w-64' ref={divRef}>
<div className='w-6'>
<ToggleSideBar title={Text({ textId: '@Properties' })} checked={showProperties} onChange={(newValue) => { setShowProperties(newValue); props.onExpandChange(); }} />
<ToggleSideBar
title={Text({ textId: '@Properties' })}
checked={props.selectedExtendedSidebar === ExtendedSidebar.Property}
onClick={() => {
const newValue = props.selectedExtendedSidebar !== ExtendedSidebar.Property
? ExtendedSidebar.Property
: ExtendedSidebar.None;
props.onExpandChange(newValue);
}}
/>
</div>
<List
itemCount={containers.length}
@ -223,16 +232,18 @@ function ElementsListRow(
: 'bg-slate-300/60 hover:bg-slate-400 hover:shadow-slate-400';
return <button type="button"
className={`transition-all border-blue-500 hover:shadow-lg elements-sidebar-row whitespace-pre
text-left text-sm font-medium flex items-center align-middle group ${container.properties.type} ${buttonSelectedClass}`}
className={`transition-all border-blue-500
hover:shadow-lg elements-sidebar-row whitespace-pre
text-left text-sm font-medium flex items-center align-middle group
${container.properties.type} ${buttonSelectedClass}`}
id={key}
key={key}
style={style}
title={container.properties.warning}
onClick={() => selectContainer(container.properties.id)}
onDrop={(event) => HandleOnDrop(event, containers, mainContainer, addContainer)}
onDragOver={(event) => HandleDragOver(event, mainContainer)}
onDragLeave={(event) => HandleDragLeave(event)}
onClick={() => { selectContainer(container.properties.id); }}
onDrop={(event) => { HandleOnDrop(event, containers, mainContainer, addContainer); }}
onDragOver={(event) => { HandleDragOver(event, mainContainer); }}
onDragLeave={(event) => { HandleDragLeave(event); }}
>
{verticalBars}
{text}

View file

@ -31,7 +31,7 @@ function ApplyParametric(x0: number, t: number, vx: number): number {
export function Dimension(props: IDimensionProps): JSX.Element {
const scale = props.scale ?? 1;
const style: React.CSSProperties = {
stroke: props.style.color,
stroke: props.style.color ?? '#000000',
strokeWidth: (props.style.width ?? 2) / scale,
strokeDasharray: props.style.dashArray
};

View file

@ -2,6 +2,7 @@ import * as React from 'react';
import { Orientation } from '../../../Enums/Orientation';
import { Position } from '../../../Enums/Position';
import {
DEFAULT_DIMENSION_SYMBOL_STYLE,
DIMENSION_MARGIN,
SHOW_BORROWER_DIMENSIONS,
SHOW_CHILDREN_DIMENSIONS,
@ -10,7 +11,7 @@ import {
} from '../../../utils/default';
import { FindContainerById, MakeRecursionDFSIterator, Pairwise } from '../../../utils/itertools';
import { TransformX, TransformY } from '../../../utils/svg';
import { Dimension, type IDimensionStyle } from './Dimension';
import { Dimension } from './Dimension';
import { type IContainerModel } from '../../../Interfaces/IContainerModel';
import { type ISymbolModel } from '../../../Interfaces/ISymbolModel';
@ -41,14 +42,12 @@ function ActionByPosition(
positions.forEach((position: Position) => {
const dim = dimMapped[position];
switch (position) {
case Position.Right:
case Position.Left:
case Position.Right: {
const isRight = position === Position.Right;
verticalAction(dim, isRight, ...params);
verticalAction(dim, false, ...params);
break;
}
case Position.Down:
case Position.Up:
case Position.Down:
horizontalAction(dim, ...params);
break;
}
@ -94,7 +93,8 @@ function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps):
);
}
if (SHOW_SELF_MARGINS_DIMENSIONS && container.properties.dimensionOptions.selfMarginsDimensions.positions.length > 0) {
if (SHOW_SELF_MARGINS_DIMENSIONS &&
container.properties.dimensionOptions.selfMarginsDimensions.positions.length > 0) {
ActionByPosition(
dimMapped,
container.properties.dimensionOptions.selfMarginsDimensions.positions,
@ -127,7 +127,9 @@ function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps):
);
}
if (SHOW_CHILDREN_DIMENSIONS && container.properties.dimensionOptions.childrenDimensions.positions.length > 0 && container.children.length >= 2) {
if (SHOW_CHILDREN_DIMENSIONS &&
container.properties.dimensionOptions.childrenDimensions.positions.length > 0 &&
container.children.length >= 2) {
ActionByPosition(
dimMapped,
container.properties.dimensionOptions.childrenDimensions.positions,
@ -144,29 +146,26 @@ function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps):
}
}
let startDepthSymbols: number = 0;
for (const symbol of symbols) {
if (symbol[1].showDimension) {
startDepthSymbols++;
AddHorizontalSymbolDimension(
symbol[1],
dimensions,
scale,
startDepthSymbols
);
// TODO: Implement DimensionManager
symbols.forEach((symbol) => {
if (symbol.showDimension) {
if (symbol.isVertical) {
AddVerticalSymbolDimension(symbol, dimensions, scale, 0);
} else {
AddHorizontalSymbolDimension(symbol, dimensions, scale, 0);
}
}
}
});
return dimensions;
}
function AddHorizontalSymbolDimension(
symbol: ISymbolModel,
function AddHorizontalSymbolDimension(symbol: ISymbolModel,
dimensions: React.ReactNode[],
scale: number,
depth: number
): void {
const width = symbol.x + (symbol.width / 2);
const width = TransformX(symbol.offset, symbol.width, symbol.config.PositionReference);
if (width != null && width > 0) {
const id = `dim-y-margin-left${symbol.width.toFixed(0)}-${symbol.id}`;
@ -174,11 +173,6 @@ function AddHorizontalSymbolDimension(
const text = width
.toFixed(0)
.toString();
// TODO: Put this in default.ts
const defaultDimensionSymbolStyle: IDimensionStyle = {
color: 'black'
};
dimensions.push(
<Dimension
key={id}
@ -189,7 +183,35 @@ function AddHorizontalSymbolDimension(
yEnd={-offset}
text={text}
scale={scale}
style={defaultDimensionSymbolStyle}/>
style={DEFAULT_DIMENSION_SYMBOL_STYLE}/>
);
}
}
function AddVerticalSymbolDimension(symbol: ISymbolModel,
dimensions: React.ReactNode[],
scale: number,
depth: number
): void {
const height = TransformY(symbol.offset, symbol.height, symbol.config.PositionReference);
if (height != null && height > 0) {
const id = `dim-x-margin-left${symbol.height.toFixed(0)}-${symbol.id}`;
const offset = (DIMENSION_MARGIN * (depth + 1)) / scale;
const text = height
.toFixed(0)
.toString();
dimensions.push(
<Dimension
key={id}
id={id}
xStart={-offset}
yStart={height}
xEnd={-offset}
yEnd={0}
text={text}
scale={scale}
style={DEFAULT_DIMENSION_SYMBOL_STYLE}/>
);
}
}
@ -227,8 +249,12 @@ function AddHorizontalChildrenDimension(
return;
}
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 xChildrenStart = 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
for (let i = container.children.length - 2; i >= 0; i--) {
@ -290,8 +316,12 @@ function AddVerticalChildrenDimension(
return;
}
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 yChildrenStart = 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
for (let i = container.children.length - 2; i >= 0; i--) {
@ -537,7 +567,7 @@ function AddHorizontalSelfMarginsDimension(
): void {
const style = container.properties.dimensionOptions.selfMarginsDimensions;
const left = container.properties.margin.left;
if (left != null) {
if (left != null && left > 0) {
const id = `dim-y-margin-left${yDim.toFixed(0)}-${container.properties.id}`;
const xStart = container.properties.x + currentTransform[0] - left;
const xEnd = xStart + left;
@ -559,7 +589,7 @@ function AddHorizontalSelfMarginsDimension(
}
const right = container.properties.margin.right;
if (right != null) {
if (right != null && right > 0) {
const id = `dim-y-margin-right${yDim.toFixed(0)}-${container.properties.id}`;
const xStart = container.properties.x + container.properties.width + currentTransform[0];
const xEnd = xStart + right;
@ -591,7 +621,7 @@ function AddVerticalSelfMarginDimension(
): void {
const style = container.properties.dimensionOptions.selfMarginsDimensions;
const top = container.properties.margin.top;
if (top != null) {
if (top != null && top > 0) {
const idVert = `dim-x-margin-top${xDim.toFixed(0)}-${container.properties.id}`;
let yStart = container.properties.y + currentTransform[1];
let yEnd = yStart - top;
@ -617,7 +647,7 @@ function AddVerticalSelfMarginDimension(
);
}
const bottom = container.properties.margin.bottom;
if (bottom != null) {
if (bottom != null && bottom > 0) {
const idVert = `dim-x-margin-bottom${xDim.toFixed(0)}-${container.properties.id}`;
let yStart = container.properties.y + container.properties.height + bottom + currentTransform[1];
let yEnd = yStart - bottom;

View file

@ -1,13 +1,12 @@
import '../Selector.scss';
import * as React from 'react';
import { SYMBOL_MARGIN } from '../../../../utils/default';
import { DIMENSION_MARGIN, SYMBOL_MARGIN } from '../../../../utils/default';
import { type ISymbolModel } from '../../../../Interfaces/ISymbolModel';
import { Selector } from '../Selector/Selector';
interface ISelectorSymbolProps {
symbols: Map<string, ISymbolModel>
selected?: ISymbolModel
scale?: number
}
export function SelectorSymbol(props: ISelectorSymbolProps): JSX.Element {
@ -18,30 +17,27 @@ export function SelectorSymbol(props: ISelectorSymbolProps): JSX.Element {
);
}
const scale = (props.scale ?? 1);
const [width, height] = [
props.selected.width / scale,
props.selected.height / scale
];
let x, y: number;
const [x, y] = [
props.selected.x + props.selected.width / 2,
-SYMBOL_MARGIN - height];
const scaledHeight = props.selected.height;
const scaledWidth = props.selected.width;
const style: React.CSSProperties = {
transform: 'translateX(-50%)',
transformBox: 'fill-box'
};
if (props.selected.isVertical) {
x = -SYMBOL_MARGIN - props.selected.width;
y = props.selected.offset;
} else {
x = props.selected.offset;
y = -SYMBOL_MARGIN - props.selected.height;
}
return (
<Selector
text={props.selected.displayedText}
x={x}
y={y}
width={width}
height={height}
scale={scale}
style={style}
width={scaledWidth}
height={scaledHeight}
scale={1}
/>
);
}

View file

@ -1,7 +1,7 @@
import { Interweave } from 'interweave';
import * as React from 'react';
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { DIMENSION_MARGIN, SYMBOL_MARGIN } from '../../../utils/default';
import { type ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { SYMBOL_MARGIN } from '../../../utils/default';
interface ISymbolProps {
model: ISymbolModel
@ -12,11 +12,22 @@ export function Symbol(props: ISymbolProps): JSX.Element {
const href = props.model.config.Image.Base64Image ?? props.model.config.Image.Url;
const hasSVG = props.model.config.Image.Svg !== undefined &&
props.model.config.Image.Svg !== null;
let x, y: number;
if (props.model.isVertical) {
x = -SYMBOL_MARGIN - props.model.width;
y = props.model.offset;
} else {
x = props.model.offset;
y = -SYMBOL_MARGIN - props.model.height;
}
if (hasSVG) {
return (
<g
x={props.model.x}
y={-DIMENSION_MARGIN / props.scale}
x={x}
y={y}
>
<Interweave
noWrap={true}
@ -31,14 +42,9 @@ export function Symbol(props: ISymbolProps): JSX.Element {
<image
href={href}
preserveAspectRatio="none"
style={{
fill: 'none',
transform: 'translateY(-100%) translateX(-50%)',
transformBox: 'fill-box'
}}
x={props.model.x + props.model.width / 2}
y={-SYMBOL_MARGIN}
height={props.model.height / props.scale}
width={props.model.width / props.scale} />
x={x}
y={y}
height={props.model.height}
width={props.model.width} />
);
}

View file

@ -1,13 +1,13 @@
import * as React from 'react';
import { ReactSVGPanZoom, type Tool, TOOL_PAN, type Value } from 'react-svg-pan-zoom';
import { ReactSVGPanZoom, type Tool, TOOL_PAN, type Value, ALIGN_CENTER } from 'react-svg-pan-zoom';
import { Container } from './Elements/Container';
import { IContainerModel } from '../../Interfaces/IContainerModel';
import { SelectorContainer } from './Elements/SelectorContainer/SelectorContainer';
import { MAX_FRAMERATE } from '../../utils/default';
import { SymbolLayer } from './Elements/SymbolLayer';
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import { DimensionLayer } from './Elements/DimensionLayer';
import { SelectorSymbol } from './Elements/SelectorSymbol/SelectorSymbol';
import { type IToolbarProps, Toolbar } from './SVGReactPanZoom/ui-toolbar/toolbar';
import { type DrawParams } from '../Viewer/Viewer';
interface ISVGProps {
className?: string
@ -15,12 +15,7 @@ interface ISVGProps {
viewerHeight: number
width: number
height: number
containers: Map<string, IContainerModel>
children: IContainerModel
selectedContainer?: IContainerModel
symbols: Map<string, ISymbolModel>
selectedSymbol?: ISymbolModel
selectorMode: SelectorMode
drawParams: DrawParams
selectContainer: (containerId: string) => void
}
@ -33,6 +28,14 @@ export enum SelectorMode {
export const ID = 'svg';
export function SVG(props: ISVGProps): JSX.Element {
const {
mainContainer,
selectorMode,
selectedContainer,
selectedSymbol,
containers,
symbols
} = props.drawParams;
const [tool, setTool] = React.useState<Tool>(TOOL_PAN);
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const [value, setValue] = React.useState<Value>({} as Value);
@ -58,27 +61,27 @@ export function SVG(props: ISVGProps): JSX.Element {
};
const children: React.ReactNode | React.ReactNode[] = <Container
key={`container-${props.children.properties.id}`}
containers={props.containers}
model={props.children}
key={`container-${mainContainer.properties.id}`}
containers={containers}
model={mainContainer}
depth={0}
scale={scale}
selectContainer={props.selectContainer}
/>;
function Selector(): JSX.Element {
switch (props.selectorMode) {
switch (selectorMode) {
case SelectorMode.Containers:
return <SelectorContainer
containers={props.containers}
containers={containers}
scale={scale}
selected={props.selectedContainer}
selected={selectedContainer}
/>;
case SelectorMode.Symbols:
return <SelectorSymbol
symbols={props.symbols}
symbols={symbols}
scale={scale}
selected={props.selectedSymbol}
selected={selectedSymbol}
/>;
default:
return <></>;
@ -109,6 +112,9 @@ export function SVG(props: ISVGProps): JSX.Element {
const value = event as Value;
setScale(value.a);
}}
onDoubleClick={() => {
svgViewer?.current?.setPointOnViewerCenter(props.width / 2, props.height / 2, 0.8);
}}
background={'#ffffff'}
defaultTool='pan'
miniatureProps={{
@ -117,11 +123,24 @@ export function SVG(props: ISVGProps): JSX.Element {
width: 120,
height: 120
}}
customToolbar={(props: IToolbarProps) => (
<Toolbar
{...props}
SVGAlignX={ALIGN_CENTER}
SVGAlignY={ALIGN_CENTER}
fittingScale={0.8}
/>
)}
>
<svg {...properties}>
{children}
<DimensionLayer containers={props.containers} symbols={props.symbols} scale={scale} root={props.children} />
<SymbolLayer scale={scale} symbols={props.symbols} />
<DimensionLayer
containers={containers}
symbols={symbols}
scale={scale}
root={mainContainer}
/>
<SymbolLayer scale={scale} symbols={symbols} />
<Selector />
</svg>
</ReactSVGPanZoom>
@ -130,7 +149,8 @@ export function SVG(props: ISVGProps): JSX.Element {
}
function UseFitOnce(svgViewer: React.RefObject<ReactSVGPanZoom>, width: number, height: number): void {
React.useEffect(() => {
svgViewer?.current?.fitToViewer();
React.useCallback(() => {
// TODO: Fix this
svgViewer?.current?.setPointOnViewerCenter(width / 2, height / 2, 0.8);
}, [svgViewer, width, height]);
}

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 https://github.com/chrvadala
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,81 @@
import React from 'react';
import { POSITION_TOP, POSITION_BOTTOM } from 'react-svg-pan-zoom';
interface IToolbarButtonProps {
title: string
name: string
toolbarPosition: string
activeColor: string
onClick: (event: React.MouseEvent | React.TouchEvent) => void
active: boolean
children: JSX.Element | JSX.Element[]
}
interface IToolbarButtonState {
hover: boolean
}
export class ToolbarButton extends React.Component<IToolbarButtonProps, IToolbarButtonState> {
public state: IToolbarButtonState;
constructor(props: IToolbarButtonProps) {
super(props);
this.state = { hover: false };
}
change(event: (React.MouseEvent | React.TouchEvent)): void {
event.preventDefault();
event.stopPropagation();
switch (event.type) {
case 'mouseenter':
case 'touchstart':
this.setState({ hover: true });
break;
case 'mouseleave':
case 'touchend':
case 'touchcancel':
this.setState({ hover: false });
break;
default:
// noop
}
}
render(): JSX.Element {
const style = {
display: 'block',
width: '24px',
height: '24px',
margin: [POSITION_TOP, POSITION_BOTTOM].includes(this.props.toolbarPosition) ? '2px 1px' : '1px 2px',
color: this.props.active || this.state.hover ? this.props.activeColor : '#FFF',
transition: 'color 200ms ease',
background: 'none',
padding: '0px',
border: '0px',
outline: '0px',
cursor: 'pointer'
};
return (
<button
onMouseEnter={e => { this.change(e); }}
onMouseLeave={e => { this.change(e); }}
onTouchStart={e => {
this.change(e);
this.props.onClick(e);
}}
onTouchEnd={e => { this.change(e); }}
onTouchCancel={e => { this.change(e); }}
onClick={this.props.onClick}
style={style}
title={this.props.title}
name={this.props.name}
type="button"
>{this.props.children}</button>
);
}
}

View file

@ -0,0 +1,188 @@
import {
ArrowsPointingOutIcon,
CursorArrowRaysIcon,
HandRaisedIcon,
MagnifyingGlassMinusIcon,
MagnifyingGlassPlusIcon
} from '@heroicons/react/24/outline';
import React from 'react';
import { applyToPoint, fromObject, inverse, scale, transform, translate } from 'transformation-matrix';
import {
fitToViewer,
POSITION_TOP,
POSITION_BOTTOM,
POSITION_LEFT,
POSITION_RIGHT,
TOOL_NONE,
TOOL_PAN,
TOOL_ZOOM_IN,
TOOL_ZOOM_OUT, ALIGN_LEFT, ALIGN_TOP,
type Value,
type Tool,
type ALIGN_BOTTOM,
type ALIGN_CENTER,
type ALIGN_RIGHT,
type ToolbarPosition
} from 'react-svg-pan-zoom';
import { ToolbarButton } from './toolbar-button';
export interface IToolbarProps {
tool: Tool
value: Value
onChangeValue: (value: Value) => void
onChangeTool: (tool: Tool) => void
activeToolColor?: string
position?: ToolbarPosition | undefined
SVGAlignX?: typeof ALIGN_CENTER | typeof ALIGN_LEFT | typeof ALIGN_RIGHT | undefined
SVGAlignY?: typeof ALIGN_CENTER | typeof ALIGN_TOP | typeof ALIGN_BOTTOM | undefined
fittingScale?: number | undefined
}
/**
* Change value
* @param value
* @param patch
* @param action
* @returns {Object}
*/
function set(value: Value, patch: object, action = null): Value {
value = Object.assign({}, value, patch, { lastAction: action });
return Object.freeze(value);
}
/**
* Export x,y coords relative to SVG
* @param value
* @param viewerX
* @param viewerY
* @returns {*|{x, y}|{x: number, y: number}}
*/
function getSVGPoint(value: Value, viewerX: number, viewerY: number): PointObjectNotation {
const matrix = fromObject(value);
const inverseMatrix = inverse(matrix);
return applyToPoint(inverseMatrix, { x: viewerX, y: viewerY });
}
export function zoom(
value: Value,
SVGPointX: number,
SVGPointY: number,
scaleFactor: number
): Value {
const matrix = transform(
fromObject(value),
translate(SVGPointX, SVGPointY),
scale(scaleFactor, scaleFactor),
translate(-SVGPointX, -SVGPointY)
);
return set(value, {
...matrix
});
}
export function Toolbar({
tool,
value,
onChangeValue,
onChangeTool,
activeToolColor = '#1CA6FC',
position = POSITION_RIGHT,
SVGAlignX = ALIGN_LEFT,
SVGAlignY = ALIGN_TOP,
fittingScale = undefined
}: IToolbarProps): JSX.Element {
function handleChangeTool(event: React.MouseEvent | React.TouchEvent, tool: Tool): void {
onChangeTool(tool);
event.stopPropagation();
event.preventDefault();
};
function handleFit(event: React.MouseEvent | React.TouchEvent): void {
let fittedValue: Value = fitToViewer(value, SVGAlignX, SVGAlignY);
if (fittingScale !== undefined) {
const { viewerWidth, viewerHeight } = fittedValue;
const SVGPoint = getSVGPoint(value, viewerWidth / 2, viewerHeight / 2);
fittedValue = zoom(fittedValue, SVGPoint.x, SVGPoint.y, fittingScale);
}
onChangeValue(fittedValue);
event.stopPropagation();
event.preventDefault();
};
const isHorizontal = [POSITION_TOP, POSITION_BOTTOM].includes(position);
const style: React.CSSProperties = {
// position
position: 'absolute',
transform: [POSITION_TOP, POSITION_BOTTOM].includes(position) ? 'translate(-50%, 0px)' : 'none',
top: [POSITION_LEFT, POSITION_RIGHT, POSITION_TOP].includes(position) ? '5px' : 'unset',
left: [POSITION_TOP, POSITION_BOTTOM].includes(position) ? '50%' : (POSITION_LEFT === position ? '5px' : 'unset'),
right: [POSITION_RIGHT].includes(position) ? '5px' : 'unset',
bottom: [POSITION_BOTTOM].includes(position) ? '5px' : 'unset',
// inner styling
backgroundColor: 'rgba(19, 20, 22, 0.90)',
borderRadius: '2px',
display: 'flex',
flexDirection: isHorizontal ? 'row' : 'column',
padding: isHorizontal ? '1px 2px' : '2px 1px'
};
return (
<div style={style} role="toolbar">
<ToolbarButton
toolbarPosition={position}
active={tool === TOOL_NONE}
activeColor={activeToolColor}
name="unselect-tools"
title="Selection"
onClick={ (event: React.MouseEvent | React.TouchEvent) => { handleChangeTool(event, TOOL_NONE); } }>
<CursorArrowRaysIcon/>
</ToolbarButton>
<ToolbarButton
toolbarPosition={position}
active={tool === TOOL_PAN}
activeColor={activeToolColor}
name="select-tool-pan"
title="Pan"
onClick={ (event: React.MouseEvent | React.TouchEvent) => { handleChangeTool(event, TOOL_PAN); } }>
<HandRaisedIcon/>
</ToolbarButton>
<ToolbarButton
toolbarPosition={position}
active={tool === TOOL_ZOOM_IN}
activeColor={activeToolColor}
name="select-tool-zoom-in"
title="Zoom in"
onClick={ (event: React.MouseEvent | React.TouchEvent) => { handleChangeTool(event, TOOL_ZOOM_IN); } }>
<MagnifyingGlassPlusIcon/>
</ToolbarButton>
<ToolbarButton
toolbarPosition={position}
active={tool === TOOL_ZOOM_OUT}
activeColor={activeToolColor}
name="select-tool-zoom-out"
title="Zoom out"
onClick={ (event: React.MouseEvent | React.TouchEvent) => { handleChangeTool(event, TOOL_ZOOM_OUT); } }>
<MagnifyingGlassMinusIcon/>
</ToolbarButton>
<ToolbarButton
toolbarPosition={position}
active={false}
activeColor={activeToolColor}
name="fit-to-viewer"
title="Fit to viewer"
onClick={ (event: React.MouseEvent | React.TouchEvent) => { handleFit(event); } }>
<ArrowsPointingOutIcon/>
</ToolbarButton>
</div>
);
}

View file

@ -4,16 +4,16 @@ import './ToggleSideBar.scss';
interface IToggleSidebarProps {
title: string
checked: boolean
onChange: (newValue: boolean) => void
onClick: () => void
}
export function ToggleSideBar({ title, checked, onChange }: IToggleSidebarProps): JSX.Element {
export function ToggleSideBar({ title, checked, onClick }: IToggleSidebarProps): JSX.Element {
return (
<div className={`${(checked ? 'bg-slate-400 hover:bg-slate-500' : 'bg-slate-300 hover:bg-slate-400')}`}>
<button
className={'w-full py-2'}
type='button'
onClick={() => onChange(!checked)}
onClick={onClick}
>
<p className='text-vertical'>{title}
</p>

View file

@ -1,11 +1,11 @@
import * as React from 'react';
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
import { RestoreX, TransformX } from '../../utils/svg';
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import { RestoreX, RestoreY, TransformX, TransformY } from '../../utils/svg';
import { InputGroup } from '../InputGroup/InputGroup';
import { TextInputGroup } from '../InputGroup/TextInputGroup';
import { Text } from '../Text/Text';
import { PropertyType } from '../../Enums/PropertyType';
import { ToggleButton } from '../ToggleButton/ToggleButton';
import { type PositionReference } from '../../Enums/PositionReference';
interface ISymbolFormProps {
symbol: ISymbolModel
@ -13,6 +13,29 @@ interface ISymbolFormProps {
onChange: (key: string, value: string | number | boolean) => void
}
function Restore(offset: number,
isVertical: boolean,
height: number,
width: number,
position: PositionReference | undefined): number {
if (isVertical) {
return RestoreY(offset, height, position);
} else {
return RestoreX(offset, width, position);
}
}
function Transform(offset: number,
isVertical: boolean,
height: number,
width: number,
position: PositionReference | undefined): number {
if (isVertical) {
return TransformY(offset, height, position);
} else {
return TransformX(offset, width, position);
}
}
export function SymbolForm(props: ISymbolFormProps): JSX.Element {
return (
<div className='grid grid-cols-2 gap-y-4'>
@ -32,16 +55,34 @@ export function SymbolForm(props: ISymbolFormProps): JSX.Element {
inputClassName=''
type='string'
value={props.symbol.displayedText}
onChange={(value) => props.onChange('displayedText', value)} />
onChange={(value) => { props.onChange('displayedText', value); }} />
<TextInputGroup
id='x'
labelText={Text({ textId: '@SymbolX' })}
inputKey='x'
id='offset'
labelText={Text({ textId: '@SymbolOffset' })}
inputKey='offset'
labelClassName=''
inputClassName=''
type='number'
value={TransformX(props.symbol.x, props.symbol.width, props.symbol.config.PositionReference).toString()}
onChange={(value) => props.onChange('x', RestoreX(Number(value), props.symbol.width, props.symbol.config.PositionReference))} />
value={Transform(props.symbol.offset,
props.symbol.isVertical,
props.symbol.height,
props.symbol.width,
props.symbol.config.PositionReference).toString()}
onChange={(value) => {
props.onChange('offset',
Restore(Number(value),
props.symbol.isVertical,
props.symbol.height,
props.symbol.width,
props.symbol.config.PositionReference));
}} />
<ToggleButton
labelText={Text({ textId: '@IsVertical' })}
inputKey='isVertical'
labelClassName=''
inputClassName=''
checked={props.symbol.isVertical}
onChange={(e) => { props.onChange('isVertical', e.target.checked); }}/>
<TextInputGroup
id='height'
labelText={Text({ textId: '@SymbolHeight' })}
@ -51,7 +92,7 @@ export function SymbolForm(props: ISymbolFormProps): JSX.Element {
type='number'
min={0}
value={props.symbol.height.toString()}
onChange={(value) => props.onChange('height', Number(value))} />
onChange={(value) => { props.onChange('height', Number(value)); }} />
<TextInputGroup
id='width'
labelText={Text({ textId: '@SymbolWidth' })}
@ -61,14 +102,14 @@ export function SymbolForm(props: ISymbolFormProps): JSX.Element {
type='number'
min={0}
value={props.symbol.width.toString()}
onChange={(value) => props.onChange('width', Number(value))} />
onChange={(value) => { props.onChange('width', Number(value)); }} />
<ToggleButton
labelText={Text({ textId: '@ShowDimension' })}
inputKey='showDimension'
labelClassName=''
inputClassName=''
checked={props.symbol.showDimension}
onChange={(e) => props.onChange('showDimension', e.target.checked)}/>
onChange={(e) => { props.onChange('showDimension', e.target.checked); }}/>
</div>
);
}

View file

@ -5,39 +5,47 @@ import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import { SymbolProperties } from '../SymbolProperties/SymbolProperties';
import { ToggleSideBar } from '../Sidebar/ToggleSideBar/ToggleSideBar';
import { Text } from '../Text/Text';
import { useState } from 'react';
import { ExtendedSidebar } from '../UI/UI';
interface ISymbolsSidebarProps {
selectedSymbolId: string
symbols: Map<string, ISymbolModel>
selectedExtendedSidebar: ExtendedSidebar
onPropertyChange: (key: string, value: string | number | boolean) => void
selectSymbol: (symbolId: string) => void
isExpanded: boolean
onExpandChange: (isExpanded: boolean) => void
onExpandChange: (value: ExtendedSidebar) => void
}
export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
// States
const divRef = React.useRef<HTMLDivElement>(null);
const height = useSize(divRef)[1];
const [showProperties, setShowProperties] = useState(props.isExpanded);
// Render
const symbols = [...props.symbols.values()];
const selectedSymbol = props.symbols.get(props.selectedSymbolId);
return (
<div className='flex flex-row h-full w-full'>
{showProperties && <div className='flex flex-1 flex-col w-64 border-r-2 border-slate-400'>
{(selectedSymbol == null) && <h1 className={'p-4'}>{Text({ textId: '@NoSymbolSelected' })}</h1>}
<SymbolProperties
symbol={selectedSymbol}
symbols={props.symbols}
onChange={props.onPropertyChange}
/>
</div>}
{props.selectedExtendedSidebar === ExtendedSidebar.Property &&
<div className='flex flex-1 flex-col w-64 border-r-2 border-slate-400'>
{(selectedSymbol == null) && <h1 className={'p-4'}>{Text({ textId: '@NoSymbolSelected' })}</h1>}
<SymbolProperties
symbol={selectedSymbol}
symbols={props.symbols}
onChange={props.onPropertyChange}
/>
</div>}
<div className={'flex w-64'} ref={divRef}>
<div className='w-6'>
<ToggleSideBar title={Text({ textId: '@Properties' })} checked={showProperties} onChange={(newValue) => { setShowProperties(newValue); props.onExpandChange(newValue); }} />
<ToggleSideBar
title={Text({ textId: '@Properties' })}
checked={props.selectedExtendedSidebar === ExtendedSidebar.Property}
onClick={() => {
const newValue = props.selectedExtendedSidebar !== ExtendedSidebar.Property
? ExtendedSidebar.Property
: ExtendedSidebar.None;
props.onExpandChange(newValue);
}} />
</div>
<List
itemCount={symbols.length}

View file

@ -1,9 +1,9 @@
import * as React from 'react';
import { ElementsSideBar } from '../ElementsList/ElementsSideBar';
import { ElementsSidebar } from '../ElementsSidebar/ElementsSidebar';
import { History } from '../History/History';
import { Bar, BAR_WIDTH } from '../Bar/Bar';
import { Symbols } from '../Symbols/Symbols';
import { SymbolsSidebar } from '../SymbolsList/SymbolsSidebar';
import { SymbolsSidebar } from '../SymbolsSidebar/SymbolsSidebar';
import { type PropertyType } from '../../Enums/PropertyType';
import { Messages } from '../Messages/Messages';
import { Sidebar } from '../Sidebar/Sidebar';
@ -17,8 +17,8 @@ import { FindContainerById } from '../../utils/itertools';
import { type IEditorState } from '../../Interfaces/IEditorState';
import { GetCurrentHistoryState } from '../Editor/Editor';
import { Text } from '../Text/Text';
import { IReplaceContainer } from '../../Interfaces/IReplaceContainer';
import { Dispatch } from 'react';
import { type IReplaceContainer } from '../../Interfaces/IReplaceContainer';
import { type Dispatch } from 'react';
export interface IUIProps {
editorState: IEditorState
@ -42,14 +42,17 @@ export interface IUIProps {
export enum SidebarType {
None,
Components,
ComponentsExpanded,
Symbols,
SymbolsExpanded,
History,
Messages,
Settings
}
export enum ExtendedSidebar {
None,
Property
}
function UseSetOrToggleSidebar(
selectedSidebar: SidebarType,
setSelectedSidebar: React.Dispatch<React.SetStateAction<SidebarType>>
@ -66,6 +69,10 @@ function UseSetOrToggleSidebar(
export function UI({ editorState, replaceContainer, setReplaceContainer, ...methods }: IUIProps): JSX.Element {
const [selectedSidebar, setSelectedSidebar] = React.useState<SidebarType>(SidebarType.Components);
const [
selectedExtendedSidebar,
setSelectedExtendedSidebarType
] = React.useState<ExtendedSidebar>(ExtendedSidebar.None);
const [messages, setMessages] = React.useState<IMessage[]>([]);
const current = GetCurrentHistoryState(editorState.history, editorState.historyCurrentStep);
@ -115,7 +122,7 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
replaceContainer={replaceContainer}
setReplaceContainer={setReplaceContainer}/>;
rightSidebarTitle = Text({ textId: '@Elements' });
rightChildren = <ElementsSideBar
rightChildren = <ElementsSidebar
containers={current.containers}
mainContainer={mainContainer}
symbols={current.symbols}
@ -123,32 +130,11 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
onPropertyChange={methods.onPropertyChange}
selectContainer={methods.selectContainer}
addContainer={methods.addContainerAt}
isExpanded ={false}
onExpandChange={() => { setOrToggleSidebar(SidebarType.ComponentsExpanded); } }
/>;
break;
case SidebarType.ComponentsExpanded:
leftSidebarTitle = Text({ textId: '@Components' });
leftChildren = <Components
selectedContainer={selectedContainer}
componentOptions={configuration.AvailableContainers}
categories={configuration.Categories}
buttonOnClick={methods.addOrReplaceContainer}
replaceContainer={replaceContainer}
setReplaceContainer={setReplaceContainer}/>;
rightSidebarTitle = Text({ textId: '@Elements' });
rightChildren = <ElementsSideBar
containers={current.containers}
mainContainer={mainContainer}
symbols={current.symbols}
selectedContainer={selectedContainer}
onPropertyChange={methods.onPropertyChange}
selectContainer={methods.selectContainer}
addContainer={methods.addContainerAt}
isExpanded ={true}
onExpandChange={() => { setOrToggleSidebar(SidebarType.Components); } }
selectedExtendedSidebar={selectedExtendedSidebar}
onExpandChange={(value) => { setSelectedExtendedSidebarType(value); } }
/>;
break;
case SidebarType.Symbols:
leftSidebarTitle = Text({ textId: '@SymbolsLeft' });
leftChildren = <Symbols
@ -161,24 +147,8 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
symbols={current.symbols}
onPropertyChange={methods.onSymbolPropertyChange}
selectSymbol={methods.selectSymbol}
isExpanded ={false}
onExpandChange={() => { setOrToggleSidebar(SidebarType.SymbolsExpanded); } }
/>;
break;
case SidebarType.SymbolsExpanded:
leftSidebarTitle = Text({ textId: '@SymbolsLeft' });
leftChildren = <Symbols
componentOptions={configuration.AvailableSymbols}
buttonOnClick={methods.addSymbol}
/>;
rightSidebarTitle = Text({ textId: '@SymbolsRight' });
rightChildren = <SymbolsSidebar
selectedSymbolId={current.selectedSymbolId}
symbols={current.symbols}
onPropertyChange={methods.onSymbolPropertyChange}
selectSymbol={methods.selectSymbol}
isExpanded ={true}
onExpandChange={() => { setOrToggleSidebar(SidebarType.Symbols); }}
selectedExtendedSidebar={selectedExtendedSidebar}
onExpandChange={(value) => { setSelectedExtendedSidebarType(value); } }
/>;
break;
@ -210,8 +180,10 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
}
const isLeftSidebarOpen = selectedSidebar !== SidebarType.None;
const isRightSidebarOpen = selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.Symbols;
const isRightSidebarOpenExpanded = selectedSidebar === SidebarType.ComponentsExpanded || selectedSidebar === SidebarType.SymbolsExpanded;
const isRightSidebarOpen = [SidebarType.Components, SidebarType.Symbols]
.includes(selectedSidebar);
const isRightSidebarOpenExpanded = isRightSidebarOpen &&
selectedExtendedSidebar !== ExtendedSidebar.None;
const isLeftSidebarOpenClasses = new Set<string>([
'left-sidebar',
@ -252,8 +224,8 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
}
const clickRestrictionsClasses = replaceContainer.isReplacing ? 'pointer-events-none opacity-50' : '';
const isComponentsOpen = selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.ComponentsExpanded;
const isSymbolsOpen = selectedSidebar === SidebarType.Symbols || selectedSidebar === SidebarType.SymbolsExpanded;
const isComponentsOpen = selectedSidebar === SidebarType.Components;
const isSymbolsOpen = selectedSidebar === SidebarType.Symbols;
return (
<>
@ -265,18 +237,10 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
isMessagesOpen={selectedSidebar === SidebarType.Messages}
isSettingsOpen={selectedSidebar === SidebarType.Settings}
toggleComponents={() => {
if (selectedSidebar === SidebarType.ComponentsExpanded) {
setOrToggleSidebar(SidebarType.ComponentsExpanded);
} else {
setOrToggleSidebar(SidebarType.Components);
}
setOrToggleSidebar(SidebarType.Components);
} }
toggleSymbols={() => {
if (selectedSidebar === SidebarType.SymbolsExpanded) {
setOrToggleSidebar(SidebarType.SymbolsExpanded);
} else {
setOrToggleSidebar(SidebarType.Symbols);
}
setOrToggleSidebar(SidebarType.Symbols);
} }
toggleTimeline={() => {
setOrToggleSidebar(SidebarType.History);

View file

@ -1,15 +1,11 @@
import * as React from 'react';
import { type IContainerModel } from '../../Interfaces/IContainerModel';
import { type IHistoryState } from '../../Interfaces/IHistoryState';
import { type IPoint } from '../../Interfaces/IPoint';
import { DIMENSION_MARGIN, USE_EXPERIMENTAL_CANVAS_API } from '../../utils/default';
import { FindContainerById, MakeRecursionDFSIterator } from '../../utils/itertools';
import { USE_EXPERIMENTAL_CANVAS_API } from '../../utils/default';
import { FindContainerById } from '../../utils/itertools';
import { BAR_WIDTH } from '../Bar/Bar';
import { Canvas } from '../Canvas/Canvas';
import { AddDimensions } from '../Canvas/DimensionLayer';
import { RenderSelector } from '../Canvas/Selector';
import { SelectorMode, SVG } from '../SVG/SVG';
import { RenderSymbol } from '../Canvas/Symbol';
import { useState } from 'react';
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
@ -17,27 +13,36 @@ interface IViewerProps {
className: string
current: IHistoryState
selectedContainer: IContainerModel | undefined
selectContainer: (containerId: string) => void
selectedSymbol: ISymbolModel | undefined
margin: number
isComponentsOpen: boolean
isSymbolsOpen: boolean
selectContainer: (containerId: string) => void
}
export interface DrawParams {
mainContainer: IContainerModel
selectorMode: SelectorMode
selectedContainer: IContainerModel | undefined
selectedSymbol: ISymbolModel | undefined
containers: Map<string, IContainerModel>
symbols: Map<string, ISymbolModel>
}
function computeWidth(margin: number): number {
return window.innerWidth - (window.innerWidth < 768 ? BAR_WIDTH : margin);
}
export function Viewer({
className,
current,
selectedContainer,
selectContainer,
selectedSymbol,
margin,
isComponentsOpen,
isSymbolsOpen
isSymbolsOpen,
selectContainer
}: IViewerProps): JSX.Element {
function computeWidth(margin: number): number {
return window.innerWidth - (window.innerWidth < 768 ? BAR_WIDTH : margin);
}
const [windowSize, setWindowSize] = useState([
computeWidth(margin),
window.innerHeight
@ -78,64 +83,22 @@ export function Viewer({
selectorMode = SelectorMode.Symbols;
}
const drawParams: DrawParams = {
mainContainer,
selectorMode,
selectedContainer,
selectedSymbol,
containers: current.containers,
symbols: current.symbols
};
if (USE_EXPERIMENTAL_CANVAS_API) {
function Draw(ctx: CanvasRenderingContext2D, frameCount: number, scale: number, translatePos: IPoint): void {
if (mainContainer === undefined) {
return;
}
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.save();
ctx.setTransform(scale, 0, 0, scale, translatePos.x, translatePos.y);
ctx.fillStyle = '#000000';
const it = MakeRecursionDFSIterator(mainContainer, current.containers, 0, [0, 0]);
for (const { container, depth, currentTransform } of it) {
const [x, y] = [
container.properties.x + currentTransform[0],
container.properties.y + currentTransform[1]
];
// Draw container
RenderContainers(ctx, container, x, y);
// Draw dimensions
RenderDimensions(
ctx,
leftDim,
bottomDim,
topDim,
rightDim,
depth,
scale,
current.containers,
container,
currentTransform
);
// Draw selector
RenderSelector(ctx, frameCount, {
containers: current.containers,
scale,
selected: selectedContainer
});
}
// Draw symbols
RenderSymbols(current, ctx, scale);
ctx.restore();
}
return (
<Canvas
draw={Draw}
className={`ml-16 ${className}`}
className={className}
width={window.innerWidth - BAR_WIDTH}
height={window.innerHeight}
drawParams={drawParams}
/>
);
}
@ -147,58 +110,8 @@ export function Viewer({
viewerHeight={windowSize[1]}
width={mainContainer.properties.width}
height={mainContainer.properties.height}
containers={current.containers}
selectedContainer={selectedContainer}
symbols={current.symbols}
selectedSymbol={selectedSymbol}
selectorMode={selectorMode}
drawParams={drawParams}
selectContainer={selectContainer}
>
{mainContainer}
</SVG>
/>
);
}
function RenderSymbols(
current: IHistoryState,
ctx: CanvasRenderingContext2D,
scale: number
): void {
current.symbols.forEach((symbol) => {
RenderSymbol(symbol, ctx, scale);
});
}
function RenderDimensions(
ctx: CanvasRenderingContext2D,
leftDim: number,
bottomDim: number,
topDim: number,
rightDim: number,
depth: number,
scale: number,
containers: Map<string, IContainerModel>,
container: IContainerModel,
currentTransform: [number, number]
): void {
ctx.save();
const depthOffset = (DIMENSION_MARGIN * (depth + 1)) / scale;
const containerLeftDim = leftDim - depthOffset;
const containerTopDim = topDim - depthOffset;
const containerBottomDim = bottomDim + depthOffset;
const containerRightDim = rightDim + depthOffset;
const dimMapped = [containerLeftDim, containerBottomDim, containerTopDim, containerRightDim];
AddDimensions(ctx, containers, container, dimMapped, currentTransform, scale, depth);
ctx.restore();
}
function RenderContainers(ctx: CanvasRenderingContext2D, container: IContainerModel, x: number, y: number): void {
ctx.save();
ctx.strokeStyle = container.properties.style?.stroke ?? '#000000';
ctx.fillStyle = container.properties.style?.fill ?? '#000000';
ctx.lineWidth = Number(container.properties.style?.strokeWidth ?? 1);
ctx.globalAlpha = Number(container.properties.style?.fillOpacity ?? 1);
ctx.fillRect(x, y, container.properties.width, container.properties.height);
ctx.globalAlpha = Number(container.properties.style?.strokeOpacity ?? 1);
ctx.strokeRect(x, y, container.properties.width, container.properties.height);
ctx.restore();
}

View file

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { PositionReference } from '../Enums/PositionReference';
import { IAvailableContainer } from './IAvailableContainer';
import { IImage } from './IImage';
import { type PositionReference } from '../Enums/PositionReference';
import { type IAvailableContainer } from './IAvailableContainer';
import { type IImage } from './IImage';
/**
* Model of available symbol to configure the application */
@ -13,9 +13,9 @@ export interface IAvailableSymbol {
/** displayed text */
DisplayedText?: string
X?: number
isVertical?: boolean
Y?: number
offset?: number
Width?: number

View file

@ -13,10 +13,10 @@ export interface ISymbolModel {
/** Configuration of the symbol */
config: IAvailableSymbol
/** Horizontal offset */
x: number
isVertical: boolean
// TODO: Implement Y and verticality
/** offset */
offset: number
/** Width */
width: number

View file

@ -22,6 +22,7 @@
"@DeleteContainerTitle": "Delete the container",
"@DeleteSymbol": "Delete",
"@DeleteSymbolTitle": "Delete the container",
"@ReplaceByContainer": "Replace by component",
"@ExportAsJSON": "Export as JSON",
"@ExportAsSVG": "Export as SVG",
@ -75,7 +76,9 @@
"@SymbolName": "Name",
"@SymbolDisplayedText": "Displayed text",
"@SymbolX": "x",
"@SymbolOffset" : "Offset",
"@IsVertical" : "Vertical",
"@ShowDimension" : "Dimension",
"@SymbolHeight": "Height",
"@SymbolWidth": "Width"
}

View file

@ -9,7 +9,7 @@
"@Symbols": "Symboles",
"@SymbolsLeft": "Symboles",
"@SymbolsRight": "Symboles",
"@NoSymbolSelected": "Pas de symbol sélectionné",
"@NoSymbolSelected": "Pas de symbole sélectionné",
"@Timeline": "Chronologie",
"@Messages": "Messages",
"@Settings": "Paramètres",
@ -22,6 +22,7 @@
"@DeleteContainerTitle": "Supprimer le conteneur",
"@DeleteSymbol": "Supprimer",
"@DeleteSymbolTitle": "Supprimer le symbole",
"@ReplaceByContainer": "Replacer par un composant",
"@ExportAsJSON": "Exporter en JSON",
"@ExportAsSVG": "Exporter en SVG",
@ -75,7 +76,9 @@
"@SymbolName": "Nom",
"@SymbolDisplayedText": "Texte affiché",
"@SymbolX": "x",
"@SymbolOffset" : "Décalage",
"@IsVertical" : "Vertical",
"@ShowDimension" : "Cotation",
"@SymbolHeight": "Hauteur",
"@SymbolWidth": "Largeur"
}

View file

@ -2,12 +2,14 @@
"@StartFromScratch": "Partir de zéro",
"@LoadConfigFile": "Charger un fichier de configuration",
"@GoBack": "Revenir",
"@Properties": "Propriétés",
"@Components": "Composants",
"@Elements": "Éléments",
"@Symbols": "Ancrages",
"@SymbolsLeft": "Ancrages",
"@SymbolsRight": "Ancrages",
"@NoSymbolSelected": "Pas d'ancre sélectionnée",
"@Timeline": "Chronologie",
"@Messages": "Messages",
"@Settings": "Paramètres",
@ -61,9 +63,18 @@
"@ContainerShowChildrenDimension": "Afficher les cotations englobante des enfants",
"@ContainerMarkPosition": "Marquer la position pour les parents",
"@ContainerShowDimensionWithMarks": "Afficher les cotations avec les enfants marqués",
"@ContainerShowMarginsDimension": "Afficher les cotations des jeux",
"@ContainerStyle": "Style",
"@StyleStrokeColor": "Couleur du tracé",
"@StyleStrokeDashArray": "Tableau de traits",
"@StyleStroke": "Tracé",
"@StyleStrokeOpacity": "Opacité du tracé",
"@StyleStrokeWidth": "Epaisseur du tracé",
"@StyleFill": "Remplissage",
"@StyleFillOpacity": "Opacité du remplissage",
"@SymbolName": "Nom",
"@SymbolDisplayedText": "Texte affiché",
"@SymbolX": "x",
"@SymbolHeight": "Hauteur",
"@SymbolWidth": "Largeur"

View file

@ -9,6 +9,7 @@ import { type ISymbolModel } from '../Interfaces/ISymbolModel';
import { Orientation } from '../Enums/Orientation';
import { AppState } from '../Enums/AppState';
import { type IDimensionOptions } from '../Interfaces/IDimensionOptions';
import { type IDimensionStyle } from '../Components/SVG/Elements/Dimension';
/// EDITOR DEFAULTS ///
@ -69,10 +70,14 @@ export const SHOW_BORROWER_DIMENSIONS = true;
export const DIMENSION_MARGIN = 50;
export const SYMBOL_MARGIN = 25;
export const NOTCHES_LENGTH = 10;
export const DEFAULT_DIMENSION_SYMBOL_STYLE: IDimensionStyle = {
color: '#000000'
};
/// SYMBOL DEFAULTS ///
export const DEFAULT_SYMBOL_WIDTH = 32;
export const DEFAULT_SYMBOL_IS_VERTICAL = false;
export const DEFAULT_SYMBOL_HEIGHT = 32;
/**
@ -270,7 +275,7 @@ export function GetDefaultContainerProps(type: string,
hideChildrenInTreeview: containerConfig.HideChildrenInTreeview ?? false,
dimensionOptions: {
childrenDimensions: {
...containerConfig.DimensionOptions?.selfDimensions,
...containerConfig.DimensionOptions?.childrenDimensions,
positions: containerConfig.DimensionOptions?.childrenDimensions.positions ?? []
},
selfDimensions: {
@ -304,7 +309,8 @@ export function GetDefaultSymbolModel(name: string,
displayedText: symbolConfig.DisplayedText ?? id,
type: name,
config: structuredClone(symbolConfig),
x: 0,
offset: 0,
isVertical: symbolConfig.isVertical ?? DEFAULT_SYMBOL_IS_VERTICAL,
width: symbolConfig.Width ?? DEFAULT_SYMBOL_WIDTH,
height: symbolConfig.Height ?? DEFAULT_SYMBOL_HEIGHT,
linkedContainers: new Set(),

View file

@ -81,11 +81,10 @@ const GetSVGLayoutConfiguration = () => {
Style: {
fillOpacity: 1,
strokeWidth: 2,
stroke: 'red',
stroke: '#ff0000',
fill: '#d3c9b7',
},
ShowSelfDimensions: [0, 2],
ShowDimensionWithMarks: [1, 3],
IsFlex: true,
Category: "Stuff"
},
{
@ -97,11 +96,10 @@ const GetSVGLayoutConfiguration = () => {
Style: {
fillOpacity: 1,
strokeWidth: 2,
stroke: 'red',
stroke: '#ff0000',
fill: '#d3c9b7',
},
ShowSelfDimensions: [0, 2],
ShowDimensionWithMarks: [1, 3],
IsFlex: true,
Category: "Stuff"
},
{
@ -117,10 +115,11 @@ const GetSVGLayoutConfiguration = () => {
Style: {
fillOpacity: 1,
strokeWidth: 2,
stroke: 'green',
fill: 'white'
stroke: '#00ff00',
fill: '#ffffff'
},
Category: "Stuff",
IsFlex: true,
Actions: [
{
Id: "Insert",
@ -173,6 +172,7 @@ const GetSVGLayoutConfiguration = () => {
<line x1="{width}" y1="0" x2="0" y2="{height}" stroke="black" style='{userData.styleLine}'></line>
`
,
IsFlex: true,
Actions: [
{
Id: "SplitRemplissage",
@ -254,22 +254,24 @@ const GetSVGLayoutConfiguration = () => {
Type: '200',
MaxWidth: 500,
MinWidth: 200,
IsFlex: true,
Style: {
fillOpacity: 1,
strokeWidth: 2,
stroke: 'blue',
fill: 'blue',
stroke: '#0000ff',
fill: '#0000ff',
}
},
{
Type: '400',
MaxWidth: 500,
MinWidth: 400,
IsFlex: true,
Style: {
fillOpacity: 1,
strokeWidth: 2,
stroke: 'red',
fill: 'red',
stroke: '#ff0000',
fill: '#ff0000',
}
}
],
@ -284,8 +286,8 @@ const GetSVGLayoutConfiguration = () => {
Svg: null,
Url: 'https://www.manutan.fr/img/S/GRP/ST/AIG3930272.jpg'
},
Name: 'Poteau structure',
PositionReference: 1,
Name: 'Poteau CenterCenter',
PositionReference: 4,
AssociatedContainer: {
Type: 'Montant'
}
@ -295,12 +297,48 @@ const GetSVGLayoutConfiguration = () => {
Height: 32,
Image: {
Base64Image: null,
Name: 'Arrow',
Name: 'ArrowTopLeft',
Svg: null,
Url: './images/arrow-down.svg'
},
Name: 'Arrow',
PositionReference: 1
Name: 'ArrowTopLeft',
PositionReference: 0
},
{
Width: 32,
Height: 32,
Image: {
Base64Image: null,
Name: 'ArrowTopRight',
Svg: null,
Url: './images/arrow-down.svg'
},
Name: 'ArrowTopRight',
PositionReference: 2
},
{
Width: 32,
Height: 32,
Image: {
Base64Image: null,
Name: 'ArrowCenterRight',
Svg: null,
Url: './images/arrow-down.svg'
},
Name: 'ArrowCenterRight',
PositionReference: 5
},
{
Width: 32,
Height: 32,
Image: {
Base64Image: null,
Name: 'ArrowBottomRight',
Svg: null,
Url: './images/arrow-down.svg'
},
Name: 'ArrowBottomRight',
PositionReference: 8
},
{
Width: 32,
@ -329,10 +367,10 @@ const GetSVGLayoutConfiguration = () => {
MainContainer: {
Type: 'main',
Width: 800,
Height: 800,
Orientation: 1,
Height: 200,
Orientation: 0,
Style: {
stroke: 'black',
stroke: '#000000',
strokeWidth: 2,
fillOpacity: 0
}