Merged PR 315: ErgonomieTreeView

Affichage fonctionnelle sous forme de toolbar vertical, comme dans les IDE

Related work items: #7976
This commit is contained in:
Carl Fuchs 2023-02-07 14:25:53 +00:00 committed by Eric Nguyen
parent acb5ba2d82
commit 8a99ef4cfd
13 changed files with 240 additions and 133 deletions

View file

@ -4,11 +4,11 @@ import { expect, describe, it, vi } from 'vitest';
import { PositionReference } from '../../Enums/PositionReference'; import { PositionReference } from '../../Enums/PositionReference';
import { IContainerProperties } from '../../Interfaces/IContainerProperties'; import { IContainerProperties } from '../../Interfaces/IContainerProperties';
import { Orientation } from '../../Enums/Orientation'; import { Orientation } from '../../Enums/Orientation';
import { Properties } from './ContainerProperties'; import { ContainerProperties } from './ContainerProperties';
describe.concurrent('Properties', () => { describe.concurrent('Properties', () => {
it('No properties', () => { it('No properties', () => {
render(<Properties render(<ContainerProperties
properties={undefined} properties={undefined}
onChange={() => {}} onChange={() => {}}
symbols={new Map()} symbols={new Map()}
@ -67,7 +67,7 @@ describe.concurrent('Properties', () => {
(prop as any)[key] = value; (prop as any)[key] = value;
}); });
const { container, rerender } = render(<Properties const { container, rerender } = render(<ContainerProperties
properties={prop} properties={prop}
onChange={handleChange} onChange={handleChange}
symbols={new Map()} symbols={new Map()}
@ -108,7 +108,7 @@ describe.concurrent('Properties', () => {
expect(prop.parentId).toBe('parentId'); expect(prop.parentId).toBe('parentId');
expect(prop.x).toBe(2); expect(prop.x).toBe(2);
expect(prop.y).toBe(2); expect(prop.y).toBe(2);
rerender(<Properties rerender(<ContainerProperties
properties={Object.assign({}, prop)} properties={Object.assign({}, prop)}
onChange={handleChange} onChange={handleChange}
symbols={new Map()} symbols={new Map()}

View file

@ -10,7 +10,7 @@ interface IPropertiesProps {
onChange: (key: string, value: string | number | boolean | number[], type?: PropertyType) => void onChange: (key: string, value: string | number | boolean | number[], type?: PropertyType) => void
} }
export function Properties(props: IPropertiesProps): JSX.Element { export function ContainerProperties(props: IPropertiesProps): JSX.Element {
if (props.properties === undefined) { if (props.properties === undefined) {
return <div></div>; return <div></div>;
} }

View file

@ -36,6 +36,30 @@ export function SelectContainer(
return history; return history;
} }
/**
* Deselect a container
* @returns New history
*/
export function DeselectContainer(
containerId: string,
fullHistory: IHistoryState[],
historyCurrentStep: number
): IHistoryState[] {
const history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1];
history.push({
lastAction: `Deselect ${containerId}`,
mainContainer: current.mainContainer,
containers: structuredClone(current.containers),
selectedContainerId: 'undefined',
typeCounters: Object.assign({}, current.typeCounters),
symbols: structuredClone(current.symbols),
selectedSymbolId: current.selectedSymbolId
});
return history;
}
/** /**
* Delete a container * Delete a container
* @param containerId containerId of the container to delete * @param containerId containerId of the container to delete
@ -115,7 +139,7 @@ export function DeleteContainer(
* If the selected container is removed, select the sibling after, * If the selected container is removed, select the sibling after,
* If there is no sibling, select the parent, * If there is no sibling, select the parent,
* *
* @param mainContainerClone Main container * @param containers
* @param selectedContainerId Current selected container * @param selectedContainerId Current selected container
* @param parent Parent of the selected/deleted container * @param parent Parent of the selected/deleted container
* @param index Index of the selected/deleted container * @param index Index of the selected/deleted container
@ -127,11 +151,10 @@ function GetSelectedContainerOnDelete(
parent: IContainerModel, parent: IContainerModel,
index: number index: number
): string { ): string {
const newSelectedContainerId = FindContainerById(containers, selectedContainerId)?.properties.id ?? return FindContainerById(containers, selectedContainerId)?.properties.id ??
parent.children.at(index) ?? parent.children.at(index) ??
parent.children.at(index - 1) ?? parent.children.at(index - 1) ??
parent.properties.id; parent.properties.id;
return newSelectedContainerId;
} }
/** /**
@ -160,6 +183,10 @@ function UnlinkContainerFromSymbols(
* Handled the property change event in the properties form * Handled the property change event in the properties form
* @param key Property name * @param key Property name
* @param value New value of the property * @param value New value of the property
* @param type
* @param selected
* @param fullHistory
* @param historyCurrentStep
* @returns void * @returns void
*/ */
export function OnPropertyChange( export function OnPropertyChange(
@ -201,6 +228,7 @@ export function OnPropertyChange(
/** /**
* Sort the parent children by x * Sort the parent children by x
* @param containers
* @param parent The clone used for the sort * @param parent The clone used for the sort
* @returns void * @returns void
*/ */
@ -265,6 +293,7 @@ export function SortChildren(
/** /**
* Set the container with properties and behaviors (mutate) * Set the container with properties and behaviors (mutate)
* @param containers
* @param container Container to update * @param container Container to update
* @param key Key of the property to update * @param key Key of the property to update
* @param value Value of the property to update * @param value Value of the property to update
@ -371,9 +400,8 @@ function AssignProperty(container: IContainerModel, key: string, value: string |
/** /**
* Link a symbol to a container * Link a symbol to a container
* @param containerId Container id * @param containerId Container id
* @param oldSymbolId Old Symbol id * @param oldSymbol
* @param newSymbolId New Symbol id * @param newSymbol
* @param symbols Current list of symbols
* @returns * @returns
*/ */
export function LinkSymbol( export function LinkSymbol(

View file

@ -1,14 +1,17 @@
import * as React from 'react'; import * as React from 'react';
import { useState } from 'react';
import useSize from '@react-hook/size'; import useSize from '@react-hook/size';
import { FixedSizeList as List } from 'react-window'; import { FixedSizeList as List } from 'react-window';
import { Properties } from '../ContainerProperties/ContainerProperties'; import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
import { ContainerProperties } from '../ContainerProperties/ContainerProperties';
import { IContainerModel } from '../../Interfaces/IContainerModel'; import { IContainerModel } from '../../Interfaces/IContainerModel';
import { FindContainerById, MakeRecursionDFSIterator } from '../../utils/itertools'; import { FindContainerById, MakeRecursionDFSIterator } from '../../utils/itertools';
import { ISymbolModel } from '../../Interfaces/ISymbolModel'; import { ISymbolModel } from '../../Interfaces/ISymbolModel';
import { PropertyType } from '../../Enums/PropertyType'; import { PropertyType } from '../../Enums/PropertyType';
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline'; import { ToggleSideBar } from '../Sidebar/ToggleSideBar/ToggleSideBar';
import { Text } from '../Text/Text';
interface IElementsListProps { interface IElementsSideBarProps {
containers: Map<string, IContainerModel> containers: Map<string, IContainerModel>
mainContainer: IContainerModel mainContainer: IContainerModel
symbols: Map<string, ISymbolModel> symbols: Map<string, ISymbolModel>
@ -20,6 +23,8 @@ interface IElementsListProps {
) => void ) => void
selectContainer: (containerId: string) => void selectContainer: (containerId: string) => void
addContainer: (index: number, type: string, parent: string) => void addContainer: (index: number, type: string, parent: string) => void
isExpanded: boolean
onExpandChange: () => void
} }
function RemoveBorderClasses(target: HTMLButtonElement, exception: string = ''): void { function RemoveBorderClasses(target: HTMLButtonElement, exception: string = ''): void {
@ -119,10 +124,11 @@ function HandleOnDrop(
} }
} }
export function ElementsList(props: IElementsListProps): JSX.Element { export function ElementsSideBar(props: IElementsSideBarProps): JSX.Element {
// States // States
const divRef = React.useRef<HTMLDivElement>(null); const divRef = React.useRef<HTMLDivElement>(null);
const [,height] = useSize(divRef); const [,height] = useSize(divRef);
const [showProperties, setShowProperties] = useState(props.isExpanded);
// Render // Render
const it = MakeRecursionDFSIterator(props.mainContainer, props.containers, 0, [0, 0], true); const it = MakeRecursionDFSIterator(props.mainContainer, props.containers, 0, [0, 0], true);
@ -160,10 +166,21 @@ export function ElementsList(props: IElementsListProps): JSX.Element {
} }
return ( return (
<div className='h-full flex flex-col'> <div className='flex flex-row h-full w-full' >
<div ref={divRef} className='h-1/2'> {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>
}
<div className='flex w-64' ref={divRef}>
<div className='w-6'>
<ToggleSideBar title={Text({ textId: '@Properties' })} checked={showProperties} onChange={(newValue) => { setShowProperties(newValue); props.onExpandChange(); }} />
</div>
<List <List
className="List divide-y divide-black overflow-y-auto"
itemCount={containers.length} itemCount={containers.length}
itemSize={35} itemSize={35}
height={height} height={height}
@ -172,13 +189,6 @@ export function ElementsList(props: IElementsListProps): JSX.Element {
{Row} {Row}
</List> </List>
</div> </div>
<div className='grow overflow-auto'>
<Properties
properties={props.selectedContainer?.properties}
symbols={props.symbols}
onChange={props.onPropertyChange}
/>
</div>
</div> </div>
); );
} }

View file

@ -0,0 +1,4 @@
.text-vertical{
text-align: right;
writing-mode: vertical-rl;
}

View file

@ -0,0 +1,23 @@
import * as React from 'react';
import './ToggleSideBar.scss';
interface IToggleSidebarProps {
title: string
checked: boolean
onChange: (newValue: boolean) => void
}
export function ToggleSideBar({ title, checked, onChange }: 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)}
>
<p className='text-vertical'>{title}
</p>
</button>
</div>
);
}

View file

@ -3,18 +3,24 @@ import useSize from '@react-hook/size';
import { FixedSizeList as List } from 'react-window'; import { FixedSizeList as List } from 'react-window';
import { ISymbolModel } from '../../Interfaces/ISymbolModel'; import { ISymbolModel } from '../../Interfaces/ISymbolModel';
import { SymbolProperties } from '../SymbolProperties/SymbolProperties'; import { SymbolProperties } from '../SymbolProperties/SymbolProperties';
import { ToggleSideBar } from '../Sidebar/ToggleSideBar/ToggleSideBar';
import { Text } from '../Text/Text';
import { useState } from 'react';
interface ISymbolsSidebarProps { interface ISymbolsSidebarProps {
selectedSymbolId: string selectedSymbolId: string
symbols: Map<string, ISymbolModel> symbols: Map<string, ISymbolModel>
onPropertyChange: (key: string, value: string | number | boolean) => void onPropertyChange: (key: string, value: string | number | boolean) => void
selectSymbol: (symbolId: string) => void selectSymbol: (symbolId: string) => void
isExpanded: boolean
onExpandChange: (isExpanded: boolean) => void
} }
export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element { export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
// States // States
const divRef = React.useRef<HTMLDivElement>(null); const divRef = React.useRef<HTMLDivElement>(null);
const height = useSize(divRef)[1]; const height = useSize(divRef)[1];
const [showProperties, setShowProperties] = useState(props.isExpanded);
// Render // Render
const symbols = [...props.symbols.values()]; const symbols = [...props.symbols.values()];
function Row({ index, style }: { index: number, style: React.CSSProperties }): JSX.Element { function Row({ index, style }: { index: number, style: React.CSSProperties }): JSX.Element {
@ -39,12 +45,22 @@ export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
</button> </button>
); );
} }
const selectedSymbol = props.symbols.get(props.selectedSymbolId);
return ( return (
<div className='h-full'> <div className='flex flex-row h-full w-full'>
<div ref={divRef} className='h-1/2 text-gray-800'> {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>}
<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); }} />
</div>
<List <List
className='List divide-y divide-black'
itemCount={symbols.length} itemCount={symbols.length}
itemSize={35} itemSize={35}
height={height} height={height}
@ -53,13 +69,6 @@ export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
{Row} {Row}
</List> </List>
</div> </div>
<div>
<SymbolProperties
symbol={props.symbols.get(props.selectedSymbolId)}
symbols={props.symbols}
onChange={props.onPropertyChange}
/>
</div>
</div> </div>
); );
} }

View file

@ -1,9 +1,9 @@
import * as React from 'react'; import * as React from 'react';
import { ElementsList } from '../ElementsList/ElementsList'; import { ElementsSideBar } from '../ElementsList/ElementsSideBar';
import { History } from '../History/History'; import { History } from '../History/History';
import { Bar } from '../Bar/Bar'; import { Bar, BAR_WIDTH } from '../Bar/Bar';
import { Symbols } from '../Symbols/Symbols'; import { Symbols } from '../Symbols/Symbols';
import { SymbolsSidebar } from '../SymbolsList/SymbolsList'; import { SymbolsSidebar } from '../SymbolsList/SymbolsSidebar';
import { PropertyType } from '../../Enums/PropertyType'; import { PropertyType } from '../../Enums/PropertyType';
import { Messages } from '../Messages/Messages'; import { Messages } from '../Messages/Messages';
import { Sidebar } from '../Sidebar/Sidebar'; import { Sidebar } from '../Sidebar/Sidebar';
@ -37,7 +37,9 @@ export interface IUIProps {
export enum SidebarType { export enum SidebarType {
None, None,
Components, Components,
ComponentsExpanded,
Symbols, Symbols,
SymbolsExpanded,
History, History,
Messages, Messages,
Settings Settings
@ -59,6 +61,7 @@ function UseSetOrToggleSidebar(
export function UI({ editorState, ...methods }: IUIProps): JSX.Element { export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
const [selectedSidebar, setSelectedSidebar] = React.useState<SidebarType>(SidebarType.Components); const [selectedSidebar, setSelectedSidebar] = React.useState<SidebarType>(SidebarType.Components);
const [messages, setMessages] = React.useState<IMessage[]>([]); const [messages, setMessages] = React.useState<IMessage[]>([]);
const current = GetCurrentHistoryState(editorState.history, editorState.historyCurrentStep); const current = GetCurrentHistoryState(editorState.history, editorState.historyCurrentStep);
const configuration = editorState.configuration; const configuration = editorState.configuration;
@ -78,7 +81,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
); );
} }
// Please use setOrToggleSidebar rather than setSelectedSidebar so we can close the sidebar // Please use setOrToggleSidebar rather than setSelectedSidebar, so we can close the sidebar
const setOrToggleSidebar = UseSetOrToggleSidebar(selectedSidebar, setSelectedSidebar); const setOrToggleSidebar = UseSetOrToggleSidebar(selectedSidebar, setSelectedSidebar);
let leftSidebarTitle = ''; let leftSidebarTitle = '';
@ -104,7 +107,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
buttonOnClick={methods.addContainer} buttonOnClick={methods.addContainer}
/>; />;
rightSidebarTitle = Text({ textId: '@Elements' }); rightSidebarTitle = Text({ textId: '@Elements' });
rightChildren = <ElementsList rightChildren = <ElementsSideBar
containers={current.containers} containers={current.containers}
mainContainer={mainContainer} mainContainer={mainContainer}
symbols={current.symbols} symbols={current.symbols}
@ -112,9 +115,31 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
onPropertyChange={methods.onPropertyChange} onPropertyChange={methods.onPropertyChange}
selectContainer={methods.selectContainer} selectContainer={methods.selectContainer}
addContainer={methods.addContainerAt} 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.addContainer}
/>;
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) }
/>; />;
break; break;
case SidebarType.Symbols: case SidebarType.Symbols:
leftSidebarTitle = Text({ textId: '@SymbolsLeft' }); leftSidebarTitle = Text({ textId: '@SymbolsLeft' });
leftChildren = <Symbols leftChildren = <Symbols
@ -127,6 +152,24 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
symbols={current.symbols} symbols={current.symbols}
onPropertyChange={methods.onSymbolPropertyChange} onPropertyChange={methods.onSymbolPropertyChange}
selectSymbol={methods.selectSymbol} 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)}
/>; />;
break; break;
@ -159,6 +202,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
const isLeftSidebarOpen = selectedSidebar !== SidebarType.None; const isLeftSidebarOpen = selectedSidebar !== SidebarType.None;
const isRightSidebarOpen = selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.Symbols; const isRightSidebarOpen = selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.Symbols;
const isRightSidebarOpenExpanded = selectedSidebar === SidebarType.ComponentsExpanded || selectedSidebar === SidebarType.SymbolsExpanded;
const isLeftSidebarOpenClasses = new Set<string>([ const isLeftSidebarOpenClasses = new Set<string>([
'left-sidebar', 'left-sidebar',
@ -170,14 +214,29 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
let isRightSidebarOpenClasses = 'right-0 -bottom-full md:-right-80 md:bottom-0'; let isRightSidebarOpenClasses = 'right-0 -bottom-full md:-right-80 md:bottom-0';
let marginSidebar = BAR_WIDTH;
const viewerMarginClasses = new Set<string>([
'ml-16'
]);
if (isLeftSidebarOpen) { if (isLeftSidebarOpen) {
isLeftSidebarOpenClasses.delete('-bottom-full'); isLeftSidebarOpenClasses.delete('-bottom-full');
isLeftSidebarOpenClasses.delete('md:-left-64'); isLeftSidebarOpenClasses.delete('md:-left-64');
isLeftSidebarOpenClasses.delete('md:bottom-0'); isLeftSidebarOpenClasses.delete('md:bottom-0');
marginSidebar += 256;
viewerMarginClasses.add(' md:ml-80');
} }
if (isRightSidebarOpen) { if (isRightSidebarOpen || isRightSidebarOpenExpanded) {
isRightSidebarOpenClasses = 'right-0'; isRightSidebarOpenClasses = 'right-0';
if (isRightSidebarOpenExpanded) {
viewerMarginClasses.add(' md:mr-[32rem]');
marginSidebar += 512;
} else {
viewerMarginClasses.add(' md:mr-64');
marginSidebar += 256;
}
} else { } else {
isLeftSidebarOpenClasses.delete('left-sidebar'); isLeftSidebarOpenClasses.delete('left-sidebar');
isLeftSidebarOpenClasses.add('left-sidebar-single'); isLeftSidebarOpenClasses.add('left-sidebar-single');
@ -186,16 +245,24 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
return ( return (
<> <>
<Bar <Bar
isComponentsOpen={selectedSidebar === SidebarType.Components} isComponentsOpen={selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.ComponentsExpanded}
isSymbolsOpen={selectedSidebar === SidebarType.Symbols} isSymbolsOpen={selectedSidebar === SidebarType.Symbols || selectedSidebar === SidebarType.SymbolsExpanded}
isHistoryOpen={selectedSidebar === SidebarType.History} isHistoryOpen={selectedSidebar === SidebarType.History}
isMessagesOpen={selectedSidebar === SidebarType.Messages} isMessagesOpen={selectedSidebar === SidebarType.Messages}
isSettingsOpen={selectedSidebar === SidebarType.Settings} isSettingsOpen={selectedSidebar === SidebarType.Settings}
toggleComponents={() => { toggleComponents={() => {
if (selectedSidebar === SidebarType.ComponentsExpanded) {
setOrToggleSidebar(SidebarType.ComponentsExpanded);
} else {
setOrToggleSidebar(SidebarType.Components); setOrToggleSidebar(SidebarType.Components);
}
} } } }
toggleSymbols={() => { toggleSymbols={() => {
if (selectedSidebar === SidebarType.SymbolsExpanded) {
setOrToggleSidebar(SidebarType.SymbolsExpanded);
} else {
setOrToggleSidebar(SidebarType.Symbols); setOrToggleSidebar(SidebarType.Symbols);
}
} } } }
toggleTimeline={() => { toggleTimeline={() => {
setOrToggleSidebar(SidebarType.History); setOrToggleSidebar(SidebarType.History);
@ -214,11 +281,11 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
{ leftChildren } { leftChildren }
</Sidebar> </Sidebar>
<Viewer <Viewer
isLeftSidebarOpen={isLeftSidebarOpen} className={`${[...viewerMarginClasses.values()].join(' ')} w-full h-full`}
isRightSidebarOpen={isRightSidebarOpen}
current={current} current={current}
selectedContainer={selectedContainer} selectedContainer={selectedContainer}
selectContainer={methods.selectContainer} selectContainer={methods.selectContainer}
margin={marginSidebar}
/> />
<Sidebar <Sidebar
className={`right-sidebar ${isRightSidebarOpenClasses}`} className={`right-sidebar ${isRightSidebarOpenClasses}`}

View file

@ -10,48 +10,38 @@ import { AddDimensions } from '../Canvas/DimensionLayer';
import { RenderSelector } from '../Canvas/Selector'; import { RenderSelector } from '../Canvas/Selector';
import { SVG } from '../SVG/SVG'; import { SVG } from '../SVG/SVG';
import { RenderSymbol } from '../Canvas/Symbol'; import { RenderSymbol } from '../Canvas/Symbol';
import { useState } from 'react';
interface IViewerProps { interface IViewerProps {
isLeftSidebarOpen: boolean className?: string
isRightSidebarOpen: boolean
current: IHistoryState current: IHistoryState
selectedContainer: IContainerModel | undefined selectedContainer: IContainerModel | undefined
selectContainer: (containerId: string) => void selectContainer: (containerId: string) => void
margin: number
} }
interface IViewer { export function Viewer({
viewerWidth: number className,
viewerHeight: number current,
selectedContainer,
selectContainer,
margin
}: IViewerProps): JSX.Element {
function computeWidth(margin: number): number {
return window.innerWidth - (window.innerWidth < 768 ? BAR_WIDTH : margin);
} }
function OnResize( const [windowSize, setWindowSize] = useState([
isLeftSidebarOpen: boolean, computeWidth(margin),
isRightSidebarOpen: boolean, window.innerHeight
setViewer: React.Dispatch<React.SetStateAction<IViewer>> ]);
): void {
let marginSidebar = BAR_WIDTH;
if (isLeftSidebarOpen) {
marginSidebar += 256;
}
if (isRightSidebarOpen) {
marginSidebar += 256;
}
const margin = window.innerWidth < 768 ? BAR_WIDTH : marginSidebar;
setViewer({
viewerWidth: window.innerWidth - margin,
viewerHeight: window.innerHeight
});
}
function UseSVGAutoResizerOnWindowResize(
isLeftSidebarOpen: boolean,
isRightSidebarOpen: boolean,
setViewer: React.Dispatch<React.SetStateAction<IViewer>>
): void {
React.useEffect(() => { React.useEffect(() => {
function SVGAutoResizer(): void { function SVGAutoResizer(): void {
OnResize(isLeftSidebarOpen, isRightSidebarOpen, setViewer); setWindowSize([
computeWidth(margin),
window.innerHeight
]);
} }
window.addEventListener('resize', SVGAutoResizer); window.addEventListener('resize', SVGAutoResizer);
@ -60,41 +50,13 @@ function UseSVGAutoResizerOnWindowResize(
window.removeEventListener('resize', SVGAutoResizer); window.removeEventListener('resize', SVGAutoResizer);
}; };
}); });
}
function UseSVGAutoResizerOnSidebar(
isLeftSidebarOpen: boolean,
isRightSidebarOpen: boolean,
setViewer: React.Dispatch<React.SetStateAction<IViewer>>
): void {
React.useEffect(() => { React.useEffect(() => {
OnResize(isLeftSidebarOpen, isRightSidebarOpen, setViewer); setWindowSize([
}, [isLeftSidebarOpen, isRightSidebarOpen, setViewer]); computeWidth(margin),
} window.innerHeight
]);
export function Viewer({ }, [margin]);
isLeftSidebarOpen, isRightSidebarOpen,
current,
selectedContainer,
selectContainer
}: IViewerProps): JSX.Element {
let marginClasses = 'ml-16';
let marginSidebar = BAR_WIDTH;
if (isLeftSidebarOpen) {
marginClasses += ' md:ml-80';
marginSidebar += 256;
}
if (isRightSidebarOpen) {
marginClasses += ' md:mr-64';
marginSidebar += 256;
}
const margin = window.innerWidth < 768 ? BAR_WIDTH : marginSidebar;
const [viewer, setViewer] = React.useState<IViewer>({
viewerWidth: window.innerWidth - margin,
viewerHeight: window.innerHeight
});
const mainContainer = FindContainerById(current.containers, current.mainContainer); const mainContainer = FindContainerById(current.containers, current.mainContainer);
@ -102,9 +64,6 @@ export function Viewer({
return <></>; return <></>;
} }
UseSVGAutoResizerOnWindowResize(isLeftSidebarOpen, isRightSidebarOpen, setViewer);
UseSVGAutoResizerOnSidebar(isLeftSidebarOpen, isRightSidebarOpen, setViewer);
if (USE_EXPERIMENTAL_CANVAS_API) { if (USE_EXPERIMENTAL_CANVAS_API) {
function Draw(ctx: CanvasRenderingContext2D, frameCount: number, scale: number, translatePos: IPoint): void { function Draw(ctx: CanvasRenderingContext2D, frameCount: number, scale: number, translatePos: IPoint): void {
if (mainContainer === undefined) { if (mainContainer === undefined) {
@ -169,9 +128,9 @@ export function Viewer({
return ( return (
<SVG <SVG
className={marginClasses} className={className}
viewerWidth={viewer.viewerWidth} viewerWidth={windowSize[0]}
viewerHeight={viewer.viewerHeight} viewerHeight={windowSize[1]}
width={mainContainer.properties.width} width={mainContainer.properties.width}
height={mainContainer.properties.height} height={mainContainer.properties.height}
containers={current.containers} containers={current.containers}
@ -179,6 +138,7 @@ export function Viewer({
symbols={current.symbols} symbols={current.symbols}
selectContainer={selectContainer} selectContainer={selectContainer}
> >
{mainContainer} {mainContainer}
</SVG> </SVG>
); );
@ -206,10 +166,11 @@ function RenderDimensions(
currentTransform: [number, number] currentTransform: [number, number]
): void { ): void {
ctx.save(); ctx.save();
const containerLeftDim = leftDim - (DIMENSION_MARGIN * (depth + 1)) / scale; const depthOffset = (DIMENSION_MARGIN * (depth + 1)) / scale;
const containerTopDim = topDim - (DIMENSION_MARGIN * (depth + 1)) / scale; const containerLeftDim = leftDim - depthOffset;
const containerBottomDim = bottomDim + (DIMENSION_MARGIN * (depth + 1)) / scale; const containerTopDim = topDim - depthOffset;
const containerRightDim = rightDim + (DIMENSION_MARGIN * (depth + 1)) / scale; const containerBottomDim = bottomDim + depthOffset;
const containerRightDim = rightDim + depthOffset;
const dimMapped = [containerLeftDim, containerBottomDim, containerTopDim, containerRightDim]; const dimMapped = [containerLeftDim, containerBottomDim, containerTopDim, containerRightDim];
AddDimensions(ctx, containers, container, dimMapped, currentTransform, scale, depth); AddDimensions(ctx, containers, container, dimMapped, currentTransform, scale, depth);
ctx.restore(); ctx.restore();

View file

@ -2,12 +2,14 @@
"@StartFromScratch": "Start from scratch", "@StartFromScratch": "Start from scratch",
"@LoadConfigFile": "Load a configuration file", "@LoadConfigFile": "Load a configuration file",
"@GoBack": "Go back", "@GoBack": "Go back",
"@Properties" : "Properties",
"@Components": "Components", "@Components": "Components",
"@Elements": "Elements", "@Elements": "Elements",
"@Symbols": "Symbols", "@Symbols": "Symbols",
"@SymbolsLeft": "Symbols", "@SymbolsLeft": "Symbols",
"@SymbolsRight": "Symbols", "@SymbolsRight": "Symbols",
"@NoSymbolSelected": "No symbol selected",
"@Timeline": "Timeline", "@Timeline": "Timeline",
"@Messages": "Messages", "@Messages": "Messages",
"@Settings": "Settings", "@Settings": "Settings",

View file

@ -2,12 +2,14 @@
"@StartFromScratch": "Partir de zéro", "@StartFromScratch": "Partir de zéro",
"@LoadConfigFile": "Charger un fichier de configuration", "@LoadConfigFile": "Charger un fichier de configuration",
"@GoBack": "Revenir", "@GoBack": "Revenir",
"@Properties" : "Propriétés",
"@Components": "Composants", "@Components": "Composants",
"@Elements": "Éléments", "@Elements": "Éléments",
"@Symbols": "Symboles", "@Symbols": "Symboles",
"@SymbolsLeft": "Symboles", "@SymbolsLeft": "Symboles",
"@SymbolsRight": "Symboles", "@SymbolsRight": "Symboles",
"@NoSymbolSelected": "Pas de symbol sélectionné",
"@Timeline": "Chronologie", "@Timeline": "Chronologie",
"@Messages": "Messages", "@Messages": "Messages",
"@Settings": "Paramètres", "@Settings": "Paramètres",

View file

@ -21,10 +21,11 @@
.right-sidebar { .right-sidebar {
@apply fixed shadow-lg z-20 @apply fixed shadow-lg z-20
w-[calc(100%_-_4rem)] md:w-64
h-1/2 md:h-full bottom-0 md:bottom-0 h-1/2 md:h-full bottom-0 md:bottom-0
} }
.sidebar-title { .sidebar-title {
@apply p-3 md:p-5 font-bold h-12 md:h-16 @apply p-3 md:p-5 font-bold h-12 md:h-16
} }