Merged PR 199: Add more Position for the dimensions + rename isDimensionBorrower and MarkPosition... + Refactor components in ContainerForm with Checkboxes and Selector + Add more docs

Add more Position for the dimensions

Rename isDimensionBorrower and MarkPosition...

Refactor components in ContainerForm with Checkboxes and Selector

Add more docs
This commit is contained in:
Eric Nguyen 2022-09-30 09:28:08 +00:00
parent 38666af314
commit b88539e34d
13 changed files with 494 additions and 314 deletions

View file

@ -1,17 +1,19 @@
import * as React from 'react'; import * as React from 'react';
import { IInputGroup } from '../../Interfaces/IInputGroup';
interface ICheckboxGroupButtonsProps { interface ICheckboxGroupButtonsProps {
name: string name: string
selectedValues: number[] selectedValues: number[]
inputClassName: string inputClassName: string
labelText: string labelText: string
inputGroups: IEnumCheckboxInputGroup[] inputGroups: ICheckboxInputGroup[]
colQty: number colQty: number
onChange: (newSelectedValues: number[]) => void onChange: (newSelectedValues: number[]) => void
} }
interface IEnumCheckboxInputGroup extends Omit<IInputGroup, 'value'> { // TODO: After modeler uses Typescript >= 4.x, extends Omit<IInputGroup, 'value'>
interface ICheckboxInputGroup {
key: string
text: React.ReactNode
value: number value: number
} }
@ -32,7 +34,7 @@ const GRID_COLS = [
'grid-cols-12' 'grid-cols-12'
]; ];
export function EnumCheckboxGroupButtons(props: ICheckboxGroupButtonsProps): JSX.Element { export function CheckboxGroupButtons(props: ICheckboxGroupButtonsProps): JSX.Element {
const selectedOptions = new Set<number>(props.selectedValues); const selectedOptions = new Set<number>(props.selectedValues);
const inputGroups = props.inputGroups.map((inputGroup) => ( const inputGroups = props.inputGroups.map((inputGroup) => (
<div key={inputGroup.key}> <div key={inputGroup.key}>
@ -69,7 +71,7 @@ export function EnumCheckboxGroupButtons(props: ICheckboxGroupButtonsProps): JSX
</> </>
); );
function IsChecked(inputGroup: IEnumCheckboxInputGroup): boolean { function IsChecked(inputGroup: ICheckboxInputGroup): boolean {
return selectedOptions.has(inputGroup.value); return selectedOptions.has(inputGroup.value);
} }

View file

@ -0,0 +1,52 @@
import React from 'react';
import { ChevronUpDownIcon } from '@heroicons/react/20/solid';
import { CheckboxGroupButtons } from './CheckboxGroupButtons';
import { Orientation } from '../../Enums/Orientation';
interface IOrientationCheckboxesProps {
id: string
name: string
labelText: string
value: Orientation[]
onChange: (key: string, value: number[]) => void
}
export function OrientationCheckboxes({
id,
name,
labelText,
value,
onChange
}: IOrientationCheckboxesProps): JSX.Element {
return <CheckboxGroupButtons
key={id}
name={name}
selectedValues={value}
inputClassName='hidden'
labelText={labelText}
colQty={2}
inputGroups={[
{
key: `${id}-horizontal`,
text: (
<div title='Horizontal' aria-label='horizontal' className='radio-button-icon'>
<ChevronUpDownIcon className='heroicon p-1' />
</div>
),
value: Orientation.Horizontal
},
{
key: `${id}-vertical`,
text: (
<div title='Vertical' aria-label='vertical' className='radio-button-icon'>
<ChevronUpDownIcon className='heroicon rotate-90 p-1' />
</div>
),
value: Orientation.Vertical
}
]}
onChange={(newSelectedValues) => {
onChange(id, newSelectedValues);
}}
/>;
}

View file

@ -0,0 +1,70 @@
import React from 'react';
import { ArrowDownIcon, ArrowLeftIcon, ArrowRightIcon, ArrowUpIcon } from '@heroicons/react/20/solid';
import { Position } from '../../Enums/Position';
import { CheckboxGroupButtons } from './CheckboxGroupButtons';
interface IPositionCheckboxesProps {
id: string
name: string
labelText: string
value: Position[]
onChange: (key: string, value: number[]) => void
}
export function PositionCheckboxes({
id,
name,
labelText,
value,
onChange
}: IPositionCheckboxesProps): JSX.Element {
return <CheckboxGroupButtons
key={id}
name={name}
selectedValues={value}
inputClassName='hidden'
labelText={labelText}
colQty={4}
inputGroups={[
{
key: `${id}-left`,
text: (
<div title='Left' aria-label='left' className='radio-button-icon'>
<ArrowLeftIcon className='heroicon p-1' />
</div>
),
value: Position.Left
},
{
key: `${id}-down`,
text: (
<div title='Down' aria-label='down' className='radio-button-icon'>
<ArrowDownIcon className='heroicon p-1' />
</div>
),
value: Position.Down
},
{
key: `${id}-up`,
text: (
<div title='Up' aria-label='up' className='radio-button-icon'>
<ArrowUpIcon className='heroicon p-1' />
</div>
),
value: Position.Up
},
{
key: `${id}-right`,
text: (
<div title='Right' aria-label='right' className='radio-button-icon'>
<ArrowRightIcon className='heroicon p-1' />
</div>
),
value: Position.Right
}
]}
onChange={(newSelectedValues) => {
onChange(id, newSelectedValues);
}}
/>;
}

View file

@ -1,19 +1,17 @@
import { Bars3BottomLeftIcon, Bars3CenterLeftIcon, Bars3Icon, Bars3BottomRightIcon, Bars2Icon } from '@heroicons/react/24/outline';
import { FlagIcon, ViewColumnsIcon } from '@heroicons/react/20/solid';
import * as React from 'react'; import * as React from 'react';
import { PropertyType } from '../../Enums/PropertyType'; import { PropertyType } from '../../Enums/PropertyType';
import { PositionReference } from '../../Enums/PositionReference';
import { IContainerProperties } from '../../Interfaces/IContainerProperties'; import { IContainerProperties } from '../../Interfaces/IContainerProperties';
import { ISymbolModel } from '../../Interfaces/ISymbolModel'; import { ISymbolModel } from '../../Interfaces/ISymbolModel';
import { SHOW_BORROWER_DIMENSIONS, SHOW_CHILDREN_DIMENSIONS, SHOW_SELF_DIMENSIONS } from '../../utils/default'; import { SHOW_BORROWER_DIMENSIONS, SHOW_CHILDREN_DIMENSIONS, SHOW_SELF_DIMENSIONS } from '../../utils/default';
import { ApplyWidthMargin, ApplyXMargin, RemoveWidthMargin, RemoveXMargin, RestoreX, RestoreY, TransformX, TransformY } from '../../utils/svg'; import { ApplyWidthMargin, ApplyXMargin, RemoveWidthMargin, RemoveXMargin, RestoreX, RestoreY, TransformX, TransformY } from '../../utils/svg';
import { InputGroup } from '../InputGroup/InputGroup'; import { InputGroup } from '../InputGroup/InputGroup';
import { TextInputGroup } from '../InputGroup/TextInputGroup'; import { TextInputGroup } from '../InputGroup/TextInputGroup';
import { RadioGroupButtons } from '../RadioGroupButtons/RadioGroupButtons';
import { Select } from '../Select/Select'; import { Select } from '../Select/Select';
import { ToggleButton, ToggleType } from '../ToggleButton/ToggleButton'; import { ToggleButton, ToggleType } from '../ToggleButton/ToggleButton';
import { Orientation } from '../../Enums/Orientation'; import { PositionReferenceSelector } from '../RadioGroupButtons/PositionReferenceSelector';
import { EnumCheckboxGroupButtons } from '../EnumCheckboxGroupButtons/EnumCheckboxGroupButtons'; import { OrientationSelector } from '../RadioGroupButtons/OrientationSelector';
import { OrientationCheckboxes } from '../CheckboxGroupButtons/OrientationCheckboxes';
import { PositionCheckboxes } from '../CheckboxGroupButtons/PositionCheckboxes';
interface IContainerFormProps { interface IContainerFormProps {
properties: IContainerProperties properties: IContainerProperties
@ -75,7 +73,13 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type='string' type='string'
value={props.properties.displayedText?.toString()} value={props.properties.displayedText?.toString()}
onChange={(value) => props.onChange('displayedText', value)} /> onChange={(value) => props.onChange('displayedText', value)} />
<OrientationSelector {...props} /> <OrientationSelector
id='orientation'
name='Orientation'
labelText='Orientation'
value={props.properties.orientation}
onChange={props.onChange}
/>
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-x`} id={`${props.properties.id}-x`}
labelText='x' labelText='x'
@ -245,8 +249,12 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type={ToggleType.Full} type={ToggleType.Full}
checked={props.properties.isAnchor} checked={props.properties.isAnchor}
onChange={(event) => props.onChange('isAnchor', event.target.checked)} /> onChange={(event) => props.onChange('isAnchor', event.target.checked)} />
<AlignmentSelector <PositionReferenceSelector
{...props} id='positionReference'
name='PositionReference'
labelText='Alignment'
value={props.properties.positionReference}
onChange={props.onChange}
/> />
<Select <Select
inputKey='linkedSymbolId' inputKey='linkedSymbolId'
@ -263,206 +271,43 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
{GetCSSInputs(props.properties, props.onChange)} {GetCSSInputs(props.properties, props.onChange)}
{ {
SHOW_SELF_DIMENSIONS && SHOW_SELF_DIMENSIONS &&
<ToggleButton <PositionCheckboxes
id='showSelfDimensions'
name='ShowSelfDimensions'
labelText='Show dimension' labelText='Show dimension'
inputKey='showSelfDimensions' value={props.properties.showSelfDimensions}
labelClassName='' onChange={props.onChange}
inputClassName='' />
type={ToggleType.Full}
checked={props.properties.showSelfDimensions}
onChange={(event) => props.onChange('showSelfDimensions', event.target.checked)} />
} }
{ {
SHOW_CHILDREN_DIMENSIONS && SHOW_CHILDREN_DIMENSIONS &&
<ToggleButton <PositionCheckboxes
id='showChildrenDimensions'
name='ShowChildrenDimensions'
labelText='Show overall dimension of its children' labelText='Show overall dimension of its children'
inputKey='showChildrenDimensions' value={props.properties.showChildrenDimensions}
labelClassName='' onChange={props.onChange}
inputClassName='' />
type={ToggleType.Full}
checked={props.properties.showChildrenDimensions}
onChange={(event) => props.onChange('showChildrenDimensions', event.target.checked)} />
} }
{ {
SHOW_BORROWER_DIMENSIONS && SHOW_BORROWER_DIMENSIONS &&
<> <>
<MarkPositionOptions {...props} /> <OrientationCheckboxes
<ToggleButton id='markPosition'
name='MarkPosition'
value={props.properties.markPosition}
labelText='Mark the position'
onChange={props.onChange}
/>
<PositionCheckboxes
id='showDimensionWithMarks'
name='ShowDimensionWithMarks'
labelText='Show dimension with marked children' labelText='Show dimension with marked children'
inputKey='isDimensionBorrower' value={props.properties.showDimensionWithMarks}
labelClassName='' onChange={props.onChange}
inputClassName='' />
type={ToggleType.Full}
checked={props.properties.isDimensionBorrower}
onChange={(event) => props.onChange('isDimensionBorrower', event.target.checked)} />
</> </>
} }
</div> </div>
); );
} }
// TODO: Implement categories in the form
function MarkPositionOptions(props: IContainerFormProps): JSX.Element {
return <EnumCheckboxGroupButtons
key='markPosition'
name='MarkPosition'
selectedValues={props.properties.markPositionToDimensionBorrower}
inputClassName='hidden'
labelText='Mark the position'
colQty={2}
inputGroups={[
{
key: 'mark-position-horizontal',
text: (
<div title='Horizontal' aria-label='horizontal' className='radio-button-icon'>
<FlagIcon className='heroicon p-1' />
</div>
),
value: Orientation.Horizontal
},
{
key: 'mark-position-vertical',
text: (
<div title='Vertical' aria-label='vertical' className='radio-button-icon'>
<FlagIcon className='heroicon rotate-90 p-1' />
</div>
),
value: Orientation.Vertical
}
]}
onChange={(newSelectedValues) => {
props.onChange('markPositionToDimensionBorrower', newSelectedValues);
}}
/>;
}
function OrientationSelector(props: IContainerFormProps): JSX.Element {
return <RadioGroupButtons
key='orientation'
name='Orientation'
value={props.properties.orientation.toString()}
inputClassName='hidden'
labelText='Orientation'
colQty={2}
inputGroups={[
{
key: 'orientation-horizontal',
text: (
<div title='Horizontal' aria-label='horizontal' className='radio-button-icon'>
<ViewColumnsIcon className='heroicon p-1' />
</div>
),
value: Orientation.Horizontal.toString()
},
{
key: 'orientation-vertical',
text: (
<div title='Vertical' aria-label='vertical' className='radio-button-icon'>
<ViewColumnsIcon className='heroicon rotate-90 p-1' />
</div>
),
value: Orientation.Vertical.toString()
}
]}
onChange={(event) => {
props.onChange('orientation', Number(event.target.value));
}}
/>;
}
function AlignmentSelector(props: IContainerFormProps): JSX.Element {
return <RadioGroupButtons
key='positionReference'
name='PositionReference'
value={props.properties.positionReference.toString()}
inputClassName='hidden'
labelText='Alignment'
colQty={3}
inputGroups={[
{
key: 'position-reference-tl',
text: (
<div title='Top Left' aria-label='top left' className='radio-button-icon'>
<Bars3BottomLeftIcon className='heroicon' />
</div>
),
value: PositionReference.TopLeft.toString()
},
{
key: 'position-reference-tc',
text: (
<div title='Top Center' aria-label='top center' className='radio-button-icon'>
<Bars2Icon className='heroicon -scale-y-100' />
</div>
),
value: PositionReference.TopCenter.toString()
},
{
key: 'position-reference-tr',
text: (
<div title='Top Right' aria-label='top right' className='radio-button-icon'>
<Bars3BottomRightIcon className='heroicon' />
</div>
),
value: PositionReference.TopRight.toString()
},
{
key: 'position-reference-cl',
text: (
<div title='Center Left' aria-label='center left' className='radio-button-icon'>
<Bars3CenterLeftIcon className='heroicon' />
</div>
),
value: PositionReference.CenterLeft.toString()
},
{
key: 'position-reference-cc',
text: (
<div title='Center Center' aria-label='center center' className='radio-button-icon'>
<Bars3Icon className='heroicon' />
</div>
),
value: PositionReference.CenterCenter.toString()
},
{
key: 'position-reference-cr',
text: (
<div title='Center Right' aria-label='center right' className='radio-button-icon'>
<Bars3CenterLeftIcon className='heroicon -scale-x-100' />
</div>
),
value: PositionReference.CenterRight.toString()
},
{
key: 'position-reference-bl',
text: (
<div title='Bottom Left' aria-label='bottom left' className='radio-button-icon'>
<Bars3BottomLeftIcon className='heroicon -scale-y-100' />
</div>
),
value: PositionReference.BottomLeft.toString()
},
{
key: 'position-reference-bc',
text: (
<div title='Bottom Center' aria-label='center center' className='radio-button-icon'>
<Bars2Icon className='heroicon' />
</div>
),
value: PositionReference.BottomCenter.toString()
},
{
key: 'position-reference-br',
text: (
<div title='Bottom Right' aria-label='bottom right' className='radio-button-icon'>
<Bars3BottomRightIcon className='heroicon -scale-y-100' />
</div>
),
value: PositionReference.BottomRight.toString()
}
]}
onChange={(event) => {
props.onChange('positionReference', Number(event.target.value));
}} />;
}

View file

@ -42,10 +42,10 @@ describe.concurrent('Properties', () => {
isAnchor: false, isAnchor: false,
warning: '', warning: '',
hideChildrenInTreeview: false, hideChildrenInTreeview: false,
showChildrenDimensions: true, // TODO: put the dimension at the top (see pdf) showChildrenDimensions: [],
showSelfDimensions: true, // TODO: put the dimension at the bottom (see pdf) showSelfDimensions: [],
isDimensionBorrower: true, // second dimensions from the bottom showDimensionWithMarks: [],
markPositionToDimensionBorrower: [] markPosition: []
}; };
const handleChange = vi.fn((key, value) => { const handleChange = vi.fn((key, value) => {

View file

@ -110,10 +110,10 @@ describe.concurrent('Elements sidebar', () => {
isAnchor: false, isAnchor: false,
warning: '', warning: '',
hideChildrenInTreeview: false, hideChildrenInTreeview: false,
showChildrenDimensions: true, showChildrenDimensions: [],
showSelfDimensions: true, showSelfDimensions: [],
isDimensionBorrower: true, showDimensionWithMarks: [],
markPositionToDimensionBorrower: [], markPosition: [],
positionReference: PositionReference.TopLeft positionReference: PositionReference.TopLeft
}, },
userData: {} userData: {}
@ -144,10 +144,10 @@ describe.concurrent('Elements sidebar', () => {
type: 'type', type: 'type',
warning: '', warning: '',
hideChildrenInTreeview: false, hideChildrenInTreeview: false,
showChildrenDimensions: true, showChildrenDimensions: [],
showSelfDimensions: true, showSelfDimensions: [],
isDimensionBorrower: true, showDimensionWithMarks: [],
markPositionToDimensionBorrower: [], markPosition: [],
isAnchor: false isAnchor: false
}, },
userData: {} userData: {}
@ -204,10 +204,10 @@ describe.concurrent('Elements sidebar', () => {
maxHeight: Infinity, maxHeight: Infinity,
type: 'type', type: 'type',
hideChildrenInTreeview: false, hideChildrenInTreeview: false,
showChildrenDimensions: true, showChildrenDimensions: [],
showSelfDimensions: true, showSelfDimensions: [],
isDimensionBorrower: true, showDimensionWithMarks: [],
markPositionToDimensionBorrower: [], markPosition: [],
isAnchor: false isAnchor: false
}, },
userData: {} userData: {}

View file

@ -0,0 +1,53 @@
import React from 'react';
import { ViewColumnsIcon } from '@heroicons/react/20/solid';
import { Orientation } from '../../Enums/Orientation';
import { RadioGroupButtons } from './RadioGroupButtons';
// TODO: After modeler uses TypeScript >= 4.x, extends Omit<ISelectorProps, 'value'>
interface IOrientationSelectorProps {
id: string
name: string
labelText: string
value: Orientation
onChange: (key: string, value: number) => void
}
export function OrientationSelector({
id,
name,
labelText,
value,
onChange
}: IOrientationSelectorProps): JSX.Element {
return <RadioGroupButtons
key={id}
name={name}
value={value.toString()}
inputClassName='hidden'
labelText={labelText}
colQty={2}
inputGroups={[
{
key: `${id}-horizontal`,
text: (
<div title='Horizontal' aria-label='horizontal' className='radio-button-icon'>
<ViewColumnsIcon className='heroicon p-1' />
</div>
),
value: Orientation.Horizontal.toString()
},
{
key: `${id}-vertical`,
text: (
<div title='Vertical' aria-label='vertical' className='radio-button-icon'>
<ViewColumnsIcon className='heroicon rotate-90 p-1' />
</div>
),
value: Orientation.Vertical.toString()
}
]}
onChange={(event) => {
onChange(id, Number(event.target.value));
}}
/>;
}

View file

@ -0,0 +1,114 @@
import React from 'react';
import { Bars3BottomLeftIcon, Bars3CenterLeftIcon, Bars3Icon, Bars3BottomRightIcon, Bars2Icon } from '@heroicons/react/24/outline';
import { PositionReference } from '../../Enums/PositionReference';
import { RadioGroupButtons } from './RadioGroupButtons';
interface IPositionReferenceSelectorProps {
id: string
name: string
labelText: string
value: PositionReference
onChange: (key: string, value: number) => void
}
export function PositionReferenceSelector({
id,
name,
labelText,
value,
onChange
}: IPositionReferenceSelectorProps): JSX.Element {
return <RadioGroupButtons
key={id}
name={name}
value={value.toString()}
inputClassName='hidden'
labelText={labelText}
colQty={3}
inputGroups={[
{
key: `${id}-tl`,
text: (
<div title='Top Left' aria-label='top left' className='radio-button-icon'>
<Bars3BottomLeftIcon className='heroicon' />
</div>
),
value: PositionReference.TopLeft.toString()
},
{
key: `${id}-tc`,
text: (
<div title='Top Center' aria-label='top center' className='radio-button-icon'>
<Bars2Icon className='heroicon -scale-y-100' />
</div>
),
value: PositionReference.TopCenter.toString()
},
{
key: `${id}-tr`,
text: (
<div title='Top Right' aria-label='top right' className='radio-button-icon'>
<Bars3BottomRightIcon className='heroicon' />
</div>
),
value: PositionReference.TopRight.toString()
},
{
key: `${id}-cl`,
text: (
<div title='Center Left' aria-label='center left' className='radio-button-icon'>
<Bars3CenterLeftIcon className='heroicon' />
</div>
),
value: PositionReference.CenterLeft.toString()
},
{
key: `${id}-cc`,
text: (
<div title='Center Center' aria-label='center center' className='radio-button-icon'>
<Bars3Icon className='heroicon' />
</div>
),
value: PositionReference.CenterCenter.toString()
},
{
key: `${id}-cr`,
text: (
<div title='Center Right' aria-label='center right' className='radio-button-icon'>
<Bars3CenterLeftIcon className='heroicon -scale-x-100' />
</div>
),
value: PositionReference.CenterRight.toString()
},
{
key: `${id}-bl`,
text: (
<div title='Bottom Left' aria-label='bottom left' className='radio-button-icon'>
<Bars3BottomLeftIcon className='heroicon -scale-y-100' />
</div>
),
value: PositionReference.BottomLeft.toString()
},
{
key: `${id}-bc`,
text: (
<div title='Bottom Center' aria-label='center center' className='radio-button-icon'>
<Bars2Icon className='heroicon' />
</div>
),
value: PositionReference.BottomCenter.toString()
},
{
key: `${id}-br`,
text: (
<div title='Bottom Right' aria-label='bottom right' className='radio-button-icon'>
<Bars3BottomRightIcon className='heroicon -scale-y-100' />
</div>
),
value: PositionReference.BottomRight.toString()
}
]}
onChange={(event) => {
onChange(id, Number(event.target.value));
}} />;
}

View file

@ -1,5 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { Orientation } from '../../../Enums/Orientation'; import { Orientation } from '../../../Enums/Orientation';
import { Position } from '../../../Enums/Position';
import { ContainerModel, IContainerModel } from '../../../Interfaces/IContainerModel'; import { ContainerModel, IContainerModel } from '../../../Interfaces/IContainerModel';
import { DIMENSION_MARGIN, SHOW_BORROWER_DIMENSIONS, SHOW_CHILDREN_DIMENSIONS, SHOW_SELF_DIMENSIONS } from '../../../utils/default'; import { DIMENSION_MARGIN, SHOW_BORROWER_DIMENSIONS, SHOW_CHILDREN_DIMENSIONS, SHOW_SELF_DIMENSIONS } from '../../../utils/default';
import { MakeRecursionDFSIterator, Pairwise } from '../../../utils/itertools'; import { MakeRecursionDFSIterator, Pairwise } from '../../../utils/itertools';
@ -13,6 +14,43 @@ interface IDimensionLayerProps {
const MODULE_STROKE_WIDTH = 1; const MODULE_STROKE_WIDTH = 1;
/**
* Fonction that call another function given the positions
* @param dimMapped Position mapped depending on the Position enum in order:
* [0:left, 1:bottom, 2:up, 3:right]
* @param positions List of positions
* @param horizontalAction Action called when a left or right position is present
* @param verticalAction Action called when a down or up position is present
* @param params Params for the actions
* (the two actions must have the same number of params, and in the same order)
*/
function ActionByPosition(
dimMapped: number[],
positions: Position[],
horizontalAction: (dim: number, ...params: any[]) => void,
verticalAction: (dim: number, ...params: any[]) => void,
params: any[]
): void {
positions.forEach((position: Position) => {
const dim = dimMapped[position];
switch (position) {
case Position.Left:
case Position.Right:
verticalAction(dim, ...params);
break;
case Position.Down:
case Position.Up:
horizontalAction(dim, ...params);
break;
}
});
}
/**
* Returns a list of dimensions of all containers in root
* @param param0 Object with the root container and the scale of the svg
* @returns A list of dimensions
*/
function Dimensions({ root, scale }: IDimensionLayerProps): React.ReactNode[] { function Dimensions({ root, scale }: IDimensionLayerProps): React.ReactNode[] {
const it = MakeRecursionDFSIterator(root, 0, [0, 0]); const it = MakeRecursionDFSIterator(root, 0, [0, 0]);
const dimensions: React.ReactNode[] = []; const dimensions: React.ReactNode[] = [];
@ -30,39 +68,71 @@ function Dimensions({ root, scale }: IDimensionLayerProps): React.ReactNode[] {
const containerTopDim = topDim - (DIMENSION_MARGIN * (depth + 1)) / scale; const containerTopDim = topDim - (DIMENSION_MARGIN * (depth + 1)) / scale;
const containerBottomDim = bottomDim + (DIMENSION_MARGIN * (depth + 1)) / scale; const containerBottomDim = bottomDim + (DIMENSION_MARGIN * (depth + 1)) / scale;
const containerRightDim = rightDim + (DIMENSION_MARGIN * (depth + 1)) / scale; const containerRightDim = rightDim + (DIMENSION_MARGIN * (depth + 1)) / scale;
if (SHOW_SELF_DIMENSIONS && container.properties.showSelfDimensions) { const dimMapped = [containerLeftDim, containerBottomDim, containerTopDim, containerRightDim];
AddSelfDimension(container, currentTransform, containerTopDim, containerLeftDim, scale, dimensions); if (SHOW_SELF_DIMENSIONS && container.properties.showSelfDimensions.length > 0) {
ActionByPosition(
dimMapped,
container.properties.showSelfDimensions,
AddHorizontalSelfDimension,
AddVerticalSelfDimension,
[container,
currentTransform,
dimensions,
scale]
);
} }
if (SHOW_BORROWER_DIMENSIONS && container.properties.isDimensionBorrower) { if (SHOW_BORROWER_DIMENSIONS && container.properties.showDimensionWithMarks.length > 0) {
AddBorrowerDimension(containerBottomDim, containerRightDim, depth, scale, container, currentTransform, dimensions); ActionByPosition(
dimMapped,
container.properties.showDimensionWithMarks,
AddHorizontalBorrowerDimension,
AddVerticalBorrowerDimension,
[container,
depth,
currentTransform,
dimensions,
scale]
);
} }
if (SHOW_CHILDREN_DIMENSIONS && container.properties.showChildrenDimensions && container.children.length > 1) { if (SHOW_CHILDREN_DIMENSIONS && container.properties.showChildrenDimensions.length > 0 && container.children.length > 1) {
AddChildrenDimension(container, currentTransform, dimensions, containerBottomDim, containerRightDim, scale); ActionByPosition(
dimMapped,
container.properties.showChildrenDimensions,
AddHorizontalChildrenDimension,
AddVerticalChildrenDimension,
[container,
currentTransform,
dimensions,
scale]
);
} }
} }
return dimensions; return dimensions;
} }
function AddChildrenDimension( /**
container: IContainerModel, * A layer containing all dimension
currentTransform: [number, number], * @param props
dimensions: React.ReactNode[], * @returns
containerBottomDim: number, */
containerRightDim: number, export function DimensionLayer(props: IDimensionLayerProps): JSX.Element {
scale: number return (
): void { <g>
AddHorizontalChildrenDimension(container, currentTransform, dimensions, containerBottomDim, scale); { Dimensions(props) }
AddVerticalChildrenDimension(container, currentTransform, dimensions, containerRightDim, scale); </g>
);
} }
/// Dimensions Actions ///
function AddHorizontalChildrenDimension( function AddHorizontalChildrenDimension(
yDim: number,
container: IContainerModel, container: IContainerModel,
currentTransform: [number, number], currentTransform: [number, number],
dimensions: React.ReactNode[], dimensions: React.ReactNode[],
containerBottomDim: number,
scale: number scale: number
): void { ): void {
const childrenId = `dim-children-${container.properties.id}`; const childrenId = `dim-children-${container.properties.id}`;
@ -94,18 +164,18 @@ function AddHorizontalChildrenDimension(
id={childrenId} id={childrenId}
xStart={xChildrenStart + offset} xStart={xChildrenStart + offset}
xEnd={xChildrenEnd + offset} xEnd={xChildrenEnd + offset}
yStart={containerBottomDim} yStart={yDim}
yEnd={containerBottomDim} yEnd={yDim}
strokeWidth={MODULE_STROKE_WIDTH} strokeWidth={MODULE_STROKE_WIDTH}
text={textChildren} text={textChildren}
scale={scale} />); scale={scale} />);
} }
function AddVerticalChildrenDimension( function AddVerticalChildrenDimension(
xDim: number,
container: IContainerModel, container: IContainerModel,
currentTransform: [number, number], currentTransform: [number, number],
dimensions: React.ReactNode[], dimensions: React.ReactNode[],
containerRightDim: number,
scale: number scale: number
): void { ): void {
const childrenId = `dim-v-children-${container.properties.id}`; const childrenId = `dim-v-children-${container.properties.id}`;
@ -135,9 +205,9 @@ function AddVerticalChildrenDimension(
dimensions.push(<Dimension dimensions.push(<Dimension
key={childrenId} key={childrenId}
id={childrenId} id={childrenId}
xStart={containerRightDim} xStart={xDim}
yStart={yChildrenStart + offset} yStart={yChildrenStart + offset}
xEnd={containerRightDim} xEnd={xDim}
yEnd={yChildrenEnd + offset} yEnd={yChildrenEnd + offset}
strokeWidth={MODULE_STROKE_WIDTH} strokeWidth={MODULE_STROKE_WIDTH}
text={textChildren} text={textChildren}
@ -145,34 +215,20 @@ function AddVerticalChildrenDimension(
/>); />);
} }
function AddBorrowerDimension(
bottomDim: number,
rightDim: number,
depth: number,
scale: number,
container: IContainerModel,
currentTransform: [number, number],
dimensions: React.ReactNode[]
): void {
AddHorizontalBorrowerDimension(bottomDim, container, depth, currentTransform, dimensions, scale);
AddVerticalBorrowerDimension(rightDim, container, depth, currentTransform, dimensions, scale);
}
function AddHorizontalBorrowerDimension( function AddHorizontalBorrowerDimension(
bottomDim: number, yDim: number,
container: IContainerModel, container: IContainerModel,
depth: number, depth: number,
currentTransform: [number, number], currentTransform: [number, number],
dimensions: React.ReactNode[], dimensions: React.ReactNode[],
scale: number scale: number
): void { ): void {
const yDim = bottomDim;
const it = MakeRecursionDFSIterator(container, depth, currentTransform); const it = MakeRecursionDFSIterator(container, depth, currentTransform);
const marks = []; // list of vertical lines for the dimension const marks = []; // list of vertical lines for the dimension
for (const { for (const {
container: childContainer, currentTransform: childCurrentTransform container: childContainer, currentTransform: childCurrentTransform
} of it) { } of it) {
const isHidden = !childContainer.properties.markPositionToDimensionBorrower.includes(Orientation.Horizontal); const isHidden = !childContainer.properties.markPosition.includes(Orientation.Horizontal);
if (isHidden) { if (isHidden) {
continue; continue;
} }
@ -212,20 +268,19 @@ function AddHorizontalBorrowerDimension(
} }
function AddVerticalBorrowerDimension( function AddVerticalBorrowerDimension(
rightDim: number, xDim: number,
container: IContainerModel, container: IContainerModel,
depth: number, depth: number,
currentTransform: [number, number], currentTransform: [number, number],
dimensions: React.ReactNode[], dimensions: React.ReactNode[],
scale: number scale: number
): void { ): void {
const xDim = rightDim;
const it = MakeRecursionDFSIterator(container, depth, currentTransform); const it = MakeRecursionDFSIterator(container, depth, currentTransform);
const marks = []; // list of vertical lines for the dimension const marks = []; // list of vertical lines for the dimension
for (const { for (const {
container: childContainer, currentTransform: childCurrentTransform container: childContainer, currentTransform: childCurrentTransform
} of it) { } of it) {
const isHidden = !childContainer.properties.markPositionToDimensionBorrower.includes(Orientation.Vertical); const isHidden = !childContainer.properties.markPosition.includes(Orientation.Vertical);
if (isHidden) { if (isHidden) {
continue; continue;
} }
@ -264,24 +319,17 @@ function AddVerticalBorrowerDimension(
} }
} }
function AddSelfDimension( function AddVerticalSelfDimension(
xDim: number,
container: IContainerModel, container: IContainerModel,
currentTransform: [number, number], currentTransform: [number, number],
topDim: number, dimensions: React.ReactNode[],
leftDim: number, scale: number
scale: number,
dimensions: React.ReactNode[]
): void { ): void {
AddHorizontalSelfDimension(container, currentTransform, topDim, dimensions, scale);
AddVerticalSelfDimension(container, currentTransform, leftDim, dimensions, scale);
}
function AddVerticalSelfDimension(container: IContainerModel, currentTransform: [number, number], leftDim: number, dimensions: React.ReactNode[], scale: number): void {
const height = container.properties.height; const height = container.properties.height;
const idVert = `dim-v-${container.properties.id}`; const idVert = `dim-v-${container.properties.id}`;
const yStart = container.properties.y + currentTransform[1]; const yStart = container.properties.y + currentTransform[1];
const yEnd = yStart + height; const yEnd = yStart + height;
const x = leftDim;
const textVert = height const textVert = height
.toFixed(0) .toFixed(0)
.toString(); .toString();
@ -289,9 +337,9 @@ function AddVerticalSelfDimension(container: IContainerModel, currentTransform:
<Dimension <Dimension
key={idVert} key={idVert}
id={idVert} id={idVert}
xStart={x} xStart={xDim}
yStart={yStart} yStart={yStart}
xEnd={x} xEnd={xDim}
yEnd={yEnd} yEnd={yEnd}
strokeWidth={MODULE_STROKE_WIDTH} strokeWidth={MODULE_STROKE_WIDTH}
text={textVert} text={textVert}
@ -300,9 +348,9 @@ function AddVerticalSelfDimension(container: IContainerModel, currentTransform:
} }
function AddHorizontalSelfDimension( function AddHorizontalSelfDimension(
yDim: number,
container: IContainerModel, container: IContainerModel,
currentTransform: [number, number], currentTransform: [number, number],
topDim: number,
dimensions: React.ReactNode[], dimensions: React.ReactNode[],
scale: number scale: number
): void { ): void {
@ -310,7 +358,6 @@ function AddHorizontalSelfDimension(
const id = `dim-${container.properties.id}`; const id = `dim-${container.properties.id}`;
const xStart = container.properties.x + currentTransform[0]; const xStart = container.properties.x + currentTransform[0];
const xEnd = xStart + width; const xEnd = xStart + width;
const y = topDim;
const text = width const text = width
.toFixed(0) .toFixed(0)
.toString(); .toString();
@ -319,24 +366,11 @@ function AddHorizontalSelfDimension(
key={id} key={id}
id={id} id={id}
xStart={xStart} xStart={xStart}
yStart={y} yStart={yDim}
xEnd={xEnd} xEnd={xEnd}
yEnd={y} yEnd={yDim}
strokeWidth={MODULE_STROKE_WIDTH} strokeWidth={MODULE_STROKE_WIDTH}
text={text} text={text}
scale={scale} /> scale={scale} />
); );
} }
/**
* A layer containing all dimension
* @param props
* @returns
*/
export function DimensionLayer(props: IDimensionLayerProps): JSX.Element {
return (
<g>
{ Dimensions(props) }
</g>
);
}

6
src/Enums/Position.ts Normal file
View file

@ -0,0 +1,6 @@
export enum Position {
Left,
Down,
Up,
Right
}

View file

@ -5,6 +5,7 @@ import { PositionReference } from '../Enums/PositionReference';
import { IAction } from './IAction'; import { IAction } from './IAction';
import { IMargin } from './IMargin'; import { IMargin } from './IMargin';
import { Orientation } from '../Enums/Orientation'; import { Orientation } from '../Enums/Orientation';
import { Position } from '../Enums/Position';
/** Model of available container used in application configuration */ /** Model of available container used in application configuration */
export interface IAvailableContainer { export interface IAvailableContainer {
@ -112,21 +113,21 @@ export interface IAvailableContainer {
HideChildrenInTreeview?: boolean HideChildrenInTreeview?: boolean
/** if true, show the dimension of the container */ /** if true, show the dimension of the container */
ShowSelfDimensions?: boolean ShowSelfDimensions?: Position[]
/** if true show the overall dimensions of its children */ /** if true show the overall dimensions of its children */
ShowChildrenDimensions?: boolean ShowChildrenDimensions?: Position[]
/** /**
* if true, allows a parent dimension borrower to uses its x coordinate for as a reference point for a dimension * if true, allows a parent dimension borrower to uses its x coordinate for as a reference point for a dimension
*/ */
MarkPositionToDimensionBorrower?: Orientation[] MarkPosition?: Orientation[]
/** /**
* if true, show a dimension from the edge of the container to end * if true, show a dimension from the edge of the container to end
* and insert dimensions marks at lift up children (see liftDimensionToBorrower) * and insert dimensions marks at lift up children (see MarkPosition)
*/ */
IsDimensionBorrower?: boolean ShowDimensionWithMarks?: Position[]
/** /**
* if true, hide the entry in the sidebar (default: false) * if true, hide the entry in the sidebar (default: false)

View file

@ -2,6 +2,7 @@ import * as React from 'react';
import { PositionReference } from '../Enums/PositionReference'; import { PositionReference } from '../Enums/PositionReference';
import { IMargin } from './IMargin'; import { IMargin } from './IMargin';
import { Orientation } from '../Enums/Orientation'; import { Orientation } from '../Enums/Orientation';
import { Position } from '../Enums/Position';
/** /**
* Properties of a container * Properties of a container
@ -73,22 +74,22 @@ export interface IContainerProperties {
hideChildrenInTreeview: boolean hideChildrenInTreeview: boolean
/** if true, show the dimension of the container */ /** if true, show the dimension of the container */
showSelfDimensions: boolean showSelfDimensions: Position[]
/** if true show the overall dimensions of its children */ /** if true show the overall dimensions of its children */
showChildrenDimensions: boolean showChildrenDimensions: Position[]
/** /**
* if true, allows a parent dimension borrower to borrow its x coordinate * if true, allows a parent dimension borrower to borrow its x coordinate
* as a reference point for a dimension * as a reference point for a dimension
*/ */
markPositionToDimensionBorrower: Orientation[] markPosition: Orientation[]
/** /**
* if true, show a dimension from the edge of the container to end * if true, show a dimension from the edge of the container to end
* and insert dimensions marks at lift up children (see liftDimensionToBorrower) * and insert dimensions marks at lift up children (see liftDimensionToBorrower)
*/ */
isDimensionBorrower: boolean showDimensionWithMarks: Position[]
/** /**
* Warnings of a container * Warnings of a container

View file

@ -7,38 +7,40 @@ import { IContainerProperties } from '../Interfaces/IContainerProperties';
import { IEditorState } from '../Interfaces/IEditorState'; import { IEditorState } from '../Interfaces/IEditorState';
import { ISymbolModel } from '../Interfaces/ISymbolModel'; import { ISymbolModel } from '../Interfaces/ISymbolModel';
import { Orientation } from '../Enums/Orientation'; import { Orientation } from '../Enums/Orientation';
import { Position } from '../Enums/Position';
/// EDITOR DEFAULTS /// /// EDITOR DEFAULTS ///
/** Enable fast boot and disable main menu */ /** Enable fast boot and disable main menu (default = false) */
export const FAST_BOOT = false; export const FAST_BOOT = false;
/** Disable any call to the API */ /** Disable any call to the API (default = false) */
export const DISABLE_API = false; export const DISABLE_API = false;
/** Enable keyboard shortcuts */ /** Enable keyboard shortcuts (default = true) */
export const ENABLE_SHORTCUTS = true; export const ENABLE_SHORTCUTS = true;
/** Size of the history */ /** Size of the history (recommanded = 200) */
export const MAX_HISTORY = 200; export const MAX_HISTORY = 200;
/** Apply beheviors on children */ /** Apply beheviors on children (recommanded = true) */
export const APPLY_BEHAVIORS_ON_CHILDREN = true; export const APPLY_BEHAVIORS_ON_CHILDREN = true;
/** Framerate of the svg controller */ /** Framerate of the svg controller (recommanded = 60) */
export const MAX_FRAMERATE = 60; export const MAX_FRAMERATE = 60;
/// CONTAINER DEFAULTS /// /// CONTAINER DEFAULTS ///
/** Enable the swap behavior */ /** Enable the swap behavior (kinda broken recommanded = false) */
export const ENABLE_SWAP = false; export const ENABLE_SWAP = false;
/** Enable the rigid behavior */ /** Enable the rigid behavior (recommanded = true) */
export const ENABLE_RIGID = true; export const ENABLE_RIGID = true;
/** /**
* Enable the hard rigid behavior * Enable the hard rigid behavior
* disallowing the container to overlap (ENABLE_RIGID must be true) * disallowing the container to overlap (ENABLE_RIGID must be true)
* (recommanded = false)
*/ */
export const ENABLE_HARD_RIGID = false; export const ENABLE_HARD_RIGID = false;
@ -188,10 +190,10 @@ export const DEFAULT_MAINCONTAINER_PROPS: IContainerProperties = {
isFlex: false, isFlex: false,
positionReference: PositionReference.TopLeft, positionReference: PositionReference.TopLeft,
hideChildrenInTreeview: false, hideChildrenInTreeview: false,
showChildrenDimensions: true, // TODO: put the dimension at the top (see pdf) showChildrenDimensions: [Position.Up, Position.Left],
showSelfDimensions: true, // TODO: put the dimension at the bottom (see pdf) showSelfDimensions: [Position.Up, Position.Left],
isDimensionBorrower: true, // second dimensions from the bottom showDimensionWithMarks: [Position.Down, Position.Right],
markPositionToDimensionBorrower: [], markPosition: [],
warning: '', warning: '',
style: { style: {
stroke: 'black', stroke: 'black',
@ -240,10 +242,10 @@ export function GetDefaultContainerProps(type: string,
minHeight: containerConfig.MinWidth ?? 1, minHeight: containerConfig.MinWidth ?? 1,
maxHeight: containerConfig.MaxWidth ?? Number.MAX_SAFE_INTEGER, maxHeight: containerConfig.MaxWidth ?? Number.MAX_SAFE_INTEGER,
hideChildrenInTreeview: containerConfig.HideChildrenInTreeview ?? false, hideChildrenInTreeview: containerConfig.HideChildrenInTreeview ?? false,
showChildrenDimensions: containerConfig.ShowChildrenDimensions ?? false, showChildrenDimensions: containerConfig.ShowChildrenDimensions ?? [],
showSelfDimensions: containerConfig.ShowSelfDimensions ?? false, showSelfDimensions: containerConfig.ShowSelfDimensions ?? [],
markPositionToDimensionBorrower: containerConfig.MarkPositionToDimensionBorrower ?? [], markPosition: containerConfig.MarkPosition ?? [],
isDimensionBorrower: containerConfig.IsDimensionBorrower ?? false, showDimensionWithMarks: containerConfig.ShowDimensionWithMarks ?? [],
warning: '', warning: '',
customSVG: containerConfig.CustomSVG, customSVG: containerConfig.CustomSVG,
style: structuredClone(containerConfig.Style), style: structuredClone(containerConfig.Style),