Implement main bar + Change colors
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Siklos 2022-08-08 11:23:15 +02:00
parent dae2f20e76
commit a42ac77d33
12 changed files with 138 additions and 66 deletions

View file

@ -0,0 +1,37 @@
import { ClockIcon, CubeIcon, MapIcon } from '@heroicons/react/outline';
import * as React from 'react';
import { BarIcon } from './BarIcon';
interface IBarProps {
isSidebarOpen: boolean
isElementsSidebarOpen: boolean
isHistoryOpen: boolean
ToggleSidebar: () => void
ToggleElementsSidebar: () => void
ToggleTimeline: () => void
}
export const Bar: React.FC<IBarProps> = (props) => {
return (
<div className='fixed z-20 flex flex-col top-0 left-0 h-screen w-16 bg-slate-100'>
<BarIcon
isActive={props.isSidebarOpen}
title='Components'
onClick={() => props.ToggleSidebar()}>
<CubeIcon className='heroicon'/>
</BarIcon>
<BarIcon
isActive={props.isElementsSidebarOpen}
title='Map'
onClick={() => props.ToggleElementsSidebar()}>
<MapIcon className='heroicon'/>
</BarIcon>
<BarIcon
isActive={props.isHistoryOpen}
title='Timeline'
onClick={() => props.ToggleTimeline()}>
<ClockIcon className='heroicon'/>
</BarIcon>
</div>
);
};

View file

@ -0,0 +1,22 @@
import * as React from 'react';
interface IBarIconProps {
title: string
children: React.ReactElement
isActive: boolean
onClick: () => void
}
export const BarIcon: React.FC<IBarIconProps> = (props) => {
const isActiveClasses = props.isActive ? 'border-l-4 border-blue-500 bg-slate-200' : '';
return (
<button
className={`bar-btn group ${isActiveClasses}`}
title={props.title}
onClick={() => props.onClick()}
>
<span className='sidebar-tooltip group-hover:scale-100'>{props.title}</span>
{ props.children }
</button>
);
};

View file

@ -1,4 +1,4 @@
import { describe, test, expect, vi } from 'vitest'; import { describe, expect, vi } from 'vitest';
import * as React from 'react'; import * as React from 'react';
import { fireEvent, render, screen } from '../../utils/test-utils'; import { fireEvent, render, screen } from '../../utils/test-utils';
import { ElementsSidebar } from './ElementsSidebar'; import { ElementsSidebar } from './ElementsSidebar';
@ -11,7 +11,6 @@ describe.concurrent('Elements sidebar', () => {
isOpen={true} isOpen={true}
isHistoryOpen={false} isHistoryOpen={false}
SelectedContainer={null} SelectedContainer={null}
onClick={() => {}}
onPropertyChange={() => {}} onPropertyChange={() => {}}
selectContainer={() => {}} selectContainer={() => {}}
/>); />);
@ -39,7 +38,6 @@ describe.concurrent('Elements sidebar', () => {
isOpen={true} isOpen={true}
isHistoryOpen={false} isHistoryOpen={false}
SelectedContainer={null} SelectedContainer={null}
onClick={() => {}}
onPropertyChange={() => {}} onPropertyChange={() => {}}
selectContainer={() => {}} selectContainer={() => {}}
/>); />);
@ -69,7 +67,6 @@ describe.concurrent('Elements sidebar', () => {
isOpen={true} isOpen={true}
isHistoryOpen={false} isHistoryOpen={false}
SelectedContainer={MainContainer} SelectedContainer={MainContainer}
onClick={() => {}}
onPropertyChange={() => {}} onPropertyChange={() => {}}
selectContainer={() => {}} selectContainer={() => {}}
/>); />);
@ -154,7 +151,6 @@ describe.concurrent('Elements sidebar', () => {
isOpen={true} isOpen={true}
isHistoryOpen={false} isHistoryOpen={false}
SelectedContainer={MainContainer} SelectedContainer={MainContainer}
onClick={() => {}}
onPropertyChange={() => {}} onPropertyChange={() => {}}
selectContainer={() => {}} selectContainer={() => {}}
/>); />);
@ -207,7 +203,6 @@ describe.concurrent('Elements sidebar', () => {
isOpen={true} isOpen={true}
isHistoryOpen={false} isHistoryOpen={false}
SelectedContainer={SelectedContainer} SelectedContainer={SelectedContainer}
onClick={() => {}}
onPropertyChange={() => {}} onPropertyChange={() => {}}
selectContainer={selectContainer} selectContainer={selectContainer}
/>); />);
@ -229,7 +224,6 @@ describe.concurrent('Elements sidebar', () => {
isOpen={true} isOpen={true}
isHistoryOpen={false} isHistoryOpen={false}
SelectedContainer={SelectedContainer} SelectedContainer={SelectedContainer}
onClick={() => {}}
onPropertyChange={() => {}} onPropertyChange={() => {}}
selectContainer={selectContainer} selectContainer={selectContainer}
/>); />);

