Merged PR 162: Implement symbols and other stuff (see desc)

Implement symbols
- Add, Remove, Select Container
- Form
- Link with container
- Symbol behavior application to container (move to x with xpositionreference)

Important changes
- Remove SelectedContainer from HistoryState, meaning that it will be slower for each load but will be faster for each operations* (SetHistory, SelectContainer, DeleteContainer, SymbolOperations)
- ElementsSidebar now opens with isSidebarOpen meaning that both sidebar will open on toggle
- Moved camelize, transformX, restoreX to different modules (stringtools.ts, svg.ts)
This commit is contained in:
Eric Nguyen 2022-08-22 13:58:32 +00:00
parent 58ef28fe89
commit 8b8d88f885
48 changed files with 1453 additions and 188 deletions

View file

@ -1,11 +1,12 @@
import * as React from 'react';
import { Interweave, Node } from 'interweave';
import { XPositionReference } from '../../../Enums/XPositionReference';
import { IContainerModel } from '../../../Interfaces/IContainerModel';
import { DIMENSION_MARGIN, SHOW_CHILDREN_DIMENSIONS, SHOW_PARENT_DIMENSION, SHOW_TEXT } from '../../../utils/default';
import { getDepth } from '../../../utils/itertools';
import { Dimension } from './Dimension';
import IProperties from '../../../Interfaces/IProperties';
import IContainerProperties from '../../../Interfaces/IContainerProperties';
import { transformX } from '../../../utils/svg';
import { camelize } from '../../../utils/stringtools';
interface IContainerProps {
model: IContainerModel
@ -45,7 +46,7 @@ export const Container: React.FC<IContainerProps> = (props: IContainerProps) =>
</rect>);
// Dimension props
const depth = getDepth(props.model);
const dimensionMargin = DIMENSION_MARGIN * (depth + 1);
const dimensionMargin = DIMENSION_MARGIN * depth;
const id = `dim-${props.model.properties.id}`;
const xStart: number = 0;
const xEnd = props.model.properties.width;
@ -132,27 +133,7 @@ function GetChildrenDimensionProps(props: IContainerProps, dimensionMargin: numb
return { childrenId, xChildrenStart, xChildrenEnd, yChildren, textChildren };
}
export function transformX(x: number, width: number, xPositionReference = XPositionReference.Left): number {
let transformedX = x;
if (xPositionReference === XPositionReference.Center) {
transformedX += width / 2;
} else if (xPositionReference === XPositionReference.Right) {
transformedX += width;
}
return transformedX;
}
export function restoreX(x: number, width: number, xPositionReference = XPositionReference.Left): number {
let transformedX = x;
if (xPositionReference === XPositionReference.Center) {
transformedX -= width / 2;
} else if (xPositionReference === XPositionReference.Right) {
transformedX -= width;
}
return transformedX;
}
function CreateReactCustomSVG(customSVG: string, props: IProperties): React.ReactNode {
function CreateReactCustomSVG(customSVG: string, props: IContainerProperties): React.ReactNode {
return <Interweave
tagName='g'
disableLineBreaks={true}
@ -162,7 +143,7 @@ function CreateReactCustomSVG(customSVG: string, props: IProperties): React.Reac
/>;
}
function transform(node: HTMLElement, children: Node[], props: IProperties): React.ReactNode {
function transform(node: HTMLElement, children: Node[], props: IContainerProperties): React.ReactNode {
const supportedTags = ['line', 'path', 'rect'];
if (supportedTags.includes(node.tagName.toLowerCase())) {
const attributes: {[att: string]: string | object | null} = {};
@ -207,7 +188,3 @@ function transform(node: HTMLElement, children: Node[], props: IProperties): Rea
}
return undefined;
}
function camelize(str: string): any {
return str.split('-').map((word, index) => index > 0 ? word.charAt(0).toUpperCase() + word.slice(1) : word).join('');
}

View file

@ -2,7 +2,7 @@ import * as React from 'react';
import { ContainerModel } from '../../../Interfaces/IContainerModel';
import { DIMENSION_MARGIN } from '../../../utils/default';
import { getAbsolutePosition, MakeBFSIterator } from '../../../utils/itertools';
import { transformX } from './Container';
import { transformX } from '../../../utils/svg';
import { Dimension } from './Dimension';
interface IDimensionLayerProps {

View file

@ -3,7 +3,7 @@ import { IContainerModel } from '../../../Interfaces/IContainerModel';
import { getAbsolutePosition } from '../../../utils/itertools';
interface ISelectorProps {
selected: IContainerModel | null
selected?: IContainerModel
}
export const Selector: React.FC<ISelectorProps> = (props) => {

View file

@ -0,0 +1,39 @@
import { Interweave } from 'interweave';
import * as React from 'react';
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { DIMENSION_MARGIN } from '../../../utils/default';
interface ISymbolProps {
model: ISymbolModel
}
export const Symbol: React.FC<ISymbolProps> = (props) => {
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;
if (hasSVG) {
return (
<g
x={props.model.x}
y={-DIMENSION_MARGIN}
>
<Interweave
noWrap={true}
disableLineBreaks={true}
content={props.model.config.Image.Svg}
allowElements={true}
/>
</g>
);
}
return (
<image
href={href}
x={props.model.x}
y={-DIMENSION_MARGIN}
height={props.model.height}
width={props.model.width}
/>
);
};

View file

@ -0,0 +1,23 @@
import * as React from 'react';
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { Symbol } from './Symbol';
interface ISymbolLayerProps {
symbols: Map<string, ISymbolModel>
}
export const SymbolLayer: React.FC<ISymbolLayerProps> = (props) => {
const symbols: JSX.Element[] = [];
props.symbols.forEach((symbol) => {
symbols.push(
<Symbol key={`symbol-${symbol.id}`} model={symbol} />
);
});
return (
<g>
{
symbols
}
</g>
);
};

View file

@ -4,15 +4,17 @@ import { Container } from './Elements/Container';
import { ContainerModel } from '../../Interfaces/IContainerModel';
import { Selector } from './Elements/Selector';
import { BAR_WIDTH } from '../Bar/Bar';
import { DimensionLayer } from './Elements/DimensionLayer';
import { DepthDimensionLayer } from './Elements/DepthDimensionLayer';
import { SHOW_DIMENSIONS_PER_DEPTH } from '../../utils/default';
import { SymbolLayer } from './Elements/SymbolLayer';
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
interface ISVGProps {
width: number
height: number
children: ContainerModel | ContainerModel[] | null
selected: ContainerModel | null
selected?: ContainerModel
symbols: Map<string, ISymbolModel>
}
interface Viewer {
@ -81,6 +83,7 @@ export const SVG: React.FC<ISVGProps> = (props: ISVGProps) => {
? <DepthDimensionLayer roots={props.children}/>
: null
}
<SymbolLayer symbols={props.symbols} />
<Selector selected={props.selected} /> {/* leave this at the end so it can be removed during the svg export */}
</svg>
</UncontrolledReactSVGPanZoom>