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 { PropertyType } from '../../Enums/PropertyType';
import { PositionReference } from '../../Enums/PositionReference';
@ -12,11 +13,12 @@ import { RadioGroupButtons } from '../RadioGroupButtons/RadioGroupButtons';
import { Select } from '../Select/Select';
import { ToggleButton, ToggleType } from '../ToggleButton/ToggleButton';
import { Orientation } from '../../Enums/Orientation';
import { EnumCheckboxGroupButtons } from '../EnumCheckboxGroupButtons/EnumCheckboxGroupButtons';
interface IContainerFormProps {
properties: IContainerProperties
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,
@ -284,14 +286,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
{
SHOW_BORROWER_DIMENSIONS &&
<>
<ToggleButton
labelText='Mark the position'
inputKey='markPositionToDimensionBorrower'
labelClassName=''
inputClassName=''
type={ToggleType.Full}
checked={props.properties.markPositionToDimensionBorrower}
onChange={(event) => props.onChange('markPositionToDimensionBorrower', event.target.checked)} />
<MarkPositionOptions {...props} />
<ToggleButton
labelText='Show dimension with marked children'
inputKey='isDimensionBorrower'
@ -308,6 +303,40 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
// 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'
@ -321,7 +350,7 @@ function OrientationSelector(props: IContainerFormProps): JSX.Element {
key: 'orientation-horizontal',
text: (
<div title='Horizontal' aria-label='horizontal' className='radio-button-icon'>
<ViewColumnsIcon className='heroicon' />
<ViewColumnsIcon className='heroicon p-1' />
</div>
),
value: Orientation.Horizontal.toString()
@ -330,7 +359,7 @@ function OrientationSelector(props: IContainerFormProps): JSX.Element {
key: 'orientation-vertical',
text: (
<div title='Vertical' aria-label='vertical' className='radio-button-icon'>
<ViewColumnsIcon className='heroicon rotate-90' />
<ViewColumnsIcon className='heroicon rotate-90 p-1' />
</div>
),
value: Orientation.Vertical.toString()

View file

@ -7,7 +7,7 @@ import { ContainerForm } from './ContainerForm';
interface IPropertiesProps {
properties?: IContainerProperties
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 {

View file

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

View file

@ -15,7 +15,7 @@ interface IElementsSidebarProps {
selectedContainer: IContainerModel | undefined
onPropertyChange: (
key: string,
value: string | number | boolean,
value: string | number | boolean | number[],
type?: PropertyType
) => 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 {
name: string
value?: string
defaultValue?: string
value: string
inputClassName: string
labelText: string
inputGroups: IInputGroup[]
@ -30,10 +29,7 @@ const GRID_COLS = [
];
export function RadioGroupButtons(props: IRadioGroupButtonsProps): JSX.Element {
let inputGroups;
if (props.value !== undefined) {
// dynamic
inputGroups = props.inputGroups.map((inputGroup) => (
const inputGroups = props.inputGroups.map((inputGroup) => (
<div key={inputGroup.key}>
<input
key={inputGroup.key}
@ -50,24 +46,6 @@ export function RadioGroupButtons(props: IRadioGroupButtonsProps): JSX.Element {
</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];
@ -76,7 +54,7 @@ export function RadioGroupButtons(props: IRadioGroupButtonsProps): JSX.Element {
<label className='text-xs font-medium text-gray-800'>
{props.labelText}
</label>
<div id='XPositionReference'
<div
className={`grid ${gridColsClass}`}
>
{inputGroups}

View file

@ -1,4 +1,5 @@
import * as React from 'react';
import { Orientation } from '../../../Enums/Orientation';
import { ContainerModel, IContainerModel } from '../../../Interfaces/IContainerModel';
import { DIMENSION_MARGIN, SHOW_BORROWER_DIMENSIONS, SHOW_CHILDREN_DIMENSIONS, SHOW_SELF_DIMENSIONS } from '../../../utils/default';
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);
}
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);
}
}
@ -171,7 +172,8 @@ function AddHorizontalBorrowerDimension(
for (const {
container: childContainer, currentTransform: childCurrentTransform
} of it) {
if (!childContainer.properties.markPositionToDimensionBorrower) {
const isHidden = !childContainer.properties.markPositionToDimensionBorrower.includes(Orientation.Horizontal);
if (isHidden) {
continue;
}
@ -223,7 +225,8 @@ function AddVerticalBorrowerDimension(
for (const {
container: childContainer, currentTransform: childCurrentTransform
} of it) {
if (!childContainer.properties.markPositionToDimensionBorrower) {
const isHidden = !childContainer.properties.markPositionToDimensionBorrower.includes(Orientation.Vertical);
if (isHidden) {
continue;
}

View file

@ -25,7 +25,7 @@ interface IUIProps {
categories: ICategory[]
selectContainer: (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
addContainerAt: (index: number, type: string, parent: 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
*/
MarkPositionToDimensionBorrower?: boolean
MarkPositionToDimensionBorrower?: Orientation[]
/**
* 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
* as a reference point for a dimension
*/
markPositionToDimensionBorrower: boolean
markPositionToDimensionBorrower: Orientation[]
/**
* 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)
showSelfDimensions: true, // TODO: put the dimension at the bottom (see pdf)
isDimensionBorrower: true, // second dimensions from the bottom
markPositionToDimensionBorrower: false,
markPositionToDimensionBorrower: [],
warning: '',
style: {
stroke: 'black',
@ -242,7 +242,7 @@ export function GetDefaultContainerProps(type: string,
hideChildrenInTreeview: containerConfig.HideChildrenInTreeview ?? false,
showChildrenDimensions: containerConfig.ShowChildrenDimensions ?? false,
showSelfDimensions: containerConfig.ShowSelfDimensions ?? false,
markPositionToDimensionBorrower: containerConfig.MarkPositionToDimensionBorrower ?? false,
markPositionToDimensionBorrower: containerConfig.MarkPositionToDimensionBorrower ?? [],
isDimensionBorrower: containerConfig.IsDimensionBorrower ?? false,
warning: '',
customSVG: containerConfig.CustomSVG,

View file

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