View file

@ -9,7 +9,6 @@ interface IElementsSidebarProps {
isOpen: boolean isOpen: boolean
isHistoryOpen: boolean isHistoryOpen: boolean
SelectedContainer: IContainerModel | null SelectedContainer: IContainerModel | null
onClick: () => void
onPropertyChange: (key: string, value: string) => void onPropertyChange: (key: string, value: string) => void
selectContainer: (container: IContainerModel) => void selectContainer: (container: IContainerModel) => void
} }
@ -42,8 +41,8 @@ export class ElementsSidebar extends React.PureComponent<IElementsSidebarProps>
const selectedClass: string = this.props.SelectedContainer !== undefined && const selectedClass: string = this.props.SelectedContainer !== undefined &&
this.props.SelectedContainer !== null && this.props.SelectedContainer !== null &&
this.props.SelectedContainer.properties.id === container.properties.id this.props.SelectedContainer.properties.id === container.properties.id
? 'bg-blue-500 hover:bg-blue-600' ? 'border-l-4 border-blue-500 bg-slate-400/60 hover:bg-slate-400'
: 'bg-slate-400 hover:bg-slate-600'; : 'bg-slate-300/60 hover:bg-slate-300';
containerRows.push( containerRows.push(
<motion.button <motion.button
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}
@ -65,14 +64,11 @@ export class ElementsSidebar extends React.PureComponent<IElementsSidebarProps>
}); });
return ( return (
<div className={`fixed flex flex-col bg-slate-300 text-white transition-all h-screen w-64 overflow-y-auto z-20 ${isOpenClasses}`}> <div className={`fixed flex flex-col bg-slate-100 text-gray-800 transition-all h-screen w-64 overflow-y-auto z-20 ${isOpenClasses}`}>
<button className='close-button bg-slate-400 hover:bg-slate-600 justify-start' onClick={this.props.onClick}> <div className='bg-slate-100 font-bold sidebar-title'>
&times; Close
</button>
<div className='bg-slate-500 sidebar-row'>
Elements Elements
</div> </div>
<div className='overflow-y-auto overflow-x-hidden text-slate-200 flex-grow divide-y divide-solid divide-slate-500'> <div className='overflow-y-auto overflow-x-hidden text-gray-800 flex-grow'>
{ containerRows } { containerRows }
</div> </div>
<Properties properties={this.props.SelectedContainer?.properties} onChange={this.props.onPropertyChange}></Properties> <Properties properties={this.props.SelectedContainer?.properties} onChange={this.props.onPropertyChange}></Properties>

View file

@ -21,7 +21,7 @@ const FloatingButton: React.FC<IFloatingButtonProps> = (props: IFloatingButtonPr
: <XIcon className="floating-btn" />; : <XIcon className="floating-btn" />;
return ( return (
<div className={props.className}> <div className={`transition-all ${props.className}`}>
<div className={`transition-all flex flex-col gap-2 items-center ${buttonListClasses}`}> <div className={`transition-all flex flex-col gap-2 items-center ${buttonListClasses}`}>
{ props.children } { props.children }
</div> </div>

View file

@ -5,7 +5,6 @@ interface IHistoryProps {
history: IHistoryState[] history: IHistoryState[]
historyCurrentStep: number historyCurrentStep: number
isOpen: boolean isOpen: boolean
onClick: () => void
jumpTo: (move: number) => void jumpTo: (move: number) => void
} }
@ -46,12 +45,9 @@ export class History extends React.PureComponent<IHistoryProps> {
states.reverse(); states.reverse();
return ( return (
<div className={`fixed flex flex-col bg-slate-400 text-white transition-all h-screen w-64 overflow-y-auto z-20 ${isOpenClasses}`}> <div className={`fixed flex flex-col bg-slate-300 text-white transition-all h-screen w-64 overflow-y-auto z-20 ${isOpenClasses}`}>
<button className='close-button bg-slate-500 hover:bg-slate-700 justify-start' onClick={this.props.onClick}> <div className='bg-slate-600 font-bold sidebar-title'>
&times; Close Timeline
</button>
<div className='bg-slate-600 sidebar-row'>
History
</div> </div>
<div className='overflow-y-auto overflow-x-hidden text-slate-300 flex-grow divide-y divide-solid divide-slate-600'> <div className='overflow-y-auto overflow-x-hidden text-slate-300 flex-grow divide-y divide-solid divide-slate-600'>
{ states } { states }

View file

@ -18,7 +18,7 @@ export class Properties extends React.PureComponent<IPropertiesProps> {
.forEach((pair) => this.handleProperties(pair, groupInput)); .forEach((pair) => this.handleProperties(pair, groupInput));
return ( return (
<div className='p-3 bg-slate-500 h-3/5 overflow-y-auto'> <div className='p-3 bg-slate-200 h-3/5 overflow-y-auto'>
{ groupInput } { groupInput }
</div> </div>
); );
@ -33,12 +33,12 @@ export class Properties extends React.PureComponent<IPropertiesProps> {
const isDisabled = key === 'id' || key === 'parentId'; // hardcoded const isDisabled = key === 'id' || key === 'parentId'; // hardcoded
groupInput.push( groupInput.push(
<div key={id} className='mt-4'> <div key={id} className='mt-4'>
<label className='text-sm font-medium text-slate-200' htmlFor={id}>{key}</label> <label className='text-sm font-medium text-gray-800' htmlFor={id}>{key}</label>
<input <input
className='text-base font-medium transition-all text-slate-200 mt-1 block w-full px-3 py-2 className='text-base font-medium transition-all text-gray-800 mt-1 block w-full px-3 py-2
bg-slate-600 border-2 border-slate-600 rounded-lg shadow-sm placeholder-slate-400 bg-white border-2 border-white rounded-lg placeholder-gray-800
focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500
disabled:bg-slate-700 disabled:text-slate-400 disabled:border-slate-700 disabled:shadow-none disabled:bg-slate-300 disabled:text-gray-500 disabled:border-slate-300 disabled:shadow-none
' '
type={type} type={type}
id={id} id={id}

View file

@ -1,31 +1,38 @@
import * as React from 'react'; import * as React from 'react';
import { AvailableContainer } from '../../Interfaces/AvailableContainer'; import { AvailableContainer } from '../../Interfaces/AvailableContainer';
import { truncateString } from '../../utils/stringtools';
interface ISidebarProps { interface ISidebarProps {
componentOptions: AvailableContainer[] componentOptions: AvailableContainer[]
isOpen: boolean isOpen: boolean
onClick: () => void
buttonOnClick: (type: string) => void buttonOnClick: (type: string) => void
} }
export default class Sidebar extends React.PureComponent<ISidebarProps> { export default class Sidebar extends React.PureComponent<ISidebarProps> {
public render(): JSX.Element { public render(): JSX.Element {
const listElements = this.props.componentOptions.map(componentOption => const listElements = this.props.componentOptions.map(componentOption =>
<button className='hover:bg-blue-600 transition-all sidebar-row' key={componentOption.Type} onClick={() => this.props.buttonOnClick(componentOption.Type)}> <button
{componentOption.Type} className='justify-center transition-all sidebar-component'
key={componentOption.Type}
title={componentOption.Type}
onClick={() => this.props.buttonOnClick(componentOption.Type)}
>
{truncateString(componentOption.Type, 5)}
</button> </button>
); );
const isOpenClasses = this.props.isOpen ? 'left-0' : '-left-64'; const isOpenClasses = this.props.isOpen ? 'left-16' : '-left-64';
return ( return (
<div className={`fixed bg-blue-500 dark:bg-blue-500 text-white transition-all h-screen w-64 overflow-y-auto z-20 ${isOpenClasses}`}> <div className={`fixed z-10 bg-slate-200
<button className='close-button hover:bg-blue-600 justify-end' onClick={this.props.onClick}> text-gray-700 transition-all h-screen w-64
Close &times; overflow-y-auto ${isOpenClasses}`}>
</button> <div className='bg-slate-100 sidebar-title'>
<div className='bg-blue-400 sidebar-row'>
Components Components
</div> </div>
{listElements} <div className='grid grid-cols-1 md:grid-cols-3 gap-2
m-2 md:text-xs font-bold'>
{listElements}
</div>
</div> </div>
); );
} }

View file

@ -7,6 +7,7 @@ import { ContainerModel } from '../../Interfaces/ContainerModel';
import { IHistoryState } from '../../App'; import { IHistoryState } from '../../App';
import { PhotographIcon, UploadIcon } from '@heroicons/react/outline'; import { PhotographIcon, UploadIcon } from '@heroicons/react/outline';
import FloatingButton from '../FloatingButton/FloatingButton'; import FloatingButton from '../FloatingButton/FloatingButton';
import { Bar } from '../Bar/Bar';
interface IUIProps { interface IUIProps {
current: IHistoryState current: IHistoryState
@ -58,7 +59,7 @@ export class UI extends React.PureComponent<IUIProps, IUIState> {
/** /**
* Toggle the elements * Toggle the elements
*/ */
public ToggleHistory(): void { public ToggleTimeline(): void {
this.setState({ this.setState({
isHistoryOpen: !this.state.isHistoryOpen isHistoryOpen: !this.state.isHistoryOpen
}); });
@ -75,47 +76,34 @@ export class UI extends React.PureComponent<IUIProps, IUIState> {
return ( return (
<> <>
<Bar
isSidebarOpen={this.state.isSidebarOpen}
isElementsSidebarOpen={this.state.isElementsSidebarOpen}
isHistoryOpen={this.state.isHistoryOpen}
ToggleElementsSidebar={() => this.ToggleElementsSidebar()}
ToggleSidebar={() => this.ToggleSidebar()}
ToggleTimeline={() => this.ToggleTimeline()}
/>
<Sidebar <Sidebar
componentOptions={this.props.AvailableContainers} componentOptions={this.props.AvailableContainers}
isOpen={this.state.isSidebarOpen} isOpen={this.state.isSidebarOpen}
onClick={() => this.ToggleSidebar()}
buttonOnClick={(type: string) => this.props.AddContainer(type)} buttonOnClick={(type: string) => this.props.AddContainer(type)}
/> />
<button
className='fixed z-10 top-4 left-4 text-lg bg-blue-200 hover:bg-blue-300 transition-all drop-shadow-md hover:drop-shadow-lg py-2 px-3 rounded-lg'
onClick={() => this.ToggleSidebar()}
>
&#9776; Components
</button>
<ElementsSidebar <ElementsSidebar
MainContainer={this.props.current.MainContainer} MainContainer={this.props.current.MainContainer}
SelectedContainer={this.props.current.SelectedContainer} SelectedContainer={this.props.current.SelectedContainer}
isOpen={this.state.isElementsSidebarOpen} isOpen={this.state.isElementsSidebarOpen}
isHistoryOpen={this.state.isHistoryOpen} isHistoryOpen={this.state.isHistoryOpen}
onClick={() => this.ToggleElementsSidebar()}
onPropertyChange={this.props.OnPropertyChange} onPropertyChange={this.props.OnPropertyChange}
selectContainer={this.props.SelectContainer} selectContainer={this.props.SelectContainer}
/> />
<button
className='fixed z-10 top-4 right-12 text-lg bg-slate-200 hover:bg-slate-300 transition-all drop-shadow-md hover:drop-shadow-lg py-2 px-3 rounded-lg'
onClick={() => this.ToggleElementsSidebar()}
>
&#9776; Elements
</button>
<History <History
history={this.props.history} history={this.props.history}
historyCurrentStep={this.props.historyCurrentStep} historyCurrentStep={this.props.historyCurrentStep}
isOpen={this.state.isHistoryOpen} isOpen={this.state.isHistoryOpen}
onClick={() => this.ToggleHistory()}
jumpTo={this.props.LoadState} jumpTo={this.props.LoadState}
/> />
<button
className='fixed z-10 top-4 right-72 text-lg bg-slate-200 hover:bg-slate-300 transition-all drop-shadow-md hover:drop-shadow-lg py-2 px-3 rounded-lg'
onClick={() => this.ToggleHistory()}>
&#9776; History
</button>
<FloatingButton className={`fixed z-10 flex flex-col gap-2 items-center bottom-40 ${buttonRightOffsetClasses}`}> <FloatingButton className={`fixed z-10 flex flex-col gap-2 items-center bottom-40 ${buttonRightOffsetClasses}`}>
<button <button
@ -123,14 +111,14 @@ export class UI extends React.PureComponent<IUIProps, IUIState> {
title='Export as JSON' title='Export as JSON'
onClick={this.props.SaveEditorAsJSON} onClick={this.props.SaveEditorAsJSON}
> >
<UploadIcon className="h-full w-full text-white align-middle items-center justify-center" /> <UploadIcon className="heroicon text-white" />
</button> </button>
<button <button
className={'transition-all w-10 h-10 p-2 align-middle items-center justify-center rounded-full bg-blue-500 hover:bg-blue-800'} className={'transition-all w-10 h-10 p-2 align-middle items-center justify-center rounded-full bg-blue-500 hover:bg-blue-800'}
title='Export as SVG' title='Export as SVG'
onClick={this.props.SaveEditorAsSVG} onClick={this.props.SaveEditorAsSVG}
> >
<PhotographIcon className="h-full w-full text-white align-middle items-center justify-center" /> <PhotographIcon className="heroicon text-white" />
</button> </button>
</FloatingButton> </FloatingButton>
</> </>

Binary file not shown.

View file

@ -3,19 +3,45 @@
@tailwind utilities; @tailwind utilities;
@layer components { @layer components {
.sidebar-row { .sidebar-title {
@apply p-6 w-full @apply p-6 font-bold
} }
.sidebar-component {
@apply transition-all px-2 py-6 text-sm rounded-lg bg-slate-300/60 hover:bg-slate-300
}
.elements-sidebar-row { .elements-sidebar-row {
@apply pl-6 pr-6 pt-2 pb-2 w-full @apply pl-6 pr-6 pt-2 pb-2 w-full
} }
.close-button { .close-button {
@apply transition-all w-full h-auto p-4 flex @apply transition-all w-full h-auto p-4 flex
} }
.mainmenu-btn { .mainmenu-btn {
@apply transition-all bg-blue-100 hover:bg-blue-200 text-blue-700 text-lg font-semibold p-8 rounded-lg @apply transition-all bg-blue-100 hover:bg-blue-200 text-blue-700 text-lg font-semibold p-8 rounded-lg
} }
.floating-btn { .floating-btn {
@apply h-full w-full text-white align-middle items-center justify-center @apply h-full w-full text-white align-middle items-center justify-center
} }
.bar-btn {
@apply h-16 w-full p-3 bg-slate-100 hover:bg-slate-200
transition-all text-gray-700 hover:text-gray-600
}
.heroicon {
@apply h-full w-full align-middle items-center justify-center
}
.sidebar-tooltip {
@apply absolute w-auto p-2 m-2 min-w-max left-14
rounded-md shadow-md
text-gray-800 bg-slate-100
dark:text-white dark:bg-gray-800
text-xs font-bold
transition-all duration-100 scale-0 origin-left;
}
} }

6
src/utils/stringtools.ts Normal file
View file

@ -0,0 +1,6 @@
export function truncateString(str: string, num: number): string {
if (str.length <= num) {
return str;
}
return `${str.slice(0, num)}...`;
}