Implement Highlight on symbols

This commit is contained in:
Carl Fuchs 2023-02-09 15:30:31 +01:00
parent c6c4bd1e32
commit 579422653b
7 changed files with 138 additions and 50 deletions

View file

@ -1,17 +1,17 @@
import './Selector.scss';
import '../Selector.scss';
import * as React from 'react';
import { IContainerModel } from '../../../../Interfaces/IContainerModel';
import { type IContainerModel } from '../../../../Interfaces/IContainerModel';
import { SHOW_SELECTOR_TEXT } from '../../../../utils/default';
import { GetAbsolutePosition } from '../../../../utils/itertools';
import { RemoveMargin } from '../../../../utils/svg';
interface ISelectorProps {
interface ISelectorContainerProps {
containers: Map<string, IContainerModel>
selected?: IContainerModel
scale?: number
}
export function Selector(props: ISelectorProps): JSX.Element {
export function SelectorContainer(props: ISelectorContainerProps): JSX.Element {
if (props.selected === undefined || props.selected === null) {
return (
<rect visibility={'hidden'}>

View file

@ -0,0 +1,65 @@
import '../Selector.scss';
import * as React from 'react';
import { SHOW_SELECTOR_TEXT, SYMBOL_MARGIN } from '../../../../utils/default';
import { type ISymbolModel } from '../../../../Interfaces/ISymbolModel';
interface ISelectorSymbolProps {
symbols: Map<string, ISymbolModel>
selected?: ISymbolModel
scale?: number
}
export function SelectorSymbol(props: ISelectorSymbolProps): JSX.Element {
if (props.selected === undefined || props.selected === null) {
return (
<rect visibility={'hidden'}>
</rect>
);
}
const scale = (props.scale ?? 1);
const [width, height] = [
props.selected.width,
props.selected.height / scale
];
const [x, y] = [props.selected.x, -SYMBOL_MARGIN - height];
const xText = x + width / 2;
const yText = y + height / 2;
const style: React.CSSProperties = {
stroke: '#3B82F6',
strokeWidth: 4 / scale,
fillOpacity: 0,
transitionProperty: 'all',
transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',
transitionDuration: '150ms',
animation: 'fadein 750ms ease-in alternate infinite'
};
return (
<>
<rect
x={x}
y={y}
width={width}
height={height}
style={style}
>
</rect>
{SHOW_SELECTOR_TEXT
? <text
x={xText}
y={yText}
style={{
transform: `scale(${1 / scale}) translateX(-50%)`,
transformBox: 'fill-box'
}}
>
{props.selected.displayedText}
</text>
: null}
</>
);
}

View file

@ -1,13 +1,14 @@
import * as React from 'react';
import { ReactSVGPanZoom, Tool, TOOL_PAN, Value } from 'react-svg-pan-zoom';
import { ReactSVGPanZoom, type Tool, TOOL_PAN, type Value } from 'react-svg-pan-zoom';
import { Container } from './Elements/Container';
import { IContainerModel } from '../../Interfaces/IContainerModel';
import { Selector } from './Elements/Selector/Selector';
import { type IContainerModel } from '../../Interfaces/IContainerModel';
import { SelectorContainer } from './Elements/SelectorContainer/SelectorContainer';
import { DepthDimensionLayer } from './Elements/DepthDimensionLayer';
import { MAX_FRAMERATE, SHOW_DIMENSIONS_PER_DEPTH } from '../../utils/default';
import { SymbolLayer } from './Elements/SymbolLayer';
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import { DimensionLayer } from './Elements/DimensionLayer';
import { SelectorSymbol } from './Elements/SelectorSymbol/SelectorSymbol';
interface ISVGProps {
className?: string
@ -17,9 +18,12 @@ interface ISVGProps {
height: number
containers: Map<string, IContainerModel>
children: IContainerModel
selected?: IContainerModel
selectedContainer?: IContainerModel
symbols: Map<string, ISymbolModel>
selectedSymbol?: ISymbolModel
selectContainer: (containerId: string) => void
isComponentsOpen: boolean
isSymbolsOpen: boolean
}
export const ID = 'svg';
@ -49,8 +53,7 @@ export function SVG(props: ISVGProps): JSX.Element {
xmlns
};
let children: React.ReactNode | React.ReactNode[] = [];
children = <Container
const children: React.ReactNode | React.ReactNode[] = <Container
key={`container-${props.children.properties.id}`}
containers={props.containers}
model={props.children}
@ -99,7 +102,9 @@ export function SVG(props: ISVGProps): JSX.Element {
: null}
<DimensionLayer containers={props.containers} symbols={props.symbols} scale={scale} root={props.children} />
<SymbolLayer scale={scale} symbols={props.symbols} />
<Selector containers={props.containers} scale={scale} selected={props.selected} /> {/* leave this at the end so it can be removed during the svg export */}
{/* leave this at the end so it can be removed during the svg export */}
{ props.isComponentsOpen ? <SelectorContainer containers={props.containers} scale={scale} selected={props.selectedContainer} /> : null }
{ props.isSymbolsOpen ? <SelectorSymbol symbols={props.symbols} scale={scale} selected={props.selectedSymbol} /> : null }
</svg>
</ReactSVGPanZoom>
</div>

View file

@ -1,7 +1,7 @@
import * as React from 'react';
import useSize from '@react-hook/size';
import { FixedSizeList as List } from 'react-window';
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import { SymbolProperties } from '../SymbolProperties/SymbolProperties';
import { ToggleSideBar } from '../Sidebar/ToggleSideBar/ToggleSideBar';
import { Text } from '../Text/Text';
@ -23,28 +23,7 @@ export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
const [showProperties, setShowProperties] = useState(props.isExpanded);
// Render
const symbols = [...props.symbols.values()];
function Row({ index, style }: { index: number, style: React.CSSProperties }): JSX.Element {
const symbol = symbols[index];
const key = symbol.id;
const text = symbol.displayedText;
const selectedClass: string = props.selectedSymbolId !== '' &&
props.selectedSymbolId === symbol.id
? 'border-l-4 bg-slate-400/60 hover:bg-slate-400'
: 'bg-slate-300/60 hover:bg-slate-300';
return (
<button type="button"
className={`w-full border-blue-500 symbols-sidebar-row whitespace-pre
text-left text-sm font-medium transition-all ${selectedClass}`}
id={key}
key={key}
style={style}
onClick={() => props.selectSymbol(key)}
>
{text}
</button>
);
}
const selectedSymbol = props.symbols.get(props.selectedSymbolId);
return (
<div className='flex flex-row h-full w-full'>
@ -71,4 +50,27 @@ export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
</div>
</div>
);
function Row({ index, style }: { index: number, style: React.CSSProperties }): JSX.Element {
const symbol = symbols[index];
const key = symbol.id;
const text = symbol.displayedText;
const selectedClass: string = props.selectedSymbolId !== '' &&
props.selectedSymbolId === symbol.id
? 'border-l-4 bg-slate-400/60 hover:bg-slate-400'
: 'bg-slate-300/60 hover:bg-slate-300';
return (
<button type="button"
className={`w-full border-blue-500 symbols-sidebar-row whitespace-pre
text-left text-sm font-medium transition-all ${selectedClass}`}
id={key}
key={key}
style={style}
onClick={() => { props.selectSymbol(key); }}
>
{text}
</button>
);
}
}

View file

@ -4,17 +4,17 @@ import { History } from '../History/History';
import { Bar, BAR_WIDTH } from '../Bar/Bar';
import { Symbols } from '../Symbols/Symbols';
import { SymbolsSidebar } from '../SymbolsList/SymbolsSidebar';
import { PropertyType } from '../../Enums/PropertyType';
import { type PropertyType } from '../../Enums/PropertyType';
import { Messages } from '../Messages/Messages';
import { Sidebar } from '../Sidebar/Sidebar';
import { Components } from '../Components/Components';
import { Viewer } from '../Viewer/Viewer';
import { Settings } from '../Settings/Settings';
import { IMessage } from '../../Interfaces/IMessage';
import { type IMessage } from '../../Interfaces/IMessage';
import { DISABLE_API } from '../../utils/default';
import { UseWorker, UseAsync } from './UseWorker';
import { FindContainerById } from '../../utils/itertools';
import { IEditorState } from '../../Interfaces/IEditorState';
import { type IEditorState } from '../../Interfaces/IEditorState';
import { GetCurrentHistoryState } from '../Editor/Editor';
import { Text } from '../Text/Text';
@ -96,6 +96,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
}
const selectedContainer = FindContainerById(current.containers, current.selectedContainerId);
const selectedSymbol = current.symbols.get(current.selectedSymbolId);
switch (selectedSidebar) {
case SidebarType.Components:
@ -116,7 +117,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
selectContainer={methods.selectContainer}
addContainer={methods.addContainerAt}
isExpanded ={false}
onExpandChange={() => setOrToggleSidebar(SidebarType.ComponentsExpanded) }
onExpandChange={() => { setOrToggleSidebar(SidebarType.ComponentsExpanded); } }
/>;
break;
case SidebarType.ComponentsExpanded:
@ -137,7 +138,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
selectContainer={methods.selectContainer}
addContainer={methods.addContainerAt}
isExpanded ={true}
onExpandChange={() => setOrToggleSidebar(SidebarType.Components) }
onExpandChange={() => { setOrToggleSidebar(SidebarType.Components); } }
/>;
break;
case SidebarType.Symbols:
@ -153,7 +154,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
onPropertyChange={methods.onSymbolPropertyChange}
selectSymbol={methods.selectSymbol}
isExpanded ={false}
onExpandChange={() => setOrToggleSidebar(SidebarType.SymbolsExpanded) }
onExpandChange={() => { setOrToggleSidebar(SidebarType.SymbolsExpanded); } }
/>;
break;
case SidebarType.SymbolsExpanded:
@ -169,7 +170,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
onPropertyChange={methods.onSymbolPropertyChange}
selectSymbol={methods.selectSymbol}
isExpanded ={true}
onExpandChange={() => setOrToggleSidebar(SidebarType.Symbols)}
onExpandChange={() => { setOrToggleSidebar(SidebarType.Symbols); }}
/>;
break;
@ -187,7 +188,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
leftChildren = <Messages
historyState={current}
messages={messages}
clearMessage={() => setMessages([])}
clearMessage={() => { setMessages([]); }}
/>;
break;
@ -242,11 +243,14 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
isLeftSidebarOpenClasses.add('left-sidebar-single');
}
const isComponentsOpen = selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.ComponentsExpanded;
const isSymbolsOpen = selectedSidebar === SidebarType.Symbols || selectedSidebar === SidebarType.SymbolsExpanded;
return (
<>
<Bar
isComponentsOpen={selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.ComponentsExpanded}
isSymbolsOpen={selectedSidebar === SidebarType.Symbols || selectedSidebar === SidebarType.SymbolsExpanded}
isComponentsOpen={isComponentsOpen}
isSymbolsOpen={isSymbolsOpen}
isHistoryOpen={selectedSidebar === SidebarType.History}
isMessagesOpen={selectedSidebar === SidebarType.Messages}
isSettingsOpen={selectedSidebar === SidebarType.Settings}
@ -283,8 +287,11 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
<Viewer
className={`${[...viewerMarginClasses.values()].join(' ')} w-full h-full`}
current={current}
isComponentsOpen={isComponentsOpen}
isSymbolsOpen={isSymbolsOpen}
selectedContainer={selectedContainer}
selectContainer={methods.selectContainer}
selectedSymbol={selectedSymbol}
margin={marginSidebar}
/>
<Sidebar

View file

@ -1,7 +1,7 @@
import * as React from 'react';
import { IContainerModel } from '../../Interfaces/IContainerModel';
import { IHistoryState } from '../../Interfaces/IHistoryState';
import { IPoint } from '../../Interfaces/IPoint';
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';
@ -11,13 +11,17 @@ import { RenderSelector } from '../Canvas/Selector';
import { 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({
@ -25,7 +29,10 @@ export function Viewer({
current,
selectedContainer,
selectContainer,
margin
selectedSymbol,
margin,
isComponentsOpen,
isSymbolsOpen
}: IViewerProps): JSX.Element {
function computeWidth(margin: number): number {
return window.innerWidth - (window.innerWidth < 768 ? BAR_WIDTH : margin);
@ -134,11 +141,13 @@ export function Viewer({
width={mainContainer.properties.width}
height={mainContainer.properties.height}
containers={current.containers}
selected={selectedContainer}
selectedContainer={selectedContainer}
symbols={current.symbols}
selectedSymbol={selectedSymbol}
selectContainer={selectContainer}
isComponentsOpen={isComponentsOpen}
isSymbolsOpen={isSymbolsOpen}
>
{mainContainer}
</SVG>
);