Implement Category list
This commit is contained in:
parent
9f24d2d40a
commit
0476a294d6
7 changed files with 61 additions and 51 deletions
|
@ -1,58 +1,38 @@
|
||||||
import { ChevronRightIcon } from '@heroicons/react/outline';
|
import { ChevronRightIcon } from '@heroicons/react/outline';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
import { ICategory } from '../../Interfaces/ICategory';
|
||||||
import { TruncateString } from '../../utils/stringtools';
|
import { TruncateString } from '../../utils/stringtools';
|
||||||
|
|
||||||
interface ICategoryProps {
|
interface ICategoryProps {
|
||||||
children: JSX.Element | JSX.Element[]
|
children: JSX.Element | JSX.Element[]
|
||||||
category: string
|
category: ICategory
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Category(props: ICategoryProps): JSX.Element {
|
export function Category(props: ICategoryProps): JSX.Element {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
if (isOpen) {
|
const categoryType: string = props.category.Type;
|
||||||
return (
|
const categoryDisplayedText: string = props.category.DisplayedText ?? categoryType;
|
||||||
<div
|
|
||||||
className='overflow-hidden h-full transition-all text-center cursor-pointer'
|
|
||||||
key={props.category}
|
|
||||||
id={props.category}
|
|
||||||
title={props.category}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className='flex flex-row group full'
|
|
||||||
onClick={() => setIsOpen(!isOpen)}
|
|
||||||
>
|
|
||||||
<span className={'transition-all flex-1 h-full justify-center sidebar-component-left rounded-b-none bg-slate-400/80 group-hover:bg-blue-600'}>
|
|
||||||
{TruncateString(props.category, 25)}
|
|
||||||
</span>
|
|
||||||
<span className={'flex-none h-full justify-center sidebar-component-right rounded-b-none'}>
|
|
||||||
<ChevronRightIcon className='transition-all w-5 rotate-90' />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className='transition-all grid gap-2 p-2 bg-slate-400/80 overflow-auto rounded-b-lg visible'>
|
|
||||||
{ props.children }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='overflow-visible h-16 group transition-all text-center cursor-pointer'
|
className={` transition-all text-center ${isOpen ? 'overflow-hidden h-full' : 'overflow-visible h-16'}`}
|
||||||
key={props.category}
|
key={categoryType}
|
||||||
id={props.category}
|
id={categoryType}
|
||||||
title={props.category}
|
title={categoryDisplayedText}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className='flex flex-row group cursor-pointer h-16'
|
||||||
onClick={() => setIsOpen(!isOpen)}
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
>
|
>
|
||||||
<div className='flex flex-row h-full'>
|
<span className={`transition-all flex-1 h-full justify-center sidebar-component-left ${isOpen ? 'rounded-b-none bg-slate-400/80 group-hover:bg-blue-600' : ''}`}>
|
||||||
<span className='flex-1 h-full justify-center sidebar-component-left'>
|
{TruncateString(categoryDisplayedText, 25)}
|
||||||
{TruncateString(props.category, 25)}
|
|
||||||
</span>
|
</span>
|
||||||
<span className='flex-none h-full justify-center sidebar-component-right'>
|
<span className={`flex-none h-full justify-center sidebar-component-right ${isOpen ? 'rounded-b-none' : ''}`}>
|
||||||
<ChevronRightIcon className='transition-all w-5' />
|
<ChevronRightIcon className={`transition-all w-5 ${isOpen ? 'rotate-90' : ''}`} />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='transition-all overflow-hidden rounded-b-lg hidden'>
|
<div className={`transition-all grid gap-2 p-2 rounded-b-lg ${isOpen ? 'visible overflow-auto bg-slate-400/80' : 'hidden overflow-hidden'}`}>
|
||||||
{ props.children }
|
{ props.children }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -210,6 +210,7 @@ export function Editor(props: IEditorProps): JSX.Element {
|
||||||
historyCurrentStep={historyCurrentStep}
|
historyCurrentStep={historyCurrentStep}
|
||||||
availableContainers={configuration.AvailableContainers}
|
availableContainers={configuration.AvailableContainers}
|
||||||
availableSymbols={configuration.AvailableSymbols}
|
availableSymbols={configuration.AvailableSymbols}
|
||||||
|
categories={configuration.Categories}
|
||||||
selectContainer={(container) => setNewHistory(
|
selectContainer={(container) => setNewHistory(
|
||||||
SelectContainer(
|
SelectContainer(
|
||||||
container,
|
container,
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
||||||
|
import { ICategory } from '../../Interfaces/ICategory';
|
||||||
import { TruncateString } from '../../utils/stringtools';
|
import { TruncateString } from '../../utils/stringtools';
|
||||||
import { Category } from '../Category/Category';
|
import { Category } from '../Category/Category';
|
||||||
|
|
||||||
interface ISidebarProps {
|
interface ISidebarProps {
|
||||||
componentOptions: IAvailableContainer[]
|
componentOptions: IAvailableContainer[]
|
||||||
|
categories: ICategory[]
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
buttonOnClick: (type: string) => void
|
buttonOnClick: (type: string) => void
|
||||||
}
|
}
|
||||||
|
@ -15,7 +17,12 @@ function HandleDragStart(event: React.DragEvent<HTMLButtonElement>): void {
|
||||||
|
|
||||||
export function Sidebar(props: ISidebarProps): JSX.Element {
|
export function Sidebar(props: ISidebarProps): JSX.Element {
|
||||||
const rootElements: Array<JSX.Element | undefined> = [];
|
const rootElements: Array<JSX.Element | undefined> = [];
|
||||||
const categories = new Map<string, JSX.Element[]>();
|
const categories = new Map<string, ICategory>();
|
||||||
|
const categoriesElements = new Map<string, JSX.Element[]>(props.categories.map(category => {
|
||||||
|
categories.set(category.Type, category);
|
||||||
|
return [category.Type, []];
|
||||||
|
}));
|
||||||
|
|
||||||
// build the components
|
// build the components
|
||||||
props.componentOptions.forEach(componentOption => {
|
props.componentOptions.forEach(componentOption => {
|
||||||
if (componentOption.IsHidden === true) {
|
if (componentOption.IsHidden === true) {
|
||||||
|
@ -39,25 +46,27 @@ export function Sidebar(props: ISidebarProps): JSX.Element {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (categories.get(componentOption.Category) === undefined) {
|
const category = categoriesElements.get(componentOption.Category);
|
||||||
categories.set(componentOption.Category, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
const category = categories.get(componentOption.Category);
|
|
||||||
|
|
||||||
if (category === undefined) {
|
if (category === undefined) {
|
||||||
// should never go here
|
console.error(`[Category] Category does not exists in configuration.Categories: ${componentOption.Category}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
category.push(componentButton);
|
category.push(componentButton);
|
||||||
});
|
});
|
||||||
|
|
||||||
// build the categories
|
// build the categories
|
||||||
categories.forEach((options, category) => {
|
categoriesElements.forEach((options, categoryName) => {
|
||||||
|
const category = categories.get(categoryName);
|
||||||
|
|
||||||
|
if (category === undefined) {
|
||||||
|
// should never go here
|
||||||
|
console.error(`[Category] Category does not exists in configuration.Categories: ${categoryName}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
rootElements.unshift(
|
rootElements.unshift(
|
||||||
<Category
|
<Category
|
||||||
key={category}
|
key={categoryName}
|
||||||
category={category}
|
category={category}
|
||||||
>
|
>
|
||||||
{ options }
|
{ options }
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { Symbols } from '../Symbols/Symbols';
|
||||||
import { SymbolsSidebar } from '../SymbolsSidebar/SymbolsSidebar';
|
import { SymbolsSidebar } from '../SymbolsSidebar/SymbolsSidebar';
|
||||||
import { PropertyType } from '../../Enums/PropertyType';
|
import { PropertyType } from '../../Enums/PropertyType';
|
||||||
import { MessagesSidebar } from '../MessagesSidebar/MessagesSidebar';
|
import { MessagesSidebar } from '../MessagesSidebar/MessagesSidebar';
|
||||||
|
import { ICategory } from '../../Interfaces/ICategory';
|
||||||
|
|
||||||
interface IUIProps {
|
interface IUIProps {
|
||||||
selectedContainer: IContainerModel | undefined
|
selectedContainer: IContainerModel | undefined
|
||||||
|
@ -21,6 +22,7 @@ interface IUIProps {
|
||||||
historyCurrentStep: number
|
historyCurrentStep: number
|
||||||
availableContainers: IAvailableContainer[]
|
availableContainers: IAvailableContainer[]
|
||||||
availableSymbols: IAvailableSymbol[]
|
availableSymbols: IAvailableSymbol[]
|
||||||
|
categories: ICategory[]
|
||||||
selectContainer: (containerId: string) => void
|
selectContainer: (containerId: string) => void
|
||||||
deleteContainer: (containerId: string) => void
|
deleteContainer: (containerId: string) => void
|
||||||
onPropertyChange: (key: string, value: string | number | boolean, type?: PropertyType) => void
|
onPropertyChange: (key: string, value: string | number | boolean, type?: PropertyType) => void
|
||||||
|
@ -83,6 +85,7 @@ export function UI(props: IUIProps): JSX.Element {
|
||||||
|
|
||||||
<Sidebar
|
<Sidebar
|
||||||
componentOptions={props.availableContainers}
|
componentOptions={props.availableContainers}
|
||||||
|
categories={props.categories}
|
||||||
isOpen={isSidebarOpen}
|
isOpen={isSidebarOpen}
|
||||||
buttonOnClick={props.addContainer} />
|
buttonOnClick={props.addContainer} />
|
||||||
<Symbols
|
<Symbols
|
||||||
|
|
5
src/Interfaces/ICategory.ts
Normal file
5
src/Interfaces/ICategory.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
|
export interface ICategory {
|
||||||
|
Type: string
|
||||||
|
DisplayedText?: string
|
||||||
|
}
|
|
@ -1,12 +1,14 @@
|
||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
import { IAvailableContainer } from './IAvailableContainer';
|
import { IAvailableContainer } from './IAvailableContainer';
|
||||||
import { IAvailableSymbol } from './IAvailableSymbol';
|
import { IAvailableSymbol } from './IAvailableSymbol';
|
||||||
|
import { ICategory } from './ICategory';
|
||||||
import { IPattern } from './IPattern';
|
import { IPattern } from './IPattern';
|
||||||
|
|
||||||
/** Model of configuration for the application to configure it */
|
/** Model of configuration for the application to configure it */
|
||||||
export interface IConfiguration {
|
export interface IConfiguration {
|
||||||
AvailableContainers: IAvailableContainer[] // TODO: Use a Map<string, IAvailableContainer>
|
AvailableContainers: IAvailableContainer[] // TODO: Use a Map<string, IAvailableContainer>
|
||||||
AvailableSymbols: IAvailableSymbol[] // TODO: Use a Map<string, IAvailableContainer>
|
AvailableSymbols: IAvailableSymbol[] // TODO: Use a Map<string, IAvailableContainer>
|
||||||
|
Categories: ICategory[]
|
||||||
Patterns: IPattern[]
|
Patterns: IPattern[]
|
||||||
MainContainer: IAvailableContainer
|
MainContainer: IAvailableContainer
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ const GetSVGLayoutConfiguration = () => {
|
||||||
},
|
},
|
||||||
ShowSelfDimensions: true,
|
ShowSelfDimensions: true,
|
||||||
IsDimensionBorrower: true,
|
IsDimensionBorrower: true,
|
||||||
Category: "Category"
|
Category: "Stuff"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: 'Trou',
|
Type: 'Trou',
|
||||||
|
@ -96,11 +96,11 @@ const GetSVGLayoutConfiguration = () => {
|
||||||
stroke: 'green',
|
stroke: 'green',
|
||||||
fill: 'white'
|
fill: 'white'
|
||||||
},
|
},
|
||||||
Category: "Category"
|
Category: "Stuff"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: 'Remplissage',
|
Type: 'Remplissage',
|
||||||
Category: "Category2",
|
Category: "Other stuff",
|
||||||
CustomSVG: `
|
CustomSVG: `
|
||||||
<rect width="{width}" height="{height}" style="{style}"></rect>
|
<rect width="{width}" height="{height}" style="{style}"></rect>
|
||||||
<rect width="{width}" height="{height}" stroke="black" fill-opacity="0"></rect>
|
<rect width="{width}" height="{height}" stroke="black" fill-opacity="0"></rect>
|
||||||
|
@ -204,6 +204,16 @@ const GetSVGLayoutConfiguration = () => {
|
||||||
XPositionReference: 0
|
XPositionReference: 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
Categories: [
|
||||||
|
{
|
||||||
|
Type: "Stuff",
|
||||||
|
DisplayedText: "Stuff made here"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "Other stuff",
|
||||||
|
DisplayedText: "Stuff not made here"
|
||||||
|
}
|
||||||
|
],
|
||||||
MainContainer: {
|
MainContainer: {
|
||||||
Height: 200,
|
Height: 200,
|
||||||
Width: 800
|
Width: 800
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue