Merged PR 198: Change MarkPosition for multiple orientation

Implement EnumCheckboxGroupInput
This commit is contained in:
Eric Nguyen 2022-09-29 14:07:30 +00:00
parent 417829945f
commit 7836d27d5f
12 changed files with 164 additions and 68 deletions

View file

@ -1,4 +1,5 @@
import { Bars3BottomLeftIcon, Bars3CenterLeftIcon, Bars3Icon, Bars3BottomRightIcon, Bars2Icon, ViewColumnsIcon } from '@heroicons/react/24/outline'; 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 { PositionReference } from '../../Enums/PositionReference';
@ -12,11 +13,12 @@ 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 { Orientation } from '../../Enums/Orientation';
import { EnumCheckboxGroupButtons } from '../EnumCheckboxGroupButtons/EnumCheckboxGroupButtons';
interface IContainerFormProps { interface IContainerFormProps {
properties: IContainerProperties properties: IContainerProperties
symbols: Map<string, ISymbolModel> symbols: Map<string, ISymbolModel>
onChange: (key: string, value: string | number | boolean, type?: PropertyType) => void onChange: (key: string, value: string | number | boolean | number[], type?: PropertyType) => void
} }
function GetCSSInputs(properties: IContainerProperties, function GetCSSInputs(properties: IContainerProperties,
@ -284,14 +286,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
{ {
SHOW_BORROWER_DIMENSIONS && SHOW_BORROWER_DIMENSIONS &&
<> <>
<ToggleButton <MarkPositionOptions {...props} />
labelText='Mark the position'
inputKey='markPositionToDimensionBorrower'
labelClassName=''
inputClassName=''
type={ToggleType.Full}
checked={props.properties.markPositionToDimensionBorrower}
onChange={(event) => props.onChange('markPositionToDimensionBorrower', event.target.checked)} />
<ToggleButton <ToggleButton
labelText='Show dimension with marked children' labelText='Show dimension with marked children'
inputKey='isDimensionBorrower' inputKey='isDimensionBorrower'
@ -308,6 +303,40 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
// TODO: Implement categories in the form // 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 { function OrientationSelector(props: IContainerFormProps): JSX.Element {
return <RadioGroupButtons return <RadioGroupButtons
key='orientation' key='orientation'
@ -321,7 +350,7 @@ function OrientationSelector(props: IContainerFormProps): JSX.Element {
key: 'orientation-horizontal', key: 'orientation-horizontal',
text: ( text: (
<div title='Horizontal' aria-label='horizontal' className='radio-button-icon'> <div title='Horizontal' aria-label='horizontal' className='radio-button-icon'>
<ViewColumnsIcon className='heroicon' /> <ViewColumnsIcon className='heroicon p-1' />
</div> </div>
), ),
value: Orientation.Horizontal.toString() value: Orientation.Horizontal.toString()
@ -330,7 +359,7 @@ function OrientationSelector(props: IContainerFormProps): JSX.Element {
key: 'orientation-vertical', key: 'orientation-vertical',
text: ( text: (
<div title='Vertical' aria-label='vertical' className='radio-button-icon'> <div title='Vertical' aria-label='vertical' className='radio-button-icon'>
<ViewColumnsIcon className='heroicon rotate-90' /> <ViewColumnsIcon className='heroicon rotate-90 p-1' />
</div> </div>
), ),
value: Orientation.Vertical.toString() value: Orientation.Vertical.toString()

View file

@ -7,7 +7,7 @@ import { ContainerForm } from './ContainerForm';
interface IPropertiesProps { interface IPropertiesProps {
properties?: IContainerProperties properties?: IContainerProperties
symbols: Map<string, ISymbolModel> symbols: Map<string, ISymbolModel>
onChange: (key: string, value: string | number | boolean, type?: PropertyType) => void onChange: (key: string, value: string | number | boolean | number[], type?: PropertyType) => void
} }
export function Properties(props: IPropertiesProps): JSX.Element { export function Properties(props: IPropertiesProps): JSX.Element {

View file

@ -152,7 +152,7 @@ function UnlinkContainerFromSymbols(symbols: Map<string, ISymbolModel>, containe
*/ */
export function OnPropertyChange( export function OnPropertyChange(
key: string, key: string,
value: string | number | boolean, value: string | number | boolean | number[],
type: PropertyType = PropertyType.Simple, type: PropertyType = PropertyType.Simple,
selected: IContainerModel | undefined, selected: IContainerModel | undefined,
fullHistory: IHistoryState[], fullHistory: IHistoryState[],
@ -247,7 +247,7 @@ export function SortChildren(parentClone: IContainerModel | null | undefined): v
*/ */
function SetContainer( function SetContainer(
container: ContainerModel, container: ContainerModel,
key: string, value: string | number | boolean, key: string, value: string | number | boolean | number[],
type: PropertyType, type: PropertyType,
symbols: Map<string, ISymbolModel> symbols: Map<string, ISymbolModel>
): void { ): void {
@ -282,7 +282,7 @@ function SetContainer(
* @param value Value of the property * @param value Value of the property
* @param type Type of the property * @param type Type of the property
*/ */
function AssignProperty(container: ContainerModel, key: string, value: string | number | boolean, type: PropertyType): void { function AssignProperty(container: ContainerModel, key: string, value: string | number | boolean | number[], type: PropertyType): void {
switch (type) { switch (type) {
case PropertyType.Style: case PropertyType.Style:
(container.properties.style as any)[key] = value; (container.properties.style as any)[key] = value;

View file

@ -15,7 +15,7 @@ interface IElementsSidebarProps {
selectedContainer: IContainerModel | undefined selectedContainer: IContainerModel | undefined
onPropertyChange: ( onPropertyChange: (
key: string, key: string,
value: string | number | boolean, value: string | number | boolean | number[],
type?: PropertyType type?: PropertyType
) => void ) => void
selectContainer: (containerId: string) => void selectContainer: (containerId: string) => void

View file

@ -0,0 +1,86 @@
import * as React from 'react';
import { IInputGroup } from '../../Interfaces/IInputGroup';
interface ICheckboxGroupButtonsProps {
name: string
selectedValues: number[]
inputClassName: string
labelText: string
inputGroups: IEnumCheckboxInputGroup[]
colQty: number
onChange: (newSelectedValues: number[]) => void
}
interface IEnumCheckboxInputGroup extends Omit<IInputGroup, 'value'> {
value: number
}
// Use whole class name for react to preparse
const GRID_COLS = [
'grid-cols-none',
'grid-cols-1',
'grid-cols-2',
'grid-cols-3',
'grid-cols-4',
'grid-cols-5',
'grid-cols-6',
'grid-cols-7',
'grid-cols-8',
'grid-cols-9',
'grid-cols-10',
'grid-cols-11',
'grid-cols-12'
];
export function EnumCheckboxGroupButtons(props: ICheckboxGroupButtonsProps): JSX.Element {
const selectedOptions = new Set<number>(props.selectedValues);
const inputGroups = props.inputGroups.map((inputGroup) => (
<div key={inputGroup.key}>
<input
key={inputGroup.key}
id={inputGroup.key}
type='checkbox'
name={props.name}
className={`peer m-2 ${props.inputClassName}`}
value={inputGroup.value}
checked={IsChecked(inputGroup)}
onChange={(event) => {
const newSelectedValues = SetChecked(Number(event.target.value), event.target.checked);
props.onChange(newSelectedValues);
}} />
<label htmlFor={inputGroup.key} className='text-gray-400 peer-checked:text-blue-500'>
{inputGroup.text}
</label>
</div>
));
const gridColsClass = GRID_COLS[props.colQty];
return (
<>
<label className='text-xs font-medium text-gray-800'>
{props.labelText}
</label>
<div id='XPositionReference'
className={`grid ${gridColsClass}`}
>
{inputGroups}
</div>
</>
);
function IsChecked(inputGroup: IEnumCheckboxInputGroup): boolean {
return selectedOptions.has(inputGroup.value);
}
/**
* Sedt an option by using a bitwise operation and returns the new selected values
* example: set the 4th option to 0: (1001) xor (1 << 3) => 0001
* @param selectedValue The option to set
* @returns The new selected values
*/
function SetChecked(selectedValue: number, isChecked: boolean): number[] {
isChecked ? selectedOptions.add(selectedValue) : selectedOptions.delete(selectedValue);
return [...selectedOptions];
}
}

View file

@ -3,8 +3,7 @@ import { IInputGroup } from '../../Interfaces/IInputGroup';
interface IRadioGroupButtonsProps { interface IRadioGroupButtonsProps {
name: string name: string
value?: string value: string
defaultValue?: string
inputClassName: string inputClassName: string
labelText: string labelText: string
inputGroups: IInputGroup[] inputGroups: IInputGroup[]
@ -30,44 +29,23 @@ const GRID_COLS = [
]; ];
export function RadioGroupButtons(props: IRadioGroupButtonsProps): JSX.Element { export function RadioGroupButtons(props: IRadioGroupButtonsProps): JSX.Element {
let inputGroups; const inputGroups = props.inputGroups.map((inputGroup) => (
if (props.value !== undefined) { <div key={inputGroup.key}>
// dynamic <input
inputGroups = props.inputGroups.map((inputGroup) => ( key={inputGroup.key}
<div key={inputGroup.key}> id={inputGroup.key}
<input type='radio'
key={inputGroup.key} name={props.name}
id={inputGroup.key} className={`peer m-2 ${props.inputClassName}`}
type='radio' value={inputGroup.value}
name={props.name} checked={props.value === inputGroup.value}
className={`peer m-2 ${props.inputClassName}`} onChange={props.onChange} />
value={inputGroup.value} <label htmlFor={inputGroup.key} className='text-gray-400 peer-checked:text-blue-500'>
checked={props.value === inputGroup.value} {inputGroup.text}
onChange={props.onChange} /> </label>
<label htmlFor={inputGroup.key} className='text-gray-400 peer-checked:text-blue-500'> </div>
{inputGroup.text}
</label>
</div>
)); ));
} else {
// static
inputGroups = props.inputGroups.map((inputGroup) => (
<div key={inputGroup.key}>
<input
key={inputGroup.key}
id={inputGroup.key}
type='radio'
name={props.name}
className={`peer m-2 ${props.inputClassName}`}
value={inputGroup.value}
defaultChecked={props.defaultValue === inputGroup.value} />
<label htmlFor={inputGroup.key} className='text-gray-400 peer-checked:text-blue-500'>
{inputGroup.text}
</label>
</div>
));
}
const gridColsClass = GRID_COLS[props.colQty]; const gridColsClass = GRID_COLS[props.colQty];
@ -76,7 +54,7 @@ export function RadioGroupButtons(props: IRadioGroupButtonsProps): JSX.Element {
<label className='text-xs font-medium text-gray-800'> <label className='text-xs font-medium text-gray-800'>
{props.labelText} {props.labelText}
</label> </label>
<div id='XPositionReference' <div
className={`grid ${gridColsClass}`} className={`grid ${gridColsClass}`}
> >
{inputGroups} {inputGroups}

View file

@ -1,4 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { Orientation } from '../../../Enums/Orientation';
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';
@ -37,7 +38,7 @@ function Dimensions({ root, scale }: IDimensionLayerProps): React.ReactNode[] {
AddBorrowerDimension(containerBottomDim, containerRightDim, depth, scale, container, currentTransform, dimensions); AddBorrowerDimension(containerBottomDim, containerRightDim, depth, scale, container, currentTransform, dimensions);
} }
if (SHOW_CHILDREN_DIMENSIONS && container.properties.showChildrenDimensions && container.children.length > 0) { if (SHOW_CHILDREN_DIMENSIONS && container.properties.showChildrenDimensions && container.children.length > 1) {
AddChildrenDimension(container, currentTransform, dimensions, containerBottomDim, containerRightDim, scale); AddChildrenDimension(container, currentTransform, dimensions, containerBottomDim, containerRightDim, scale);
} }
} }
@ -171,7 +172,8 @@ function AddHorizontalBorrowerDimension(
for (const { for (const {
container: childContainer, currentTransform: childCurrentTransform container: childContainer, currentTransform: childCurrentTransform
} of it) { } of it) {
if (!childContainer.properties.markPositionToDimensionBorrower) { const isHidden = !childContainer.properties.markPositionToDimensionBorrower.includes(Orientation.Horizontal);
if (isHidden) {
continue; continue;
} }
@ -223,7 +225,8 @@ function AddVerticalBorrowerDimension(
for (const { for (const {
container: childContainer, currentTransform: childCurrentTransform container: childContainer, currentTransform: childCurrentTransform
} of it) { } of it) {
if (!childContainer.properties.markPositionToDimensionBorrower) { const isHidden = !childContainer.properties.markPositionToDimensionBorrower.includes(Orientation.Vertical);
if (isHidden) {
continue; continue;
} }

View file

@ -25,7 +25,7 @@ interface IUIProps {
categories: ICategory[] 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 | number[], type?: PropertyType) => void
addContainer: (type: string) => void addContainer: (type: string) => void
addContainerAt: (index: number, type: string, parent: string) => void addContainerAt: (index: number, type: string, parent: string) => void
addSymbol: (type: string) => void addSymbol: (type: string) => void

View file

@ -120,7 +120,7 @@ export interface IAvailableContainer {
/** /**
* 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?: boolean MarkPositionToDimensionBorrower?: 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

View file

@ -82,7 +82,7 @@ export interface IContainerProperties {
* 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: boolean markPositionToDimensionBorrower: 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

View file

@ -191,7 +191,7 @@ export const DEFAULT_MAINCONTAINER_PROPS: IContainerProperties = {
showChildrenDimensions: true, // TODO: put the dimension at the top (see pdf) showChildrenDimensions: true, // TODO: put the dimension at the top (see pdf)
showSelfDimensions: true, // TODO: put the dimension at the bottom (see pdf) showSelfDimensions: true, // TODO: put the dimension at the bottom (see pdf)
isDimensionBorrower: true, // second dimensions from the bottom isDimensionBorrower: true, // second dimensions from the bottom
markPositionToDimensionBorrower: false, markPositionToDimensionBorrower: [],
warning: '', warning: '',
style: { style: {
stroke: 'black', stroke: 'black',
@ -242,7 +242,7 @@ export function GetDefaultContainerProps(type: string,
hideChildrenInTreeview: containerConfig.HideChildrenInTreeview ?? false, hideChildrenInTreeview: containerConfig.HideChildrenInTreeview ?? false,
showChildrenDimensions: containerConfig.ShowChildrenDimensions ?? false, showChildrenDimensions: containerConfig.ShowChildrenDimensions ?? false,
showSelfDimensions: containerConfig.ShowSelfDimensions ?? false, showSelfDimensions: containerConfig.ShowSelfDimensions ?? false,
markPositionToDimensionBorrower: containerConfig.MarkPositionToDimensionBorrower ?? false, markPositionToDimensionBorrower: containerConfig.MarkPositionToDimensionBorrower ?? [],
isDimensionBorrower: containerConfig.IsDimensionBorrower ?? false, isDimensionBorrower: containerConfig.IsDimensionBorrower ?? false,
warning: '', warning: '',
customSVG: containerConfig.CustomSVG, customSVG: containerConfig.CustomSVG,

View file

@ -157,7 +157,7 @@ const GetSVGLayoutConfiguration = () => {
Type: 'Montant', Type: 'Montant',
Width: 10, Width: 10,
PositionReference: 4, PositionReference: 4,
MarkPositionToDimensionBorrower: true, MarkPositionToDimensionBorrower: [0],
Style: { Style: {
fillOpacity: 0, fillOpacity: 0,
strokeWidth: 2, strokeWidth: 2,
@ -170,7 +170,7 @@ const GetSVGLayoutConfiguration = () => {
Height: 10, Height: 10,
PositionReference: 4, PositionReference: 4,
Orientation: 1, Orientation: 1,
MarkPositionToDimensionBorrower: true, MarkPositionToDimensionBorrower: [1],
Style: { Style: {
fillOpacity: 0, fillOpacity: 0,
strokeWidth: 2, strokeWidth: 2,
@ -182,7 +182,7 @@ const GetSVGLayoutConfiguration = () => {
Type: 'Dilatation', Type: 'Dilatation',
Width: 4, Width: 4,
PositionReference: 1, PositionReference: 1,
MarkPositionToDimensionBorrower: true, MarkPositionToDimensionBorrower: [0],
Style: { Style: {
fillOpacity: 0, fillOpacity: 0,
strokeWidth: 2, strokeWidth: 2,