Merge branch 'master' into master.7687.ReplaceBy
This commit is contained in:
commit
9aec5ae751
38 changed files with 2346 additions and 1434 deletions
|
@ -10,7 +10,7 @@ import { IConfiguration } from '../../Interfaces/IConfiguration';
|
|||
import { IContainerModel, ContainerModel } from '../../Interfaces/IContainerModel';
|
||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||
import { IPattern } from '../../Interfaces/IPattern';
|
||||
import { DEFAULT_MAINCONTAINER_PROPS, GetDefaultContainerProps } from '../../utils/default';
|
||||
import { DEFAULT_DIMENSION_OPTION, DEFAULT_MAINCONTAINER_PROPS, GetDefaultContainerProps } from '../../utils/default';
|
||||
import { FetchConfiguration } from './api';
|
||||
|
||||
const CSHARP_WEB_API_BASE_URL = 'http://localhost:5209/';
|
||||
|
@ -144,23 +144,11 @@ describe.concurrent('Models test suite', () => {
|
|||
PositionReference: 0,
|
||||
HideChildrenInTreeview: true,
|
||||
DimensionOptions: {
|
||||
childrenDimensions: {
|
||||
color: '#000000',
|
||||
positions: []
|
||||
},
|
||||
selfDimensions: {
|
||||
color: '#000000',
|
||||
positions: []
|
||||
},
|
||||
selfMarginsDimensions: {
|
||||
color: '#000000',
|
||||
positions: []
|
||||
},
|
||||
childrenDimensions: DEFAULT_DIMENSION_OPTION,
|
||||
selfDimensions: DEFAULT_DIMENSION_OPTION,
|
||||
selfMarginsDimensions: DEFAULT_DIMENSION_OPTION,
|
||||
markPosition: [],
|
||||
dimensionWithMarks: {
|
||||
color: '#000000',
|
||||
positions: []
|
||||
}
|
||||
dimensionWithMarks: DEFAULT_DIMENSION_OPTION
|
||||
},
|
||||
IsHidden: true,
|
||||
Blacklist: [
|
||||
|
|
|
@ -1,14 +1,22 @@
|
|||
import * as React from 'react';
|
||||
import { PropertyType } from '../../Enums/PropertyType';
|
||||
import { IContainerProperties } from '../../Interfaces/IContainerProperties';
|
||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
||||
import { type IContainerProperties } from '../../Interfaces/IContainerProperties';
|
||||
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
|
||||
import {
|
||||
SHOW_BORROWER_DIMENSIONS,
|
||||
SHOW_CHILDREN_DIMENSIONS,
|
||||
SHOW_SELF_DIMENSIONS,
|
||||
SHOW_SELF_MARGINS_DIMENSIONS
|
||||
SHOW_SELF_DIMENSIONS, SHOW_SELF_MARGINS_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 { Text } from '../Text/Text';
|
||||
import { InputGroup } from '../InputGroup/InputGroup';
|
||||
import { TextInputGroup } from '../InputGroup/TextInputGroup';
|
||||
|
@ -38,7 +46,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
inputClassName=''
|
||||
type='string'
|
||||
value={props.properties.displayedText?.toString()}
|
||||
onChange={(value) => props.onChange('displayedText', value)} />
|
||||
onChange={(value) => { props.onChange('displayedText', value); }}/>
|
||||
<OrientationSelector
|
||||
id='orientation'
|
||||
name='Orientation'
|
||||
|
@ -63,7 +71,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
inputClassName=''
|
||||
type='string'
|
||||
value={props.properties.id.toString()}
|
||||
isDisabled={true} />
|
||||
isDisabled={true}/>
|
||||
</div>
|
||||
<div>
|
||||
<InputGroup
|
||||
|
@ -73,7 +81,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
inputClassName=''
|
||||
type='string'
|
||||
value={props.properties.parentId}
|
||||
isDisabled={true} />
|
||||
isDisabled={true}/>
|
||||
</div>
|
||||
<div>
|
||||
<InputGroup
|
||||
|
@ -83,7 +91,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
inputClassName=''
|
||||
type='string'
|
||||
value={props.properties.type}
|
||||
isDisabled={true} />
|
||||
isDisabled={true}/>
|
||||
</div>
|
||||
</div>
|
||||
</Category>
|
||||
|
@ -109,17 +117,19 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
props.properties.width,
|
||||
props.properties.positionReference
|
||||
).toString()}
|
||||
onChange={(value) => props.onChange(
|
||||
'x',
|
||||
ApplyXMargin(
|
||||
RestoreX(
|
||||
Number(value),
|
||||
props.properties.width,
|
||||
props.properties.positionReference
|
||||
),
|
||||
props.properties.margin.left
|
||||
)
|
||||
)} />
|
||||
onChange={(value) => {
|
||||
props.onChange(
|
||||
'x',
|
||||
ApplyXMargin(
|
||||
RestoreX(
|
||||
Number(value),
|
||||
props.properties.width,
|
||||
props.properties.positionReference
|
||||
),
|
||||
props.properties.margin.left
|
||||
)
|
||||
);
|
||||
}}/>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-y`}
|
||||
labelText={Text({ textId: '@ContainerY' })}
|
||||
|
@ -132,17 +142,19 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
props.properties.height,
|
||||
props.properties.positionReference
|
||||
).toString()}
|
||||
onChange={(value) => props.onChange(
|
||||
'y',
|
||||
ApplyXMargin(
|
||||
RestoreY(
|
||||
Number(value),
|
||||
props.properties.height,
|
||||
props.properties.positionReference
|
||||
),
|
||||
props.properties.margin.top
|
||||
)
|
||||
)} />
|
||||
onChange={(value) => {
|
||||
props.onChange(
|
||||
'y',
|
||||
ApplyXMargin(
|
||||
RestoreY(
|
||||
Number(value),
|
||||
props.properties.height,
|
||||
props.properties.positionReference
|
||||
),
|
||||
props.properties.margin.top
|
||||
)
|
||||
);
|
||||
}}/>
|
||||
</div>
|
||||
</Category>
|
||||
|
||||
|
@ -163,7 +175,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
type='number'
|
||||
min={1}
|
||||
value={props.properties.minWidth.toString()}
|
||||
onChange={(value) => props.onChange('minWidth', Number(value))} />
|
||||
onChange={(value) => { props.onChange('minWidth', Number(value)); }}/>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-width`}
|
||||
labelText={Text({ textId: '@ContainerWidth' })}
|
||||
|
@ -174,8 +186,8 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
min={props.properties.minWidth}
|
||||
max={props.properties.maxWidth}
|
||||
value={(RemoveWidthMargin(props.properties.width, props.properties.margin.left, props.properties.margin.right)).toString()}
|
||||
onChange={(value) => props.onChange('width', ApplyWidthMargin(Number(value), props.properties.margin.left, props.properties.margin.right))}
|
||||
isDisabled={props.properties.isFlex} />
|
||||
onChange={(value) => { props.onChange('width', ApplyWidthMargin(Number(value), props.properties.margin.left, props.properties.margin.right)); }}
|
||||
isDisabled={props.properties.isFlex}/>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-maxWidth`}
|
||||
labelText={Text({ textId: '@ContainerMaxWidth' })}
|
||||
|
@ -185,7 +197,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
type='number'
|
||||
min={1}
|
||||
value={props.properties.maxWidth.toString()}
|
||||
onChange={(value) => props.onChange('maxWidth', Number(value))} />
|
||||
onChange={(value) => { props.onChange('maxWidth', Number(value)); }}/>
|
||||
<div className='col-span-5 p-3'></div>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-minHeight`}
|
||||
|
@ -196,7 +208,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
type='number'
|
||||
min={1}
|
||||
value={props.properties.minHeight.toString()}
|
||||
onChange={(value) => props.onChange('minHeight', Number(value))} />
|
||||
onChange={(value) => { props.onChange('minHeight', Number(value)); }}/>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-height`}
|
||||
labelText={Text({ textId: '@ContainerHeight' })}
|
||||
|
@ -207,7 +219,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
min={props.properties.minHeight}
|
||||
max={props.properties.maxHeight}
|
||||
value={(RemoveWidthMargin(props.properties.height, props.properties.margin.top, props.properties.margin.bottom)).toString()}
|
||||
onChange={(value) => props.onChange('height', ApplyWidthMargin(Number(value), props.properties.margin.top, props.properties.margin.bottom))}
|
||||
onChange={(value) => { props.onChange('height', ApplyWidthMargin(Number(value), props.properties.margin.top, props.properties.margin.bottom)); }}
|
||||
isDisabled={props.properties.isFlex}
|
||||
/>
|
||||
<TextInputGroup
|
||||
|
@ -219,7 +231,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
type='number'
|
||||
min={1}
|
||||
value={props.properties.maxHeight.toString()}
|
||||
onChange={(value) => props.onChange('maxHeight', Number(value))} />
|
||||
onChange={(value) => { props.onChange('maxHeight', Number(value)); }}/>
|
||||
</div>
|
||||
</Category>
|
||||
|
||||
|
@ -239,7 +251,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
type='number'
|
||||
min={0}
|
||||
value={(props.properties.margin.left ?? 0).toString()}
|
||||
onChange={(value) => props.onChange('left', Number(value), PropertyType.Margin)} />
|
||||
onChange={(value) => { props.onChange('left', Number(value), PropertyType.Margin); }}/>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-mb`}
|
||||
labelText={Text({ textId: '@ContainerMarginBottom' })}
|
||||
|
@ -249,7 +261,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
type='number'
|
||||
min={0}
|
||||
value={(props.properties.margin.bottom ?? 0).toString()}
|
||||
onChange={(value) => props.onChange('bottom', Number(value), PropertyType.Margin)} />
|
||||
onChange={(value) => { props.onChange('bottom', Number(value), PropertyType.Margin); }}/>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-mt`}
|
||||
labelText={Text({ textId: '@ContainerMarginTop' })}
|
||||
|
@ -259,7 +271,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
type='number'
|
||||
min={0}
|
||||
value={(props.properties.margin.top ?? 0).toString()}
|
||||
onChange={(value) => props.onChange('top', Number(value), PropertyType.Margin)} />
|
||||
onChange={(value) => { props.onChange('top', Number(value), PropertyType.Margin); }}/>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-mr`}
|
||||
labelText={Text({ textId: '@ContainerMarginRight' })}
|
||||
|
@ -269,7 +281,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
type='number'
|
||||
min={0}
|
||||
value={(props.properties.margin.right ?? 0).toString()}
|
||||
onChange={(value) => props.onChange('right', Number(value), PropertyType.Margin)} />
|
||||
onChange={(value) => { props.onChange('right', Number(value), PropertyType.Margin); }}/>
|
||||
</div>
|
||||
</Category>
|
||||
|
||||
|
@ -287,7 +299,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
inputClassName='ml-auto mr-auto block'
|
||||
type={ToggleType.Full}
|
||||
checked={props.properties.isFlex}
|
||||
onChange={(event) => props.onChange('isFlex', event.target.checked)}
|
||||
onChange={(event) => { props.onChange('isFlex', event.target.checked); }}
|
||||
/>
|
||||
<ToggleButton
|
||||
labelText={Text({ textId: '@ContainerAnchor' })}
|
||||
|
@ -296,7 +308,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
inputClassName='ml-auto mr-auto block'
|
||||
type={ToggleType.Full}
|
||||
checked={props.properties.isAnchor}
|
||||
onChange={(event) => props.onChange('isAnchor', event.target.checked)} />
|
||||
onChange={(event) => { props.onChange('isAnchor', event.target.checked); }}/>
|
||||
</div>
|
||||
</Category>
|
||||
|
||||
|
@ -326,7 +338,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
value: symbol.id
|
||||
}))}
|
||||
value={props.properties.linkedSymbolId ?? ''}
|
||||
onChange={(event) => props.onChange('linkedSymbolId', event.target.value)} />
|
||||
onChange={(event) => { props.onChange('linkedSymbolId', event.target.value); }}/>
|
||||
</div>
|
||||
</Category>
|
||||
|
||||
|
@ -346,38 +358,76 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
name='ShowSelfDimensions'
|
||||
labelText={Text({ textId: '@ContainerShowDimension' })}
|
||||
value={props.properties.dimensionOptions.selfDimensions.positions}
|
||||
onChange={(key, value) => props.onChange(key, value, PropertyType.SelfDimension)}
|
||||
onChange={(key, value) => { props.onChange(key, value, PropertyType.SelfDimension); }}
|
||||
/>
|
||||
<InputGroup
|
||||
labelText={Text({ textId: '@Color' })}
|
||||
labelText={Text({ textId: '@StyleStrokeColor' })}
|
||||
inputKey='color'
|
||||
labelClassName=''
|
||||
inputClassName=''
|
||||
type='color'
|
||||
value={props.properties.dimensionOptions.selfDimensions.color}
|
||||
onChange={(e) => props.onChange('color', e.target.value, PropertyType.SelfDimension)}/>
|
||||
onChange={(e) => { props.onChange('color', e.target.value, PropertyType.SelfDimension); }}/>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-selfDimensions-width`}
|
||||
labelText={Text({ textId: '@StyleStrokeWidth' })}
|
||||
inputKey='width'
|
||||
labelClassName=''
|
||||
inputClassName=''
|
||||
type='number'
|
||||
min={0}
|
||||
value={(props.properties.dimensionOptions.selfDimensions.width ?? 0).toString()}
|
||||
onChange={(value) => { props.onChange('width', Number(value), PropertyType.SelfDimension); }}/>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-selfDimensions-dasharray`}
|
||||
labelText={Text({ textId: '@StyleStrokeDashArray' })}
|
||||
inputKey='dashArray'
|
||||
labelClassName=''
|
||||
inputClassName=''
|
||||
type='text'
|
||||
value={props.properties.dimensionOptions.selfDimensions.dashArray ?? ''}
|
||||
onChange={(value) => { props.onChange('dashArray', value, PropertyType.SelfDimension); }}/>
|
||||
</div>
|
||||
}
|
||||
{
|
||||
SHOW_SELF_MARGINS_DIMENSIONS &&
|
||||
<div className='grid grid-cols-1 gap-2'>
|
||||
<PositionCheckboxes
|
||||
id='positionsSelfMarginsDimensions'
|
||||
attribute='positions'
|
||||
name='ShowSelfMarginsDimensions'
|
||||
labelText={Text({ textId: '@ContainerShowMarginsDimension' })}
|
||||
value={props.properties.dimensionOptions.selfMarginsDimensions.positions}
|
||||
onChange={(key, value) => props.onChange(key, value, PropertyType.SelfMarginDimension)}
|
||||
/>
|
||||
<InputGroup
|
||||
labelText={Text({ textId: '@Color' })}
|
||||
inputKey='color'
|
||||
labelClassName=''
|
||||
inputClassName=''
|
||||
type='color'
|
||||
value={props.properties.dimensionOptions.selfMarginsDimensions.color}
|
||||
onChange={(e) => props.onChange('color', e.target.value, PropertyType.SelfMarginDimension)}/>
|
||||
</div>
|
||||
<div className='grid grid-cols-1 gap-2'>
|
||||
<PositionCheckboxes
|
||||
id='positionsSelfMarginsDimensions'
|
||||
attribute='positions'
|
||||
name='ShowSelfMarginsDimensions'
|
||||
labelText={Text({ textId: '@ContainerShowMarginsDimension' })}
|
||||
value={props.properties.dimensionOptions.selfMarginsDimensions.positions}
|
||||
onChange={(key, value) => { props.onChange(key, value, PropertyType.SelfMarginDimension); }}
|
||||
/>
|
||||
<InputGroup
|
||||
labelText={Text({ textId: '@StyleStrokeColor' })}
|
||||
inputKey='color'
|
||||
labelClassName=''
|
||||
inputClassName=''
|
||||
type='color'
|
||||
value={props.properties.dimensionOptions.selfMarginsDimensions.color}
|
||||
onChange={(e) => { props.onChange('color', e.target.value, PropertyType.SelfMarginDimension); }}/>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-selfMarginsDimensions-width`}
|
||||
labelText={Text({ textId: '@StyleStrokeWidth' })}
|
||||
inputKey='width'
|
||||
labelClassName=''
|
||||
inputClassName=''
|
||||
type='number'
|
||||
min={0}
|
||||
value={(props.properties.dimensionOptions.selfMarginsDimensions.width ?? 0).toString()}
|
||||
onChange={(value) => { props.onChange('width', Number(value), PropertyType.SelfMarginDimension); }}/>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-selfMarginsDimensions-dasharray`}
|
||||
labelText={Text({ textId: '@StyleStrokeDashArray' })}
|
||||
inputKey='dashArray'
|
||||
labelClassName=''
|
||||
inputClassName=''
|
||||
type='text'
|
||||
value={props.properties.dimensionOptions.selfMarginsDimensions.dashArray ?? ''}
|
||||
onChange={(value) => { props.onChange('dashArray', value, PropertyType.SelfMarginDimension); }}/>
|
||||
</div>
|
||||
}
|
||||
{
|
||||
SHOW_CHILDREN_DIMENSIONS &&
|
||||
|
@ -388,16 +438,35 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
name='ShowChildrenDimensions'
|
||||
labelText={Text({ textId: '@ContainerShowChildrenDimension' })}
|
||||
value={props.properties.dimensionOptions.childrenDimensions.positions}
|
||||
onChange={(key, value) => props.onChange(key, value, PropertyType.ChildrenDimensions)}
|
||||
onChange={(key, value) => { props.onChange(key, value, PropertyType.ChildrenDimensions); }}
|
||||
/>
|
||||
<InputGroup
|
||||
labelText={Text({ textId: '@Color' })}
|
||||
labelText={Text({ textId: '@StyleStrokeColor' })}
|
||||
inputKey='color'
|
||||
labelClassName=''
|
||||
inputClassName=''
|
||||
type='color'
|
||||
value={props.properties.dimensionOptions.childrenDimensions.color}
|
||||
onChange={(e) => props.onChange('color', e.target.value, PropertyType.ChildrenDimensions)}/>
|
||||
onChange={(e) => { props.onChange('color', e.target.value, PropertyType.ChildrenDimensions); }}/>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-childrenDimensions-width`}
|
||||
labelText={Text({ textId: '@StyleStrokeWidth' })}
|
||||
inputKey='width'
|
||||
labelClassName=''
|
||||
inputClassName=''
|
||||
type='number'
|
||||
min={0}
|
||||
value={(props.properties.dimensionOptions.childrenDimensions.width ?? 0).toString()}
|
||||
onChange={(value) => { props.onChange('width', Number(value), PropertyType.ChildrenDimensions); }}/>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-childrenDimensions-dasharray`}
|
||||
labelText={Text({ textId: '@StyleStrokeDashArray' })}
|
||||
inputKey='dashArray'
|
||||
labelClassName=''
|
||||
inputClassName=''
|
||||
type='text'
|
||||
value={props.properties.dimensionOptions.childrenDimensions.dashArray ?? ''}
|
||||
onChange={(value) => { props.onChange('dashArray', value, PropertyType.ChildrenDimensions); }}/>
|
||||
</div>
|
||||
}
|
||||
{
|
||||
|
@ -409,7 +478,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
name='MarkPosition'
|
||||
value={props.properties.dimensionOptions.markPosition}
|
||||
labelText={Text({ textId: '@ContainerMarkPosition' })}
|
||||
onChange={(key, value) => props.onChange(key, value, PropertyType.DimensionOptions)}
|
||||
onChange={(key, value) => { props.onChange(key, value, PropertyType.DimensionOptions); }}
|
||||
/>
|
||||
</div>
|
||||
<div className='grid grid-cols-1 gap-2'>
|
||||
|
@ -419,23 +488,42 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
name='ShowDimensionWithMarks'
|
||||
labelText={Text({ textId: '@ContainerShowDimensionWithMarks' })}
|
||||
value={props.properties.dimensionOptions.dimensionWithMarks.positions}
|
||||
onChange={(key, value) => props.onChange(key, value, PropertyType.DimensionWithMarks)}
|
||||
onChange={(key, value) => { props.onChange(key, value, PropertyType.DimensionWithMarks); }}
|
||||
/>
|
||||
<InputGroup
|
||||
labelText={Text({ textId: '@Color' })}
|
||||
labelText={Text({ textId: '@StyleStrokeColor' })}
|
||||
inputKey='color'
|
||||
labelClassName=''
|
||||
inputClassName=''
|
||||
type='color'
|
||||
value={props.properties.dimensionOptions.dimensionWithMarks.color}
|
||||
onChange={(e) => props.onChange('color', e.target.value, PropertyType.DimensionWithMarks)}/>
|
||||
onChange={(e) => { props.onChange('color', e.target.value, PropertyType.DimensionWithMarks); }}/>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-dimensionWithMarks-width`}
|
||||
labelText={Text({ textId: '@StyleStrokeWidth' })}
|
||||
inputKey='width'
|
||||
labelClassName=''
|
||||
inputClassName=''
|
||||
type='number'
|
||||
min={0}
|
||||
value={(props.properties.dimensionOptions.dimensionWithMarks.width ?? 0).toString()}
|
||||
onChange={(value) => { props.onChange('width', Number(value), PropertyType.DimensionWithMarks); }}/>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-dimensionWithMarks-dasharray`}
|
||||
labelText={Text({ textId: '@StyleStrokeDashArray' })}
|
||||
inputKey='dashArray'
|
||||
labelClassName=''
|
||||
inputClassName=''
|
||||
type='text'
|
||||
value={props.properties.dimensionOptions.dimensionWithMarks.dashArray ?? ''}
|
||||
onChange={(value) => { props.onChange('dashArray', value, PropertyType.DimensionWithMarks); }}/>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
</Category>
|
||||
|
||||
{ props.properties.style !== undefined &&
|
||||
{props.properties.style !== undefined &&
|
||||
<Category category={{
|
||||
Type: 'Style',
|
||||
DisplayedText: Text({ textId: '@ContainerStyle' })
|
||||
|
@ -451,7 +539,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
inputClassName='col-span-3'
|
||||
type='string'
|
||||
value={props.properties.style.stroke ?? 'black'}
|
||||
onChange={(value) => props.onChange('stroke', value, PropertyType.Style)}
|
||||
onChange={(value) => { props.onChange('stroke', value, PropertyType.Style); }}
|
||||
/>
|
||||
<InputGroup
|
||||
labelKey={`${props.properties.id}-strokeOpacity`}
|
||||
|
@ -464,7 +552,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
max={1}
|
||||
step={0.01}
|
||||
value={(props.properties.style.strokeOpacity ?? 1).toString()}
|
||||
onChange={(event) => props.onChange('strokeOpacity', Number(event.target.value), PropertyType.Style)}
|
||||
onChange={(event) => { props.onChange('strokeOpacity', Number(event.target.value), PropertyType.Style); }}
|
||||
/>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-strokeWidth`}
|
||||
|
@ -474,7 +562,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
inputClassName='col-span-3'
|
||||
type='number'
|
||||
value={(props.properties.style.strokeWidth ?? 1).toString()}
|
||||
onChange={(value) => props.onChange('strokeWidth', Number(value), PropertyType.Style)}
|
||||
onChange={(value) => { props.onChange('strokeWidth', Number(value), PropertyType.Style); }}
|
||||
/>
|
||||
<TextInputGroup
|
||||
id={`${props.properties.id}-fill`}
|
||||
|
@ -484,7 +572,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
inputClassName='col-span-3'
|
||||
type='string'
|
||||
value={props.properties.style.fill ?? 'black'}
|
||||
onChange={(value) => props.onChange('fill', value, PropertyType.Style)}
|
||||
onChange={(value) => { props.onChange('fill', value, PropertyType.Style); }}
|
||||
/>
|
||||
<InputGroup
|
||||
labelKey={`${props.properties.id}-fillOpacity`}
|
||||
|
@ -497,7 +585,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
|||
max={1}
|
||||
step={0.01}
|
||||
value={(props.properties.style.fillOpacity ?? 1).toString()}
|
||||
onChange={(event) => props.onChange('fillOpacity', Number(event.target.value), PropertyType.Style)}
|
||||
onChange={(event) => { props.onChange('fillOpacity', Number(event.target.value), PropertyType.Style); }}
|
||||
/>
|
||||
</div>
|
||||
</Category>
|
||||
|
|
|
@ -4,11 +4,12 @@ import { expect, describe, it, vi } from 'vitest';
|
|||
import { PositionReference } from '../../Enums/PositionReference';
|
||||
import { IContainerProperties } from '../../Interfaces/IContainerProperties';
|
||||
import { Orientation } from '../../Enums/Orientation';
|
||||
import { Properties } from './ContainerProperties';
|
||||
import { ContainerProperties } from './ContainerProperties';
|
||||
import { DEFAULT_DIMENSION_OPTION } from '../../utils/default';
|
||||
|
||||
describe.concurrent('Properties', () => {
|
||||
it('No properties', () => {
|
||||
render(<Properties
|
||||
render(<ContainerProperties
|
||||
properties={undefined}
|
||||
onChange={() => {}}
|
||||
symbols={new Map()}
|
||||
|
@ -43,23 +44,11 @@ describe.concurrent('Properties', () => {
|
|||
warning: '',
|
||||
hideChildrenInTreeview: false,
|
||||
dimensionOptions: {
|
||||
childrenDimensions: {
|
||||
color: '#000000',
|
||||
positions: []
|
||||
},
|
||||
selfDimensions: {
|
||||
color: '#000000',
|
||||
positions: []
|
||||
},
|
||||
selfMarginsDimensions: {
|
||||
color: '#000000',
|
||||
positions: []
|
||||
},
|
||||
childrenDimensions: DEFAULT_DIMENSION_OPTION,
|
||||
selfDimensions: DEFAULT_DIMENSION_OPTION,
|
||||
selfMarginsDimensions: DEFAULT_DIMENSION_OPTION,
|
||||
markPosition: [],
|
||||
dimensionWithMarks: {
|
||||
color: '#000000',
|
||||
positions: []
|
||||
}
|
||||
dimensionWithMarks: DEFAULT_DIMENSION_OPTION
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -67,7 +56,7 @@ describe.concurrent('Properties', () => {
|
|||
(prop as any)[key] = value;
|
||||
});
|
||||
|
||||
const { container, rerender } = render(<Properties
|
||||
const { container, rerender } = render(<ContainerProperties
|
||||
properties={prop}
|
||||
onChange={handleChange}
|
||||
symbols={new Map()}
|
||||
|
@ -108,7 +97,7 @@ describe.concurrent('Properties', () => {
|
|||
expect(prop.parentId).toBe('parentId');
|
||||
expect(prop.x).toBe(2);
|
||||
expect(prop.y).toBe(2);
|
||||
rerender(<Properties
|
||||
rerender(<ContainerProperties
|
||||
properties={Object.assign({}, prop)}
|
||||
onChange={handleChange}
|
||||
symbols={new Map()}
|
||||
|
|
|
@ -10,7 +10,7 @@ interface IPropertiesProps {
|
|||
onChange: (key: string, value: string | number | boolean | number[], type?: PropertyType) => void
|
||||
}
|
||||
|
||||
export function Properties(props: IPropertiesProps): JSX.Element {
|
||||
export function ContainerProperties(props: IPropertiesProps): JSX.Element {
|
||||
if (props.properties === undefined) {
|
||||
return <div></div>;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,30 @@ export function SelectContainer(
|
|||
return history;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deselect a container
|
||||
* @returns New history
|
||||
*/
|
||||
export function DeselectContainer(
|
||||
containerId: string,
|
||||
fullHistory: IHistoryState[],
|
||||
historyCurrentStep: number
|
||||
): IHistoryState[] {
|
||||
const history = GetCurrentHistory(fullHistory, historyCurrentStep);
|
||||
const current = history[history.length - 1];
|
||||
|
||||
history.push({
|
||||
lastAction: `Deselect ${containerId}`,
|
||||
mainContainer: current.mainContainer,
|
||||
containers: structuredClone(current.containers),
|
||||
selectedContainerId: 'undefined',
|
||||
typeCounters: Object.assign({}, current.typeCounters),
|
||||
symbols: structuredClone(current.symbols),
|
||||
selectedSymbolId: current.selectedSymbolId
|
||||
});
|
||||
return history;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a container
|
||||
* @param containerId containerId of the container to delete
|
||||
|
|
|
@ -53,6 +53,7 @@ export function SaveEditorAsSVG(): void {
|
|||
svg.replaceChildren(...mainSvg);
|
||||
|
||||
// remove the selector
|
||||
// TODO: Fix this with SelectorMode != Nothing or with some html magic
|
||||
const group = svg.children[svg.children.length - 1];
|
||||
group.removeChild(group.children[group.children.length - 1]);
|
||||
if (SHOW_SELECTOR_TEXT) {
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
import * as React from 'react';
|
||||
import { useState } from 'react';
|
||||
import useSize from '@react-hook/size';
|
||||
import { FixedSizeList as List } from 'react-window';
|
||||
import { Properties } from '../ContainerProperties/ContainerProperties';
|
||||
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
|
||||
import { ContainerProperties } from '../ContainerProperties/ContainerProperties';
|
||||
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||
import { FindContainerById, MakeRecursionDFSIterator } from '../../utils/itertools';
|
||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
||||
import { PropertyType } from '../../Enums/PropertyType';
|
||||
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
|
||||
import { ToggleSideBar } from '../Sidebar/ToggleSideBar/ToggleSideBar';
|
||||
import { Text } from '../Text/Text';
|
||||
|
||||
interface IElementsListProps {
|
||||
interface IElementsSideBarProps {
|
||||
containers: Map<string, IContainerModel>
|
||||
mainContainer: IContainerModel
|
||||
symbols: Map<string, ISymbolModel>
|
||||
|
@ -20,6 +23,8 @@ interface IElementsListProps {
|
|||
) => void
|
||||
selectContainer: (containerId: string) => void
|
||||
addContainer: (index: number, type: string, parent: string) => void
|
||||
isExpanded: boolean
|
||||
onExpandChange: () => void
|
||||
}
|
||||
|
||||
function RemoveBorderClasses(target: HTMLButtonElement, exception: string = ''): void {
|
||||
|
@ -119,10 +124,11 @@ function HandleOnDrop(
|
|||
}
|
||||
}
|
||||
|
||||
export function ElementsList(props: IElementsListProps): JSX.Element {
|
||||
export function ElementsSideBar(props: IElementsSideBarProps): JSX.Element {
|
||||
// States
|
||||
const divRef = React.useRef<HTMLDivElement>(null);
|
||||
const [, height] = useSize(divRef);
|
||||
const [,height] = useSize(divRef);
|
||||
const [showProperties, setShowProperties] = useState(props.isExpanded);
|
||||
|
||||
// Render
|
||||
const it = MakeRecursionDFSIterator(props.mainContainer, props.containers, 0, [0, 0], true);
|
||||
|
@ -160,10 +166,21 @@ export function ElementsList(props: IElementsListProps): JSX.Element {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='h-full flex flex-col'>
|
||||
<div ref={divRef} className='h-1/2'>
|
||||
<div className='flex flex-row h-full w-full' >
|
||||
{showProperties &&
|
||||
<div className='flex flex-1 flex-col w-64 border-r-2 border-slate-400'>
|
||||
<ContainerProperties
|
||||
properties={props.selectedContainer?.properties}
|
||||
symbols={props.symbols}
|
||||
onChange={props.onPropertyChange}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
<div className='flex w-64' ref={divRef}>
|
||||
<div className='w-6'>
|
||||
<ToggleSideBar title={Text({ textId: '@Properties' })} checked={showProperties} onChange={(newValue) => { setShowProperties(newValue); props.onExpandChange(); }} />
|
||||
</div>
|
||||
<List
|
||||
className="List divide-y divide-black overflow-y-auto"
|
||||
itemCount={containers.length}
|
||||
itemSize={35}
|
||||
height={height}
|
||||
|
@ -172,13 +189,6 @@ export function ElementsList(props: IElementsListProps): JSX.Element {
|
|||
{Row}
|
||||
</List>
|
||||
</div>
|
||||
<div className='grow overflow-auto'>
|
||||
<Properties
|
||||
properties={props.selectedContainer?.properties}
|
||||
symbols={props.symbols}
|
||||
onChange={props.onPropertyChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||
import { DIMENSION_MARGIN } from '../../../utils/default';
|
||||
import { GetAbsolutePosition, MakeBFSIterator } from '../../../utils/itertools';
|
||||
import { TransformX } from '../../../utils/svg';
|
||||
import { Dimension } from './Dimension';
|
||||
|
||||
interface IDimensionLayerProps {
|
||||
containers: Map<string, IContainerModel>
|
||||
roots: IContainerModel | IContainerModel[] | null
|
||||
scale?: number
|
||||
}
|
||||
|
||||
function GetDimensionsNodes(
|
||||
containers: Map<string, IContainerModel>,
|
||||
root: IContainerModel,
|
||||
scale: number
|
||||
): React.ReactNode[] {
|
||||
const it = MakeBFSIterator(root, containers);
|
||||
const dimensions: React.ReactNode[] = [];
|
||||
let currentDepth = 0;
|
||||
let min = Infinity;
|
||||
let max = -Infinity;
|
||||
let lastY = 0;
|
||||
for (const { container, depth } of it) {
|
||||
if (currentDepth !== depth) {
|
||||
AddNewDimension(currentDepth, min, max, lastY, scale, '#000000', dimensions);
|
||||
|
||||
currentDepth = depth;
|
||||
min = Infinity;
|
||||
max = -Infinity;
|
||||
}
|
||||
|
||||
const absoluteX = GetAbsolutePosition(containers, container)[0];
|
||||
const x = TransformX(absoluteX, container.properties.width, container.properties.positionReference);
|
||||
lastY = container.properties.y + container.properties.height;
|
||||
if (x < min) {
|
||||
min = x;
|
||||
}
|
||||
|
||||
if (x > max) {
|
||||
max = x;
|
||||
}
|
||||
}
|
||||
|
||||
AddNewDimension(currentDepth, min, max, lastY, scale, '#000000', dimensions);
|
||||
|
||||
return dimensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* A layer containing all dimension
|
||||
* @param props
|
||||
* @returns
|
||||
*/
|
||||
export function DepthDimensionLayer(props: IDimensionLayerProps): JSX.Element {
|
||||
let dimensions: React.ReactNode[] = [];
|
||||
const scale = props.scale ?? 1;
|
||||
if (Array.isArray(props.roots)) {
|
||||
props.roots.forEach(child => {
|
||||
dimensions.concat(GetDimensionsNodes(props.containers, child, scale));
|
||||
});
|
||||
} else if (props.roots !== null) {
|
||||
dimensions = GetDimensionsNodes(props.containers, props.roots, scale);
|
||||
}
|
||||
return (
|
||||
<g>
|
||||
{dimensions}
|
||||
</g>
|
||||
);
|
||||
}
|
||||
|
||||
function AddNewDimension(currentDepth: number, min: number, max: number, lastY: number, scale: number, color: string, dimensions: React.ReactNode[]): void {
|
||||
const id = `dim-depth-${currentDepth}`;
|
||||
const xStart = min;
|
||||
const xEnd = max;
|
||||
const y = lastY + (DIMENSION_MARGIN * (currentDepth + 1)) / scale;
|
||||
const width = xEnd - xStart;
|
||||
const text = width
|
||||
.toFixed(0)
|
||||
.toString();
|
||||
|
||||
if (width === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dimensions.push(
|
||||
<Dimension
|
||||
key={id}
|
||||
id={id}
|
||||
xStart={xStart}
|
||||
yStart={y}
|
||||
xEnd={xEnd}
|
||||
yEnd={y}
|
||||
text={text}
|
||||
scale={scale}
|
||||
color={color}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
import * as React from 'react';
|
||||
import { type IDimensionOptions } from '../../../Interfaces/IDimensionOptions';
|
||||
import { NOTCHES_LENGTH } from '../../../utils/default';
|
||||
|
||||
export type IDimensionStyle = Omit<IDimensionOptions, 'positions'>;
|
||||
|
||||
interface IDimensionProps {
|
||||
id: string
|
||||
xStart: number
|
||||
|
@ -8,7 +11,7 @@ interface IDimensionProps {
|
|||
xEnd: number
|
||||
yEnd: number
|
||||
text: string
|
||||
color: string
|
||||
style: IDimensionStyle
|
||||
scale?: number
|
||||
}
|
||||
|
||||
|
@ -28,8 +31,9 @@ function ApplyParametric(x0: number, t: number, vx: number): number {
|
|||
export function Dimension(props: IDimensionProps): JSX.Element {
|
||||
const scale = props.scale ?? 1;
|
||||
const style: React.CSSProperties = {
|
||||
stroke: props.color,
|
||||
strokeWidth: 2 / scale
|
||||
stroke: props.style.color,
|
||||
strokeWidth: (props.style.width ?? 2) / scale,
|
||||
strokeDasharray: props.style.dashArray
|
||||
};
|
||||
|
||||
/// We need to find the points of the notches
|
||||
|
@ -79,9 +83,11 @@ export function Dimension(props: IDimensionProps): JSX.Element {
|
|||
x2={endBottomX}
|
||||
y2={endBottomY}
|
||||
style={style}/>
|
||||
<text textAnchor={'middle'} alignmentBaseline={'central'}
|
||||
<text
|
||||
x={textX}
|
||||
y={textY}
|
||||
textAnchor={'middle'}
|
||||
alignmentBaseline={'central'}
|
||||
style={{
|
||||
transform: `rotate(${rotation}turn) scale(${1 / scale})`,
|
||||
transformOrigin: `${textX}px ${textY}px`
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import * as React from 'react';
|
||||
import { Orientation } from '../../../Enums/Orientation';
|
||||
import { Position } from '../../../Enums/Position';
|
||||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||
import {
|
||||
DIMENSION_MARGIN,
|
||||
SHOW_BORROWER_DIMENSIONS,
|
||||
|
@ -11,10 +10,13 @@ import {
|
|||
} from '../../../utils/default';
|
||||
import { FindContainerById, MakeRecursionDFSIterator, Pairwise } from '../../../utils/itertools';
|
||||
import { TransformX, TransformY } from '../../../utils/svg';
|
||||
import { Dimension } from './Dimension';
|
||||
import { Dimension, type IDimensionStyle } from './Dimension';
|
||||
import { type IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||
import { type ISymbolModel } from '../../../Interfaces/ISymbolModel';
|
||||
|
||||
interface IDimensionLayerProps {
|
||||
containers: Map<string, IContainerModel>
|
||||
symbols: Map<string, ISymbolModel>
|
||||
root: IContainerModel
|
||||
scale: number
|
||||
}
|
||||
|
@ -58,7 +60,7 @@ function ActionByPosition(
|
|||
* @param param0 Object with the root container and the scale of the svg
|
||||
* @returns A list of dimensions
|
||||
*/
|
||||
function Dimensions({ containers, root, scale }: IDimensionLayerProps): React.ReactNode[] {
|
||||
function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps): React.ReactNode[] {
|
||||
const it = MakeRecursionDFSIterator(root, containers, 0, [0, 0]);
|
||||
const dimensions: React.ReactNode[] = [];
|
||||
const topDim = root.properties.y;
|
||||
|
@ -87,8 +89,8 @@ function Dimensions({ containers, root, scale }: IDimensionLayerProps): React.Re
|
|||
container,
|
||||
currentTransform,
|
||||
dimensions,
|
||||
scale,
|
||||
container.properties.dimensionOptions.selfDimensions.color]
|
||||
scale
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -102,8 +104,8 @@ function Dimensions({ containers, root, scale }: IDimensionLayerProps): React.Re
|
|||
container,
|
||||
currentTransform,
|
||||
dimensions,
|
||||
scale,
|
||||
container.properties.dimensionOptions.selfMarginsDimensions.color]
|
||||
scale
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -111,6 +113,7 @@ function Dimensions({ containers, root, scale }: IDimensionLayerProps): React.Re
|
|||
ActionByPosition(
|
||||
dimMapped,
|
||||
container.properties.dimensionOptions.dimensionWithMarks.positions,
|
||||
|
||||
AddHorizontalBorrowerDimension,
|
||||
AddVerticalBorrowerDimension,
|
||||
[
|
||||
|
@ -119,8 +122,8 @@ function Dimensions({ containers, root, scale }: IDimensionLayerProps): React.Re
|
|||
depth,
|
||||
currentTransform,
|
||||
dimensions,
|
||||
scale,
|
||||
container.properties.dimensionOptions.dimensionWithMarks.color]
|
||||
scale
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -135,8 +138,21 @@ function Dimensions({ containers, root, scale }: IDimensionLayerProps): React.Re
|
|||
container,
|
||||
currentTransform,
|
||||
dimensions,
|
||||
scale,
|
||||
container.properties.dimensionOptions.childrenDimensions.color]
|
||||
scale
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let startDepthSymbols: number = 0;
|
||||
for (const symbol of symbols) {
|
||||
if (symbol[1].showDimension) {
|
||||
startDepthSymbols++;
|
||||
AddHorizontalSymbolDimension(
|
||||
symbol[1],
|
||||
dimensions,
|
||||
scale,
|
||||
startDepthSymbols
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -144,6 +160,40 @@ function Dimensions({ containers, root, scale }: IDimensionLayerProps): React.Re
|
|||
return dimensions;
|
||||
}
|
||||
|
||||
function AddHorizontalSymbolDimension(
|
||||
symbol: ISymbolModel,
|
||||
dimensions: React.ReactNode[],
|
||||
scale: number,
|
||||
depth: number
|
||||
): void {
|
||||
const width = symbol.x + (symbol.width / 2);
|
||||
if (width != null && width > 0) {
|
||||
const id = `dim-y-margin-left${symbol.width.toFixed(0)}-${symbol.id}`;
|
||||
|
||||
const offset = (DIMENSION_MARGIN * (depth + 1)) / scale;
|
||||
const text = width
|
||||
.toFixed(0)
|
||||
.toString();
|
||||
|
||||
// TODO: Put this in default.ts
|
||||
const defaultDimensionSymbolStyle: IDimensionStyle = {
|
||||
color: 'black'
|
||||
};
|
||||
dimensions.push(
|
||||
<Dimension
|
||||
key={id}
|
||||
id={id}
|
||||
xStart={0}
|
||||
yStart={-offset}
|
||||
xEnd={width}
|
||||
yEnd={-offset}
|
||||
text={text}
|
||||
scale={scale}
|
||||
style={defaultDimensionSymbolStyle}/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A layer containing all dimension
|
||||
* @param props
|
||||
|
@ -152,7 +202,7 @@ function Dimensions({ containers, root, scale }: IDimensionLayerProps): React.Re
|
|||
export function DimensionLayer(props: IDimensionLayerProps): JSX.Element {
|
||||
return (
|
||||
<g>
|
||||
{ Dimensions(props) }
|
||||
{Dimensions(props)}
|
||||
</g>
|
||||
);
|
||||
}
|
||||
|
@ -165,10 +215,10 @@ function AddHorizontalChildrenDimension(
|
|||
container: IContainerModel,
|
||||
currentTransform: [number, number],
|
||||
dimensions: React.ReactNode[],
|
||||
scale: number,
|
||||
color: string
|
||||
scale: number
|
||||
): void {
|
||||
const childrenId = `dim-y${yDim.toFixed(0)}-children-${container.properties.id}`;
|
||||
const style = container.properties.dimensionOptions.childrenDimensions;
|
||||
|
||||
const lastChildId = container.children[container.children.length - 1];
|
||||
const lastChild = FindContainerById(containers, lastChildId);
|
||||
|
@ -218,7 +268,7 @@ function AddHorizontalChildrenDimension(
|
|||
yEnd={yDim}
|
||||
text={textChildren}
|
||||
scale={scale}
|
||||
color={color}/>);
|
||||
style={style}/>);
|
||||
}
|
||||
|
||||
function AddVerticalChildrenDimension(
|
||||
|
@ -228,11 +278,10 @@ function AddVerticalChildrenDimension(
|
|||
container: IContainerModel,
|
||||
currentTransform: [number, number],
|
||||
dimensions: React.ReactNode[],
|
||||
scale: number,
|
||||
color: string
|
||||
|
||||
scale: number
|
||||
): void {
|
||||
const childrenId = `dim-x${xDim.toFixed(0)}-children-${container.properties.id}`;
|
||||
const style = container.properties.dimensionOptions.childrenDimensions;
|
||||
|
||||
const lastChildId = container.children[container.children.length - 1];
|
||||
const lastChild = FindContainerById(containers, lastChildId);
|
||||
|
@ -287,7 +336,7 @@ function AddVerticalChildrenDimension(
|
|||
yEnd={yChildrenEnd + offset}
|
||||
text={textChildren}
|
||||
scale={scale}
|
||||
color={color}
|
||||
style={style}
|
||||
/>);
|
||||
}
|
||||
|
||||
|
@ -298,9 +347,9 @@ function AddHorizontalBorrowerDimension(
|
|||
depth: number,
|
||||
currentTransform: [number, number],
|
||||
dimensions: React.ReactNode[],
|
||||
scale: number,
|
||||
color: string
|
||||
scale: number
|
||||
): void {
|
||||
const style = container.properties.dimensionOptions.dimensionWithMarks;
|
||||
const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform);
|
||||
const marks = []; // list of vertical lines for the dimension
|
||||
for (const {
|
||||
|
@ -345,7 +394,7 @@ function AddHorizontalBorrowerDimension(
|
|||
yEnd={yDim}
|
||||
text={value.toFixed(0)}
|
||||
scale={scale}
|
||||
color={color}/>);
|
||||
style={style}/>);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
@ -358,9 +407,9 @@ function AddVerticalBorrowerDimension(
|
|||
depth: number,
|
||||
currentTransform: [number, number],
|
||||
dimensions: React.ReactNode[],
|
||||
scale: number,
|
||||
color: string
|
||||
scale: number
|
||||
): void {
|
||||
const style = container.properties.dimensionOptions.dimensionWithMarks;
|
||||
const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform);
|
||||
const marks = []; // list of vertical lines for the dimension
|
||||
for (const {
|
||||
|
@ -410,7 +459,7 @@ function AddVerticalBorrowerDimension(
|
|||
yEnd={next}
|
||||
text={value.toFixed(0)}
|
||||
scale={scale}
|
||||
color={color}/>);
|
||||
style={style}/>);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
@ -421,9 +470,9 @@ function AddVerticalSelfDimension(
|
|||
container: IContainerModel,
|
||||
currentTransform: [number, number],
|
||||
dimensions: React.ReactNode[],
|
||||
scale: number,
|
||||
color: string
|
||||
scale: number
|
||||
): void {
|
||||
const style = container.properties.dimensionOptions.selfDimensions;
|
||||
const height = container.properties.height;
|
||||
const idVert = `dim-x${xDim.toFixed(0)}-${container.properties.id}`;
|
||||
let yStart = container.properties.y + currentTransform[1] + height;
|
||||
|
@ -446,19 +495,18 @@ function AddVerticalSelfDimension(
|
|||
yEnd={yEnd}
|
||||
text={textVert}
|
||||
scale={scale}
|
||||
color={color}/>
|
||||
style={style}/>
|
||||
);
|
||||
}
|
||||
|
||||
function AddHorizontalSelfDimension(
|
||||
|
||||
yDim: number,
|
||||
container: IContainerModel,
|
||||
currentTransform: [number, number],
|
||||
dimensions: React.ReactNode[],
|
||||
scale: number,
|
||||
color: string
|
||||
scale: number
|
||||
): void {
|
||||
const style = container.properties.dimensionOptions.selfDimensions;
|
||||
const width = container.properties.width;
|
||||
const id = `dim-y${yDim.toFixed(0)}-${container.properties.id}`;
|
||||
const xStart = container.properties.x + currentTransform[0];
|
||||
|
@ -476,19 +524,18 @@ function AddHorizontalSelfDimension(
|
|||
yEnd={yDim}
|
||||
text={text}
|
||||
scale={scale}
|
||||
color={color}/>
|
||||
style={style}/>
|
||||
);
|
||||
}
|
||||
|
||||
function AddHorizontalSelfMarginsDimension(
|
||||
|
||||
yDim: number,
|
||||
container: IContainerModel,
|
||||
currentTransform: [number, number],
|
||||
dimensions: React.ReactNode[],
|
||||
scale: number,
|
||||
color: string
|
||||
scale: number
|
||||
): void {
|
||||
const style = container.properties.dimensionOptions.selfMarginsDimensions;
|
||||
const left = container.properties.margin.left;
|
||||
if (left != null) {
|
||||
const id = `dim-y-margin-left${yDim.toFixed(0)}-${container.properties.id}`;
|
||||
|
@ -507,7 +554,7 @@ function AddHorizontalSelfMarginsDimension(
|
|||
yEnd={yDim}
|
||||
text={text}
|
||||
scale={scale}
|
||||
color={color}/>
|
||||
style={style}/>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -529,7 +576,7 @@ function AddHorizontalSelfMarginsDimension(
|
|||
yEnd={yDim}
|
||||
text={text}
|
||||
scale={scale}
|
||||
color={color}/>
|
||||
style={style}/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -540,9 +587,9 @@ function AddVerticalSelfMarginDimension(
|
|||
container: IContainerModel,
|
||||
currentTransform: [number, number],
|
||||
dimensions: React.ReactNode[],
|
||||
scale: number,
|
||||
color: string
|
||||
scale: number
|
||||
): void {
|
||||
const style = container.properties.dimensionOptions.selfMarginsDimensions;
|
||||
const top = container.properties.margin.top;
|
||||
if (top != null) {
|
||||
const idVert = `dim-x-margin-top${xDim.toFixed(0)}-${container.properties.id}`;
|
||||
|
@ -566,7 +613,7 @@ function AddVerticalSelfMarginDimension(
|
|||
yEnd={yEnd}
|
||||
text={textVert}
|
||||
scale={scale}
|
||||
color={color}/>
|
||||
style={style}/>
|
||||
);
|
||||
}
|
||||
const bottom = container.properties.margin.bottom;
|
||||
|
@ -592,7 +639,7 @@ function AddVerticalSelfMarginDimension(
|
|||
yEnd={yEnd}
|
||||
text={textVert}
|
||||
scale={scale}
|
||||
color={color}/>
|
||||
style={style}/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,18 @@
|
|||
import './Selector.scss';
|
||||
import '../Selector.scss';
|
||||
import * as React from 'react';
|
||||
import { IContainerModel } from '../../../../Interfaces/IContainerModel';
|
||||
import { SHOW_SELECTOR_TEXT } from '../../../../utils/default';
|
||||
import { GetAbsolutePosition } from '../../../../utils/itertools';
|
||||
import { RemoveMargin } from '../../../../utils/svg';
|
||||
|
||||
interface ISelectorProps {
|
||||
containers: Map<string, IContainerModel>
|
||||
selected?: IContainerModel
|
||||
scale?: number
|
||||
text: string
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
scale: number
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
export function Selector(props: ISelectorProps): JSX.Element {
|
||||
if (props.selected === undefined || props.selected === null) {
|
||||
return (
|
||||
<rect visibility={'hidden'}>
|
||||
</rect>
|
||||
);
|
||||
}
|
||||
|
||||
const scale = (props.scale ?? 1);
|
||||
let [x, y] = GetAbsolutePosition(props.containers, props.selected);
|
||||
let [width, height] = [
|
||||
props.selected.properties.width,
|
||||
props.selected.properties.height
|
||||
];
|
||||
|
||||
({ x, y, width, height } = RemoveMargin(x, y, width, height,
|
||||
props.selected.properties.margin.left,
|
||||
props.selected.properties.margin.bottom,
|
||||
props.selected.properties.margin.top,
|
||||
props.selected.properties.margin.right
|
||||
));
|
||||
|
||||
export function Selector({ text, x, y, width, height, scale, style: overrideStyle }: ISelectorProps): JSX.Element {
|
||||
const xText = x + width / 2;
|
||||
const yText = y + height / 2;
|
||||
|
||||
|
@ -43,7 +23,8 @@ export function Selector(props: ISelectorProps): JSX.Element {
|
|||
transitionProperty: 'all',
|
||||
transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
transitionDuration: '150ms',
|
||||
animation: 'fadein 750ms ease-in alternate infinite'
|
||||
animation: 'fadein 750ms ease-in alternate infinite',
|
||||
...overrideStyle
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -65,7 +46,7 @@ export function Selector(props: ISelectorProps): JSX.Element {
|
|||
transformBox: 'fill-box'
|
||||
}}
|
||||
>
|
||||
{props.selected.properties.displayedText}
|
||||
{ text }
|
||||
</text>
|
||||
: null}
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
import '../Selector.scss';
|
||||
import * as React from 'react';
|
||||
import { type IContainerModel } from '../../../../Interfaces/IContainerModel';
|
||||
import { GetAbsolutePosition } from '../../../../utils/itertools';
|
||||
import { RemoveMargin } from '../../../../utils/svg';
|
||||
import { Selector } from '../Selector/Selector';
|
||||
|
||||
interface ISelectorContainerProps {
|
||||
containers: Map<string, IContainerModel>
|
||||
selected?: IContainerModel
|
||||
scale?: number
|
||||
}
|
||||
|
||||
export function SelectorContainer(props: ISelectorContainerProps): JSX.Element {
|
||||
if (props.selected === undefined || props.selected === null) {
|
||||
return (
|
||||
<rect visibility={'hidden'}>
|
||||
</rect>
|
||||
);
|
||||
}
|
||||
|
||||
const scale = (props.scale ?? 1);
|
||||
let [x, y] = GetAbsolutePosition(props.containers, props.selected);
|
||||
let [width, height] = [
|
||||
props.selected.properties.width,
|
||||
props.selected.properties.height
|
||||
];
|
||||
|
||||
({ x, y, width, height } = RemoveMargin(x, y, width, height,
|
||||
props.selected.properties.margin.left,
|
||||
props.selected.properties.margin.bottom,
|
||||
props.selected.properties.margin.top,
|
||||
props.selected.properties.margin.right
|
||||
));
|
||||
|
||||
return (
|
||||
<Selector
|
||||
text={props.selected.properties.displayedText}
|
||||
x={x}
|
||||
y={y}
|
||||
width={width}
|
||||
height={height}
|
||||
scale={scale}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import '../Selector.scss';
|
||||
import * as React from 'react';
|
||||
import { SYMBOL_MARGIN } from '../../../../utils/default';
|
||||
import { type ISymbolModel } from '../../../../Interfaces/ISymbolModel';
|
||||
import { Selector } from '../Selector/Selector';
|
||||
|
||||
interface ISelectorSymbolProps {
|
||||
symbols: Map<string, ISymbolModel>
|
||||
selected?: ISymbolModel
|
||||
scale?: number
|
||||
}
|
||||
|
||||
export function SelectorSymbol(props: ISelectorSymbolProps): JSX.Element {
|
||||
if (props.selected === undefined || props.selected === null) {
|
||||
return (
|
||||
<rect visibility={'hidden'}>
|
||||
</rect>
|
||||
);
|
||||
}
|
||||
|
||||
const scale = (props.scale ?? 1);
|
||||
const [width, height] = [
|
||||
props.selected.width / scale,
|
||||
props.selected.height / scale
|
||||
];
|
||||
|
||||
const [x, y] = [
|
||||
props.selected.x + props.selected.width / 2,
|
||||
-SYMBOL_MARGIN - height];
|
||||
|
||||
const style: React.CSSProperties = {
|
||||
transform: 'translateX(-50%)',
|
||||
transformBox: 'fill-box'
|
||||
};
|
||||
|
||||
return (
|
||||
<Selector
|
||||
text={props.selected.displayedText}
|
||||
x={x}
|
||||
y={y}
|
||||
width={width}
|
||||
height={height}
|
||||
scale={scale}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
import * as React from 'react';
|
||||
import { ReactSVGPanZoom, Tool, TOOL_PAN, Value } from 'react-svg-pan-zoom';
|
||||
import { ReactSVGPanZoom, type Tool, TOOL_PAN, type Value } from 'react-svg-pan-zoom';
|
||||
import { Container } from './Elements/Container';
|
||||
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||
import { Selector } from './Elements/Selector/Selector';
|
||||
import { DepthDimensionLayer } from './Elements/DepthDimensionLayer';
|
||||
import { MAX_FRAMERATE, SHOW_DIMENSIONS_PER_DEPTH } from '../../utils/default';
|
||||
import { SelectorContainer } from './Elements/SelectorContainer/SelectorContainer';
|
||||
import { MAX_FRAMERATE } from '../../utils/default';
|
||||
import { SymbolLayer } from './Elements/SymbolLayer';
|
||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
||||
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
|
||||
import { DimensionLayer } from './Elements/DimensionLayer';
|
||||
import { SelectorSymbol } from './Elements/SelectorSymbol/SelectorSymbol';
|
||||
|
||||
interface ISVGProps {
|
||||
className?: string
|
||||
|
@ -17,11 +17,19 @@ interface ISVGProps {
|
|||
height: number
|
||||
containers: Map<string, IContainerModel>
|
||||
children: IContainerModel
|
||||
selected?: IContainerModel
|
||||
selectedContainer?: IContainerModel
|
||||
symbols: Map<string, ISymbolModel>
|
||||
selectedSymbol?: ISymbolModel
|
||||
selectorMode: SelectorMode
|
||||
selectContainer: (containerId: string) => void
|
||||
}
|
||||
|
||||
export enum SelectorMode {
|
||||
Nothing,
|
||||
Containers,
|
||||
Symbols
|
||||
}
|
||||
|
||||
export const ID = 'svg';
|
||||
|
||||
export function SVG(props: ISVGProps): JSX.Element {
|
||||
|
@ -49,8 +57,7 @@ export function SVG(props: ISVGProps): JSX.Element {
|
|||
xmlns
|
||||
};
|
||||
|
||||
let children: React.ReactNode | React.ReactNode[] = [];
|
||||
children = <Container
|
||||
const children: React.ReactNode | React.ReactNode[] = <Container
|
||||
key={`container-${props.children.properties.id}`}
|
||||
containers={props.containers}
|
||||
model={props.children}
|
||||
|
@ -59,6 +66,25 @@ export function SVG(props: ISVGProps): JSX.Element {
|
|||
selectContainer={props.selectContainer}
|
||||
/>;
|
||||
|
||||
function Selector(): JSX.Element {
|
||||
switch (props.selectorMode) {
|
||||
case SelectorMode.Containers:
|
||||
return <SelectorContainer
|
||||
containers={props.containers}
|
||||
scale={scale}
|
||||
selected={props.selectedContainer}
|
||||
/>;
|
||||
case SelectorMode.Symbols:
|
||||
return <SelectorSymbol
|
||||
symbols={props.symbols}
|
||||
scale={scale}
|
||||
selected={props.selectedSymbol}
|
||||
/>;
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div id={ID} className={props.className}>
|
||||
<ReactSVGPanZoom
|
||||
|
@ -94,12 +120,9 @@ export function SVG(props: ISVGProps): JSX.Element {
|
|||
>
|
||||
<svg {...properties}>
|
||||
{children}
|
||||
{SHOW_DIMENSIONS_PER_DEPTH
|
||||
? <DepthDimensionLayer containers={props.containers} scale={scale} roots={props.children} />
|
||||
: null}
|
||||
<DimensionLayer containers={props.containers} scale={scale} root={props.children} />
|
||||
<DimensionLayer containers={props.containers} symbols={props.symbols} scale={scale} root={props.children} />
|
||||
<SymbolLayer scale={scale} symbols={props.symbols} />
|
||||
<Selector containers={props.containers} scale={scale} selected={props.selected} /> {/* leave this at the end so it can be removed during the svg export */}
|
||||
<Selector />
|
||||
</svg>
|
||||
</ReactSVGPanZoom>
|
||||
</div>
|
||||
|
|
4
src/Components/Sidebar/ToggleSideBar/ToggleSideBar.scss
Normal file
4
src/Components/Sidebar/ToggleSideBar/ToggleSideBar.scss
Normal file
|
@ -0,0 +1,4 @@
|
|||
.text-vertical{
|
||||
text-align: right;
|
||||
writing-mode: vertical-rl;
|
||||
}
|
23
src/Components/Sidebar/ToggleSideBar/ToggleSideBar.tsx
Normal file
23
src/Components/Sidebar/ToggleSideBar/ToggleSideBar.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import * as React from 'react';
|
||||
import './ToggleSideBar.scss';
|
||||
|
||||
interface IToggleSidebarProps {
|
||||
title: string
|
||||
checked: boolean
|
||||
onChange: (newValue: boolean) => void
|
||||
}
|
||||
|
||||
export function ToggleSideBar({ title, checked, onChange }: IToggleSidebarProps): JSX.Element {
|
||||
return (
|
||||
<div className={`${(checked ? 'bg-slate-400 hover:bg-slate-500' : 'bg-slate-300 hover:bg-slate-400')}`}>
|
||||
<button
|
||||
className={'w-full py-2'}
|
||||
type='button'
|
||||
onClick={() => onChange(!checked)}
|
||||
>
|
||||
<p className='text-vertical'>{title}
|
||||
</p>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -4,6 +4,8 @@ import { RestoreX, TransformX } from '../../utils/svg';
|
|||
import { InputGroup } from '../InputGroup/InputGroup';
|
||||
import { TextInputGroup } from '../InputGroup/TextInputGroup';
|
||||
import { Text } from '../Text/Text';
|
||||
import { PropertyType } from '../../Enums/PropertyType';
|
||||
import { ToggleButton } from '../ToggleButton/ToggleButton';
|
||||
|
||||
interface ISymbolFormProps {
|
||||
symbol: ISymbolModel
|
||||
|
@ -60,6 +62,13 @@ export function SymbolForm(props: ISymbolFormProps): JSX.Element {
|
|||
min={0}
|
||||
value={props.symbol.width.toString()}
|
||||
onChange={(value) => props.onChange('width', Number(value))} />
|
||||
<ToggleButton
|
||||
labelText={Text({ textId: '@ShowDimension' })}
|
||||
inputKey='showDimension'
|
||||
labelClassName=''
|
||||
inputClassName=''
|
||||
checked={props.symbol.showDimension}
|
||||
onChange={(e) => props.onChange('showDimension', e.target.checked)}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ export function SymbolProperties(props: ISymbolPropertiesProps): JSX.Element {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='h-full p-3 bg-slate-200 overflow-y-auto'>
|
||||
<div className='h-full p-3 bg-slate-200 overflow-y-auto '>
|
||||
<SymbolForm
|
||||
symbol={props.symbol}
|
||||
symbols={props.symbols}
|
||||
|
|
|
@ -1,28 +1,62 @@
|
|||
import * as React from 'react';
|
||||
import useSize from '@react-hook/size';
|
||||
import { FixedSizeList as List } from 'react-window';
|
||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
||||
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
|
||||
import { SymbolProperties } from '../SymbolProperties/SymbolProperties';
|
||||
import { ToggleSideBar } from '../Sidebar/ToggleSideBar/ToggleSideBar';
|
||||
import { Text } from '../Text/Text';
|
||||
import { useState } from 'react';
|
||||
|
||||
interface ISymbolsSidebarProps {
|
||||
selectedSymbolId: string
|
||||
symbols: Map<string, ISymbolModel>
|
||||
onPropertyChange: (key: string, value: string | number | boolean) => void
|
||||
selectSymbol: (symbolId: string) => void
|
||||
isExpanded: boolean
|
||||
onExpandChange: (isExpanded: boolean) => void
|
||||
}
|
||||
|
||||
export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
|
||||
// States
|
||||
const divRef = React.useRef<HTMLDivElement>(null);
|
||||
const height = useSize(divRef)[1];
|
||||
const [showProperties, setShowProperties] = useState(props.isExpanded);
|
||||
// Render
|
||||
const symbols = [...props.symbols.values()];
|
||||
|
||||
const selectedSymbol = props.symbols.get(props.selectedSymbolId);
|
||||
return (
|
||||
<div className='flex flex-row h-full w-full'>
|
||||
{showProperties && <div className='flex flex-1 flex-col w-64 border-r-2 border-slate-400'>
|
||||
{(selectedSymbol == null) && <h1 className={'p-4'}>{Text({ textId: '@NoSymbolSelected' })}</h1>}
|
||||
<SymbolProperties
|
||||
symbol={selectedSymbol}
|
||||
symbols={props.symbols}
|
||||
onChange={props.onPropertyChange}
|
||||
/>
|
||||
</div>}
|
||||
<div className={'flex w-64'} ref={divRef}>
|
||||
<div className='w-6'>
|
||||
<ToggleSideBar title={Text({ textId: '@Properties' })} checked={showProperties} onChange={(newValue) => { setShowProperties(newValue); props.onExpandChange(newValue); }} />
|
||||
</div>
|
||||
<List
|
||||
itemCount={symbols.length}
|
||||
itemSize={35}
|
||||
height={height}
|
||||
width={'100%'}
|
||||
>
|
||||
{Row}
|
||||
</List>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
function Row({ index, style }: { index: number, style: React.CSSProperties }): JSX.Element {
|
||||
const symbol = symbols[index];
|
||||
const key = symbol.id;
|
||||
const text = symbol.displayedText;
|
||||
const selectedClass: string = props.selectedSymbolId !== '' &&
|
||||
props.selectedSymbolId === symbol.id
|
||||
props.selectedSymbolId === symbol.id
|
||||
? 'border-l-4 bg-slate-400/60 hover:bg-slate-400'
|
||||
: 'bg-slate-300/60 hover:bg-slate-300';
|
||||
|
||||
|
@ -33,33 +67,10 @@ export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
|
|||
id={key}
|
||||
key={key}
|
||||
style={style}
|
||||
onClick={() => props.selectSymbol(key)}
|
||||
onClick={() => { props.selectSymbol(key); }}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='h-full'>
|
||||
<div ref={divRef} className='h-1/2 text-gray-800'>
|
||||
<List
|
||||
className='List divide-y divide-black'
|
||||
itemCount={symbols.length}
|
||||
itemSize={35}
|
||||
height={height}
|
||||
width={'100%'}
|
||||
>
|
||||
{Row}
|
||||
</List>
|
||||
</div>
|
||||
<div>
|
||||
<SymbolProperties
|
||||
symbol={props.symbols.get(props.selectedSymbolId)}
|
||||
symbols={props.symbols}
|
||||
onChange={props.onPropertyChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,20 +1,20 @@
|
|||
import * as React from 'react';
|
||||
import { ElementsList } from '../ElementsList/ElementsList';
|
||||
import { ElementsSideBar } from '../ElementsList/ElementsSideBar';
|
||||
import { History } from '../History/History';
|
||||
import { Bar } from '../Bar/Bar';
|
||||
import { Bar, BAR_WIDTH } from '../Bar/Bar';
|
||||
import { Symbols } from '../Symbols/Symbols';
|
||||
import { SymbolsSidebar } from '../SymbolsList/SymbolsList';
|
||||
import { PropertyType } from '../../Enums/PropertyType';
|
||||
import { SymbolsSidebar } from '../SymbolsList/SymbolsSidebar';
|
||||
import { type PropertyType } from '../../Enums/PropertyType';
|
||||
import { Messages } from '../Messages/Messages';
|
||||
import { Sidebar } from '../Sidebar/Sidebar';
|
||||
import { Components } from '../Components/Components';
|
||||
import { Viewer } from '../Viewer/Viewer';
|
||||
import { Settings } from '../Settings/Settings';
|
||||
import { IMessage } from '../../Interfaces/IMessage';
|
||||
import { type IMessage } from '../../Interfaces/IMessage';
|
||||
import { DISABLE_API } from '../../utils/default';
|
||||
import { UseWorker, UseAsync } from './UseWorker';
|
||||
import { FindContainerById } from '../../utils/itertools';
|
||||
import { IEditorState } from '../../Interfaces/IEditorState';
|
||||
import { type IEditorState } from '../../Interfaces/IEditorState';
|
||||
import { GetCurrentHistoryState } from '../Editor/Editor';
|
||||
import { Text } from '../Text/Text';
|
||||
import { IReplaceContainer } from '../../Interfaces/IReplaceContainer';
|
||||
|
@ -42,7 +42,9 @@ export interface IUIProps {
|
|||
export enum SidebarType {
|
||||
None,
|
||||
Components,
|
||||
ComponentsExpanded,
|
||||
Symbols,
|
||||
SymbolsExpanded,
|
||||
History,
|
||||
Messages,
|
||||
Settings
|
||||
|
@ -84,7 +86,7 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
|
|||
);
|
||||
}
|
||||
|
||||
// Please use setOrToggleSidebar rather than setSelectedSidebar so we can close the sidebar
|
||||
// Please use setOrToggleSidebar rather than setSelectedSidebar, so we can close the sidebar
|
||||
const setOrToggleSidebar = UseSetOrToggleSidebar(selectedSidebar, setSelectedSidebar);
|
||||
|
||||
let leftSidebarTitle = '';
|
||||
|
@ -99,6 +101,7 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
|
|||
}
|
||||
|
||||
const selectedContainer = FindContainerById(current.containers, current.selectedContainerId);
|
||||
const selectedSymbol = current.symbols.get(current.selectedSymbolId);
|
||||
|
||||
switch (selectedSidebar) {
|
||||
case SidebarType.Components:
|
||||
|
@ -112,7 +115,7 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
|
|||
replaceContainer={replaceContainer}
|
||||
setReplaceContainer={setReplaceContainer}/>;
|
||||
rightSidebarTitle = Text({ textId: '@Elements' });
|
||||
rightChildren = <ElementsList
|
||||
rightChildren = <ElementsSideBar
|
||||
containers={current.containers}
|
||||
mainContainer={mainContainer}
|
||||
symbols={current.symbols}
|
||||
|
@ -120,9 +123,32 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
|
|||
onPropertyChange={methods.onPropertyChange}
|
||||
selectContainer={methods.selectContainer}
|
||||
addContainer={methods.addContainerAt}
|
||||
isExpanded ={false}
|
||||
onExpandChange={() => { setOrToggleSidebar(SidebarType.ComponentsExpanded); } }
|
||||
/>;
|
||||
break;
|
||||
case SidebarType.ComponentsExpanded:
|
||||
leftSidebarTitle = Text({ textId: '@Components' });
|
||||
leftChildren = <Components
|
||||
selectedContainer={selectedContainer}
|
||||
componentOptions={configuration.AvailableContainers}
|
||||
categories={configuration.Categories}
|
||||
buttonOnClick={methods.addOrReplaceContainer}
|
||||
replaceContainer={replaceContainer}
|
||||
setReplaceContainer={setReplaceContainer}/>;
|
||||
rightSidebarTitle = Text({ textId: '@Elements' });
|
||||
rightChildren = <ElementsSideBar
|
||||
containers={current.containers}
|
||||
mainContainer={mainContainer}
|
||||
symbols={current.symbols}
|
||||
selectedContainer={selectedContainer}
|
||||
onPropertyChange={methods.onPropertyChange}
|
||||
selectContainer={methods.selectContainer}
|
||||
addContainer={methods.addContainerAt}
|
||||
isExpanded ={true}
|
||||
onExpandChange={() => { setOrToggleSidebar(SidebarType.Components); } }
|
||||
/>;
|
||||
break;
|
||||
|
||||
case SidebarType.Symbols:
|
||||
leftSidebarTitle = Text({ textId: '@SymbolsLeft' });
|
||||
leftChildren = <Symbols
|
||||
|
@ -135,6 +161,24 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
|
|||
symbols={current.symbols}
|
||||
onPropertyChange={methods.onSymbolPropertyChange}
|
||||
selectSymbol={methods.selectSymbol}
|
||||
isExpanded ={false}
|
||||
onExpandChange={() => { setOrToggleSidebar(SidebarType.SymbolsExpanded); } }
|
||||
/>;
|
||||
break;
|
||||
case SidebarType.SymbolsExpanded:
|
||||
leftSidebarTitle = Text({ textId: '@SymbolsLeft' });
|
||||
leftChildren = <Symbols
|
||||
componentOptions={configuration.AvailableSymbols}
|
||||
buttonOnClick={methods.addSymbol}
|
||||
/>;
|
||||
rightSidebarTitle = Text({ textId: '@SymbolsRight' });
|
||||
rightChildren = <SymbolsSidebar
|
||||
selectedSymbolId={current.selectedSymbolId}
|
||||
symbols={current.symbols}
|
||||
onPropertyChange={methods.onSymbolPropertyChange}
|
||||
selectSymbol={methods.selectSymbol}
|
||||
isExpanded ={true}
|
||||
onExpandChange={() => { setOrToggleSidebar(SidebarType.Symbols); }}
|
||||
/>;
|
||||
break;
|
||||
|
||||
|
@ -152,7 +196,7 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
|
|||
leftChildren = <Messages
|
||||
historyState={current}
|
||||
messages={messages}
|
||||
clearMessage={() => setMessages([])}
|
||||
clearMessage={() => { setMessages([]); }}
|
||||
/>;
|
||||
break;
|
||||
|
||||
|
@ -167,6 +211,7 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
|
|||
|
||||
const isLeftSidebarOpen = selectedSidebar !== SidebarType.None;
|
||||
const isRightSidebarOpen = selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.Symbols;
|
||||
const isRightSidebarOpenExpanded = selectedSidebar === SidebarType.ComponentsExpanded || selectedSidebar === SidebarType.SymbolsExpanded;
|
||||
|
||||
const isLeftSidebarOpenClasses = new Set<string>([
|
||||
'left-sidebar',
|
||||
|
@ -178,35 +223,60 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
|
|||
|
||||
let isRightSidebarOpenClasses = 'right-0 -bottom-full md:-right-80 md:bottom-0';
|
||||
|
||||
let marginSidebar = BAR_WIDTH;
|
||||
const viewerMarginClasses = new Set<string>([
|
||||
'ml-16'
|
||||
]);
|
||||
|
||||
if (isLeftSidebarOpen) {
|
||||
isLeftSidebarOpenClasses.delete('-bottom-full');
|
||||
isLeftSidebarOpenClasses.delete('md:-left-64');
|
||||
isLeftSidebarOpenClasses.delete('md:bottom-0');
|
||||
marginSidebar += 256;
|
||||
viewerMarginClasses.add(' md:ml-80');
|
||||
}
|
||||
|
||||
if (isRightSidebarOpen) {
|
||||
if (isRightSidebarOpen || isRightSidebarOpenExpanded) {
|
||||
isRightSidebarOpenClasses = 'right-0';
|
||||
|
||||
if (isRightSidebarOpenExpanded) {
|
||||
viewerMarginClasses.add(' md:mr-[32rem]');
|
||||
marginSidebar += 512;
|
||||
} else {
|
||||
viewerMarginClasses.add(' md:mr-64');
|
||||
marginSidebar += 256;
|
||||
}
|
||||
} else {
|
||||
isLeftSidebarOpenClasses.delete('left-sidebar');
|
||||
isLeftSidebarOpenClasses.add('left-sidebar-single');
|
||||
}
|
||||
|
||||
const clickRestrictionsClasses = replaceContainer.isReplacing ? 'pointer-events-none opacity-50' : '';
|
||||
const isComponentsOpen = selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.ComponentsExpanded;
|
||||
const isSymbolsOpen = selectedSidebar === SidebarType.Symbols || selectedSidebar === SidebarType.SymbolsExpanded;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Bar
|
||||
className={clickRestrictionsClasses}
|
||||
isComponentsOpen={selectedSidebar === SidebarType.Components}
|
||||
isSymbolsOpen={selectedSidebar === SidebarType.Symbols}
|
||||
isComponentsOpen={isComponentsOpen}
|
||||
isSymbolsOpen={isSymbolsOpen}
|
||||
isHistoryOpen={selectedSidebar === SidebarType.History}
|
||||
isMessagesOpen={selectedSidebar === SidebarType.Messages}
|
||||
isSettingsOpen={selectedSidebar === SidebarType.Settings}
|
||||
toggleComponents={() => {
|
||||
setOrToggleSidebar(SidebarType.Components);
|
||||
if (selectedSidebar === SidebarType.ComponentsExpanded) {
|
||||
setOrToggleSidebar(SidebarType.ComponentsExpanded);
|
||||
} else {
|
||||
setOrToggleSidebar(SidebarType.Components);
|
||||
}
|
||||
} }
|
||||
toggleSymbols={() => {
|
||||
setOrToggleSidebar(SidebarType.Symbols);
|
||||
if (selectedSidebar === SidebarType.SymbolsExpanded) {
|
||||
setOrToggleSidebar(SidebarType.SymbolsExpanded);
|
||||
} else {
|
||||
setOrToggleSidebar(SidebarType.Symbols);
|
||||
}
|
||||
} }
|
||||
toggleTimeline={() => {
|
||||
setOrToggleSidebar(SidebarType.History);
|
||||
|
@ -225,12 +295,14 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
|
|||
{ leftChildren }
|
||||
</Sidebar>
|
||||
<Viewer
|
||||
className={clickRestrictionsClasses}
|
||||
isLeftSidebarOpen={isLeftSidebarOpen}
|
||||
isRightSidebarOpen={isRightSidebarOpen}
|
||||
className={`${clickRestrictionsClasses} ${[...viewerMarginClasses.values()].join(' ')} w-full h-full`}
|
||||
current={current}
|
||||
isComponentsOpen={isComponentsOpen}
|
||||
isSymbolsOpen={isSymbolsOpen}
|
||||
selectedContainer={selectedContainer}
|
||||
selectContainer={methods.selectContainer}
|
||||
selectedSymbol={selectedSymbol}
|
||||
margin={marginSidebar}
|
||||
/>
|
||||
<Sidebar
|
||||
className={`right-sidebar ${isRightSidebarOpenClasses} ${clickRestrictionsClasses}`}
|
||||
|
|
|
@ -1,58 +1,54 @@
|
|||
import * as React from 'react';
|
||||
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||
import { IPoint } from '../../Interfaces/IPoint';
|
||||
import { type IContainerModel } from '../../Interfaces/IContainerModel';
|
||||
import { type IHistoryState } from '../../Interfaces/IHistoryState';
|
||||
import { type IPoint } from '../../Interfaces/IPoint';
|
||||
import { DIMENSION_MARGIN, USE_EXPERIMENTAL_CANVAS_API } from '../../utils/default';
|
||||
import { FindContainerById, MakeRecursionDFSIterator } from '../../utils/itertools';
|
||||
import { BAR_WIDTH } from '../Bar/Bar';
|
||||
import { Canvas } from '../Canvas/Canvas';
|
||||
import { AddDimensions } from '../Canvas/DimensionLayer';
|
||||
import { RenderSelector } from '../Canvas/Selector';
|
||||
import { SVG } from '../SVG/SVG';
|
||||
import { SelectorMode, SVG } from '../SVG/SVG';
|
||||
import { RenderSymbol } from '../Canvas/Symbol';
|
||||
import { useState } from 'react';
|
||||
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
|
||||
|
||||
interface IViewerProps {
|
||||
className: string
|
||||
isLeftSidebarOpen: boolean
|
||||
isRightSidebarOpen: boolean
|
||||
current: IHistoryState
|
||||
selectedContainer: IContainerModel | undefined
|
||||
selectContainer: (containerId: string) => void
|
||||
selectedSymbol: ISymbolModel | undefined
|
||||
margin: number
|
||||
isComponentsOpen: boolean
|
||||
isSymbolsOpen: boolean
|
||||
}
|
||||
|
||||
interface IViewer {
|
||||
viewerWidth: number
|
||||
viewerHeight: number
|
||||
}
|
||||
|
||||
function OnResize(
|
||||
isLeftSidebarOpen: boolean,
|
||||
isRightSidebarOpen: boolean,
|
||||
setViewer: React.Dispatch<React.SetStateAction<IViewer>>
|
||||
): void {
|
||||
let marginSidebar = BAR_WIDTH;
|
||||
if (isLeftSidebarOpen) {
|
||||
marginSidebar += 256;
|
||||
}
|
||||
if (isRightSidebarOpen) {
|
||||
marginSidebar += 256;
|
||||
export function Viewer({
|
||||
className,
|
||||
current,
|
||||
selectedContainer,
|
||||
selectContainer,
|
||||
selectedSymbol,
|
||||
margin,
|
||||
isComponentsOpen,
|
||||
isSymbolsOpen
|
||||
}: IViewerProps): JSX.Element {
|
||||
function computeWidth(margin: number): number {
|
||||
return window.innerWidth - (window.innerWidth < 768 ? BAR_WIDTH : margin);
|
||||
}
|
||||
|
||||
const margin = window.innerWidth < 768 ? BAR_WIDTH : marginSidebar;
|
||||
setViewer({
|
||||
viewerWidth: window.innerWidth - margin,
|
||||
viewerHeight: window.innerHeight
|
||||
});
|
||||
}
|
||||
const [windowSize, setWindowSize] = useState([
|
||||
computeWidth(margin),
|
||||
window.innerHeight
|
||||
]);
|
||||
|
||||
function UseSVGAutoResizerOnWindowResize(
|
||||
isLeftSidebarOpen: boolean,
|
||||
isRightSidebarOpen: boolean,
|
||||
setViewer: React.Dispatch<React.SetStateAction<IViewer>>
|
||||
): void {
|
||||
React.useEffect(() => {
|
||||
function SVGAutoResizer(): void {
|
||||
OnResize(isLeftSidebarOpen, isRightSidebarOpen, setViewer);
|
||||
setWindowSize([
|
||||
computeWidth(margin),
|
||||
window.innerHeight
|
||||
]);
|
||||
}
|
||||
|
||||
window.addEventListener('resize', SVGAutoResizer);
|
||||
|
@ -61,42 +57,13 @@ function UseSVGAutoResizerOnWindowResize(
|
|||
window.removeEventListener('resize', SVGAutoResizer);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function UseSVGAutoResizerOnSidebar(
|
||||
isLeftSidebarOpen: boolean,
|
||||
isRightSidebarOpen: boolean,
|
||||
setViewer: React.Dispatch<React.SetStateAction<IViewer>>
|
||||
): void {
|
||||
React.useEffect(() => {
|
||||
OnResize(isLeftSidebarOpen, isRightSidebarOpen, setViewer);
|
||||
}, [isLeftSidebarOpen, isRightSidebarOpen, setViewer]);
|
||||
}
|
||||
|
||||
export function Viewer({
|
||||
className,
|
||||
isLeftSidebarOpen, isRightSidebarOpen,
|
||||
current,
|
||||
selectedContainer,
|
||||
selectContainer
|
||||
}: IViewerProps): JSX.Element {
|
||||
let marginClasses = 'ml-16';
|
||||
let marginSidebar = BAR_WIDTH;
|
||||
if (isLeftSidebarOpen) {
|
||||
marginClasses += ' md:ml-80';
|
||||
marginSidebar += 256;
|
||||
}
|
||||
|
||||
if (isRightSidebarOpen) {
|
||||
marginClasses += ' md:mr-64';
|
||||
marginSidebar += 256;
|
||||
}
|
||||
|
||||
const margin = window.innerWidth < 768 ? BAR_WIDTH : marginSidebar;
|
||||
const [viewer, setViewer] = React.useState<IViewer>({
|
||||
viewerWidth: window.innerWidth - margin,
|
||||
viewerHeight: window.innerHeight
|
||||
});
|
||||
setWindowSize([
|
||||
computeWidth(margin),
|
||||
window.innerHeight
|
||||
]);
|
||||
}, [margin]);
|
||||
|
||||
const mainContainer = FindContainerById(current.containers, current.mainContainer);
|
||||
|
||||
|
@ -104,8 +71,12 @@ export function Viewer({
|
|||
return <></>;
|
||||
}
|
||||
|
||||
UseSVGAutoResizerOnWindowResize(isLeftSidebarOpen, isRightSidebarOpen, setViewer);
|
||||
UseSVGAutoResizerOnSidebar(isLeftSidebarOpen, isRightSidebarOpen, setViewer);
|
||||
let selectorMode = SelectorMode.Nothing;
|
||||
if (isComponentsOpen) {
|
||||
selectorMode = SelectorMode.Containers;
|
||||
} else if (isSymbolsOpen) {
|
||||
selectorMode = SelectorMode.Symbols;
|
||||
}
|
||||
|
||||
if (USE_EXPERIMENTAL_CANVAS_API) {
|
||||
function Draw(ctx: CanvasRenderingContext2D, frameCount: number, scale: number, translatePos: IPoint): void {
|
||||
|
@ -171,14 +142,16 @@ export function Viewer({
|
|||
|
||||
return (
|
||||
<SVG
|
||||
className={`${marginClasses} ${className}`}
|
||||
viewerWidth={viewer.viewerWidth}
|
||||
viewerHeight={viewer.viewerHeight}
|
||||
className={className}
|
||||
viewerWidth={windowSize[0]}
|
||||
viewerHeight={windowSize[1]}
|
||||
width={mainContainer.properties.width}
|
||||
height={mainContainer.properties.height}
|
||||
containers={current.containers}
|
||||
selected={selectedContainer}
|
||||
selectedContainer={selectedContainer}
|
||||
symbols={current.symbols}
|
||||
selectedSymbol={selectedSymbol}
|
||||
selectorMode={selectorMode}
|
||||
selectContainer={selectContainer}
|
||||
>
|
||||
{mainContainer}
|
||||
|
@ -208,10 +181,11 @@ function RenderDimensions(
|
|||
currentTransform: [number, number]
|
||||
): void {
|
||||
ctx.save();
|
||||
const containerLeftDim = leftDim - (DIMENSION_MARGIN * (depth + 1)) / scale;
|
||||
const containerTopDim = topDim - (DIMENSION_MARGIN * (depth + 1)) / scale;
|
||||
const containerBottomDim = bottomDim + (DIMENSION_MARGIN * (depth + 1)) / scale;
|
||||
const containerRightDim = rightDim + (DIMENSION_MARGIN * (depth + 1)) / scale;
|
||||
const depthOffset = (DIMENSION_MARGIN * (depth + 1)) / scale;
|
||||
const containerLeftDim = leftDim - depthOffset;
|
||||
const containerTopDim = topDim - depthOffset;
|
||||
const containerBottomDim = bottomDim + depthOffset;
|
||||
const containerRightDim = rightDim + depthOffset;
|
||||
const dimMapped = [containerLeftDim, containerBottomDim, containerTopDim, containerRightDim];
|
||||
AddDimensions(ctx, containers, container, dimMapped, currentTransform, scale, depth);
|
||||
ctx.restore();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue