diff --git a/src/Components/Editor/Actions/Save.ts b/src/Components/Editor/Actions/Save.ts index ae7b4e9..2dc2f95 100644 --- a/src/Components/Editor/Actions/Save.ts +++ b/src/Components/Editor/Actions/Save.ts @@ -53,6 +53,7 @@ export function SaveEditorAsSVG(): void { svg.replaceChildren(...mainSvg); // remove the selector + // TODO: Fix this with SelectorMode != Nothing or with some html magic const group = svg.children[svg.children.length - 1]; group.removeChild(group.children[group.children.length - 1]); if (SHOW_SELECTOR_TEXT) { diff --git a/src/Components/SVG/Elements/Selector/Selector.scss b/src/Components/SVG/Elements/Selector.scss similarity index 100% rename from src/Components/SVG/Elements/Selector/Selector.scss rename to src/Components/SVG/Elements/Selector.scss diff --git a/src/Components/SVG/Elements/Selector/Selector.tsx b/src/Components/SVG/Elements/Selector/Selector.tsx index c7baf92..1a8fb3e 100644 --- a/src/Components/SVG/Elements/Selector/Selector.tsx +++ b/src/Components/SVG/Elements/Selector/Selector.tsx @@ -1,38 +1,18 @@ -import './Selector.scss'; +import '../Selector.scss'; import * as React from 'react'; -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 - selected?: IContainerModel - scale?: number + text: string + x: number + y: number + width: number + height: number + scale: number + style?: React.CSSProperties } -export function Selector(props: ISelectorProps): JSX.Element { - 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 Selector({ text, x, y, width, height, scale, style: overrideStyle }: ISelectorProps): JSX.Element { const xText = x + width / 2; const yText = y + height / 2; @@ -43,7 +23,8 @@ export function Selector(props: ISelectorProps): JSX.Element { transitionProperty: 'all', transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)', transitionDuration: '150ms', - animation: 'fadein 750ms ease-in alternate infinite' + animation: 'fadein 750ms ease-in alternate infinite', + ...overrideStyle }; return ( @@ -65,7 +46,7 @@ export function Selector(props: ISelectorProps): JSX.Element { transformBox: 'fill-box' }} > - {props.selected.properties.displayedText} + { text } : null} diff --git a/src/Components/SVG/Elements/SelectorContainer/SelectorContainer.tsx b/src/Components/SVG/Elements/SelectorContainer/SelectorContainer.tsx new file mode 100644 index 0000000..c2ad3e2 --- /dev/null +++ b/src/Components/SVG/Elements/SelectorContainer/SelectorContainer.tsx @@ -0,0 +1,46 @@ +import '../Selector.scss'; +import * as React from 'react'; +import { type IContainerModel } from '../../../../Interfaces/IContainerModel'; +import { GetAbsolutePosition } from '../../../../utils/itertools'; +import { RemoveMargin } from '../../../../utils/svg'; +import { Selector } from '../Selector/Selector'; + +interface ISelectorContainerProps { + containers: Map + selected?: IContainerModel + scale?: number +} + +export function SelectorContainer(props: ISelectorContainerProps): JSX.Element { + 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 + )); + + return ( + + ); +} diff --git a/src/Components/SVG/Elements/SelectorSymbol/SelectorSymbol.tsx b/src/Components/SVG/Elements/SelectorSymbol/SelectorSymbol.tsx new file mode 100644 index 0000000..01133fb --- /dev/null +++ b/src/Components/SVG/Elements/SelectorSymbol/SelectorSymbol.tsx @@ -0,0 +1,47 @@ +import '../Selector.scss'; +import * as React from 'react'; +import { SYMBOL_MARGIN } from '../../../../utils/default'; +import { type ISymbolModel } from '../../../../Interfaces/ISymbolModel'; +import { Selector } from '../Selector/Selector'; + +interface ISelectorSymbolProps { + symbols: Map + selected?: ISymbolModel + scale?: number +} + +export function SelectorSymbol(props: ISelectorSymbolProps): JSX.Element { + if (props.selected === undefined || props.selected === null) { + return ( + + + ); + } + + const scale = (props.scale ?? 1); + const [width, height] = [ + props.selected.width / scale, + props.selected.height / scale + ]; + + const [x, y] = [ + props.selected.x + props.selected.width / 2, + -SYMBOL_MARGIN - height]; + + const style: React.CSSProperties = { + transform: 'translateX(-50%)', + transformBox: 'fill-box' + }; + + return ( + + ); +} diff --git a/src/Components/SVG/SVG.tsx b/src/Components/SVG/SVG.tsx index dc80773..4b49213 100644 --- a/src/Components/SVG/SVG.tsx +++ b/src/Components/SVG/SVG.tsx @@ -1,12 +1,13 @@ 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 { SelectorContainer } from './Elements/SelectorContainer/SelectorContainer'; import { MAX_FRAMERATE } 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 @@ -16,11 +17,19 @@ interface ISVGProps { height: number containers: Map children: IContainerModel - selected?: IContainerModel + selectedContainer?: IContainerModel symbols: Map + selectedSymbol?: ISymbolModel + selectorMode: SelectorMode selectContainer: (containerId: string) => void } +export enum SelectorMode { + Nothing, + Containers, + Symbols +} + export const ID = 'svg'; export function SVG(props: ISVGProps): JSX.Element { @@ -48,8 +57,7 @@ export function SVG(props: ISVGProps): JSX.Element { xmlns }; - let children: React.ReactNode | React.ReactNode[] = []; - children = ; + function Selector(): JSX.Element { + switch (props.selectorMode) { + case SelectorMode.Containers: + return ; + case SelectorMode.Symbols: + return ; + default: + return <>; + } + } + return (
- {/* leave this at the end so it can be removed during the svg export */} +
diff --git a/src/Components/SymbolsList/SymbolsSidebar.tsx b/src/Components/SymbolsList/SymbolsSidebar.tsx index a43e8ae..300dcf9 100644 --- a/src/Components/SymbolsList/SymbolsSidebar.tsx +++ b/src/Components/SymbolsList/SymbolsSidebar.tsx @@ -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 ( - - ); - } const selectedSymbol = props.symbols.get(props.selectedSymbolId); return (
@@ -71,4 +50,27 @@ export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
); + + 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 ( + + ); + } } diff --git a/src/Components/UI/UI.tsx b/src/Components/UI/UI.tsx index f85b63c..01d5407 100644 --- a/src/Components/UI/UI.tsx +++ b/src/Components/UI/UI.tsx @@ -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 = 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 ( <> 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); @@ -64,6 +71,13 @@ export function Viewer({ 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) { @@ -134,11 +148,12 @@ export function Viewer({ width={mainContainer.properties.width} height={mainContainer.properties.height} containers={current.containers} - selected={selectedContainer} + selectedContainer={selectedContainer} symbols={current.symbols} + selectedSymbol={selectedSymbol} + selectorMode={selectorMode} selectContainer={selectContainer} > - {mainContainer} );