204 lines
6 KiB
TypeScript
204 lines
6 KiB
TypeScript
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 { 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';
|
|
|
|
interface IViewerProps {
|
|
className: string
|
|
current: IHistoryState
|
|
selectedContainer: IContainerModel | undefined
|
|
selectContainer: (containerId: string) => void
|
|
selectedSymbol: ISymbolModel | undefined
|
|
margin: number
|
|
isComponentsOpen: boolean
|
|
isSymbolsOpen: boolean
|
|
}
|
|
|
|
export function Viewer({
|
|
className,
|
|
current,
|
|
selectedContainer,
|
|
selectContainer,
|
|
selectedSymbol,
|
|
margin,
|
|
isComponentsOpen,
|
|
isSymbolsOpen
|
|
}: 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
|
|
]);
|
|
|
|
React.useEffect(() => {
|
|
function SVGAutoResizer(): void {
|
|
setWindowSize([
|
|
computeWidth(margin),
|
|
window.innerHeight
|
|
]);
|
|
}
|
|
|
|
window.addEventListener('resize', SVGAutoResizer);
|
|
|
|
return () => {
|
|
window.removeEventListener('resize', SVGAutoResizer);
|
|
};
|
|
});
|
|
|
|
React.useEffect(() => {
|
|
setWindowSize([
|
|
computeWidth(margin),
|
|
window.innerHeight
|
|
]);
|
|
}, [margin]);
|
|
|
|
const mainContainer = FindContainerById(current.containers, current.mainContainer);
|
|
|
|
if (mainContainer === undefined) {
|
|
return <></>;
|
|
}
|
|
|
|
let selectorMode = SelectorMode.Nothing;
|
|
if (isComponentsOpen) {
|
|
selectorMode = SelectorMode.Containers;
|
|
} else if (isSymbolsOpen) {
|
|
selectorMode = SelectorMode.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}`}
|
|
width={window.innerWidth - BAR_WIDTH}
|
|
height={window.innerHeight}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<SVG
|
|
className={className}
|
|
viewerWidth={windowSize[0]}
|
|
viewerHeight={windowSize[1]}
|
|
width={mainContainer.properties.width}
|
|
height={mainContainer.properties.height}
|
|
containers={current.containers}
|
|
selectedContainer={selectedContainer}
|
|
symbols={current.symbols}
|
|
selectedSymbol={selectedSymbol}
|
|
selectorMode={selectorMode}
|
|
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();
|
|
}
|