Affichage fonctionnelle sous forme de toolbar vertical, comme dans les IDE Related work items: #7976
298 lines
10 KiB
TypeScript
298 lines
10 KiB
TypeScript
import * as React from 'react';
|
|
import { ElementsSideBar } from '../ElementsList/ElementsSideBar';
|
|
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 { 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 { DISABLE_API } from '../../utils/default';
|
|
import { UseWorker, UseAsync } from './UseWorker';
|
|
import { FindContainerById } from '../../utils/itertools';
|
|
import { IEditorState } from '../../Interfaces/IEditorState';
|
|
import { GetCurrentHistoryState } from '../Editor/Editor';
|
|
import { Text } from '../Text/Text';
|
|
|
|
export interface IUIProps {
|
|
editorState: IEditorState
|
|
selectContainer: (containerId: string) => void
|
|
deleteContainer: (containerId: string) => void
|
|
onPropertyChange: (key: string, value: string | number | boolean | number[], type?: PropertyType) => void
|
|
addContainer: (type: string) => void
|
|
addContainerAt: (index: number, type: string, parent: string) => void
|
|
addSymbol: (type: string) => void
|
|
onSymbolPropertyChange: (key: string, value: string | number | boolean) => void
|
|
selectSymbol: (symbolId: string) => void
|
|
deleteSymbol: (symbolId: string) => void
|
|
saveEditorAsJSON: () => void
|
|
saveEditorAsSVG: () => void
|
|
loadState: (move: number) => void
|
|
}
|
|
|
|
export enum SidebarType {
|
|
None,
|
|
Components,
|
|
ComponentsExpanded,
|
|
Symbols,
|
|
SymbolsExpanded,
|
|
History,
|
|
Messages,
|
|
Settings
|
|
}
|
|
|
|
function UseSetOrToggleSidebar(
|
|
selectedSidebar: SidebarType,
|
|
setSelectedSidebar: React.Dispatch<React.SetStateAction<SidebarType>>
|
|
): (newSidebarType: SidebarType) => void {
|
|
return (newSidebarType) => {
|
|
if (newSidebarType === selectedSidebar) {
|
|
setSelectedSidebar(SidebarType.None);
|
|
return;
|
|
}
|
|
|
|
setSelectedSidebar(newSidebarType);
|
|
};
|
|
}
|
|
|
|
export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
|
|
const [selectedSidebar, setSelectedSidebar] = React.useState<SidebarType>(SidebarType.Components);
|
|
|
|
const [messages, setMessages] = React.useState<IMessage[]>([]);
|
|
const current = GetCurrentHistoryState(editorState.history, editorState.historyCurrentStep);
|
|
const configuration = editorState.configuration;
|
|
|
|
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
if (window.Worker && !DISABLE_API) {
|
|
UseWorker(
|
|
current,
|
|
configuration.APIConfiguration?.apiGetFeedbackUrl,
|
|
setMessages
|
|
);
|
|
} else if (!DISABLE_API) {
|
|
UseAsync(
|
|
current,
|
|
configuration.APIConfiguration?.apiGetFeedbackUrl,
|
|
setMessages
|
|
);
|
|
}
|
|
|
|
// Please use setOrToggleSidebar rather than setSelectedSidebar, so we can close the sidebar
|
|
const setOrToggleSidebar = UseSetOrToggleSidebar(selectedSidebar, setSelectedSidebar);
|
|
|
|
let leftSidebarTitle = '';
|
|
let rightSidebarTitle = '';
|
|
let leftChildren: JSX.Element = (<></>);
|
|
let rightChildren: JSX.Element = (<></>);
|
|
|
|
const mainContainer = FindContainerById(current.containers, current.mainContainer);
|
|
|
|
if (mainContainer === undefined) {
|
|
throw new Error('Tried to initialized UI but there is no main container!');
|
|
}
|
|
|
|
const selectedContainer = FindContainerById(current.containers, current.selectedContainerId);
|
|
|
|
switch (selectedSidebar) {
|
|
case SidebarType.Components:
|
|
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 ={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;
|
|
case SidebarType.Symbols:
|
|
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 ={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;
|
|
|
|
case SidebarType.History:
|
|
leftSidebarTitle = Text({ textId: '@Timeline' });
|
|
leftChildren = <History
|
|
history={editorState.history}
|
|
historyCurrentStep={editorState.historyCurrentStep}
|
|
jumpTo={methods.loadState}
|
|
/>;
|
|
break;
|
|
|
|
case SidebarType.Messages:
|
|
leftSidebarTitle = Text({ textId: '@Messages' });
|
|
leftChildren = <Messages
|
|
historyState={current}
|
|
messages={messages}
|
|
clearMessage={() => setMessages([])}
|
|
/>;
|
|
break;
|
|
|
|
case SidebarType.Settings:
|
|
leftSidebarTitle = Text({ textId: '@Settings' });
|
|
leftChildren = <Settings
|
|
saveEditorAsJSON={methods.saveEditorAsJSON}
|
|
saveEditorAsSVG={methods.saveEditorAsSVG}
|
|
/>;
|
|
break;
|
|
}
|
|
|
|
const isLeftSidebarOpen = selectedSidebar !== SidebarType.None;
|
|
const isRightSidebarOpen = selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.Symbols;
|
|
const isRightSidebarOpenExpanded = selectedSidebar === SidebarType.ComponentsExpanded || selectedSidebar === SidebarType.SymbolsExpanded;
|
|
|
|
const isLeftSidebarOpenClasses = new Set<string>([
|
|
'left-sidebar',
|
|
'left-16',
|
|
'-bottom-full',
|
|
'md:-left-64',
|
|
'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) {
|
|
isLeftSidebarOpenClasses.delete('-bottom-full');
|
|
isLeftSidebarOpenClasses.delete('md:-left-64');
|
|
isLeftSidebarOpenClasses.delete('md:bottom-0');
|
|
marginSidebar += 256;
|
|
viewerMarginClasses.add(' md:ml-80');
|
|
}
|
|
|
|
if (isRightSidebarOpen || isRightSidebarOpenExpanded) {
|
|
isRightSidebarOpenClasses = 'right-0';
|
|
|
|
if (isRightSidebarOpenExpanded) {
|
|
viewerMarginClasses.add(' md:mr-[32rem]');
|
|
marginSidebar += 512;
|
|
} else {
|
|
viewerMarginClasses.add(' md:mr-64');
|
|
marginSidebar += 256;
|
|
}
|
|
} else {
|
|
isLeftSidebarOpenClasses.delete('left-sidebar');
|
|
isLeftSidebarOpenClasses.add('left-sidebar-single');
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Bar
|
|
isComponentsOpen={selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.ComponentsExpanded}
|
|
isSymbolsOpen={selectedSidebar === SidebarType.Symbols || selectedSidebar === SidebarType.SymbolsExpanded}
|
|
isHistoryOpen={selectedSidebar === SidebarType.History}
|
|
isMessagesOpen={selectedSidebar === SidebarType.Messages}
|
|
isSettingsOpen={selectedSidebar === SidebarType.Settings}
|
|
toggleComponents={() => {
|
|
if (selectedSidebar === SidebarType.ComponentsExpanded) {
|
|
setOrToggleSidebar(SidebarType.ComponentsExpanded);
|
|
} else {
|
|
setOrToggleSidebar(SidebarType.Components);
|
|
}
|
|
} }
|
|
toggleSymbols={() => {
|
|
if (selectedSidebar === SidebarType.SymbolsExpanded) {
|
|
setOrToggleSidebar(SidebarType.SymbolsExpanded);
|
|
} else {
|
|
setOrToggleSidebar(SidebarType.Symbols);
|
|
}
|
|
} }
|
|
toggleTimeline={() => {
|
|
setOrToggleSidebar(SidebarType.History);
|
|
} }
|
|
toggleMessages={() => {
|
|
setOrToggleSidebar(SidebarType.Messages);
|
|
} }
|
|
toggleSettings={() => {
|
|
setOrToggleSidebar(SidebarType.Settings);
|
|
} }
|
|
/>
|
|
<Sidebar
|
|
className={`${[...isLeftSidebarOpenClasses.values()].join(' ')}`}
|
|
title={leftSidebarTitle}
|
|
>
|
|
{ leftChildren }
|
|
</Sidebar>
|
|
<Viewer
|
|
className={`${[...viewerMarginClasses.values()].join(' ')} w-full h-full`}
|
|
current={current}
|
|
selectedContainer={selectedContainer}
|
|
selectContainer={methods.selectContainer}
|
|
margin={marginSidebar}
|
|
/>
|
|
<Sidebar
|
|
className={`right-sidebar ${isRightSidebarOpenClasses}`}
|
|
title={rightSidebarTitle}
|
|
>
|
|
{ rightChildren }
|
|
</Sidebar>
|
|
</>
|
|
);
|
|
}
|