Merged PR 18: Add support for custom SVG
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
Add support for custom SVG Add userData back into IProperties Added library interweave Update example
This commit is contained in:
parent
82eae4971e
commit
e96e4f123b
8 changed files with 109 additions and 9 deletions
|
@ -31,6 +31,7 @@ module.exports = {
|
||||||
'@typescript-eslint/semi': ['warn', 'always'],
|
'@typescript-eslint/semi': ['warn', 'always'],
|
||||||
'no-unused-vars': 'off',
|
'no-unused-vars': 'off',
|
||||||
'@typescript-eslint/no-unused-vars': 'error',
|
'@typescript-eslint/no-unused-vars': 'error',
|
||||||
|
'@typescript-eslint/ban-types': ['error'],
|
||||||
'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks
|
'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks
|
||||||
'react-hooks/exhaustive-deps': 'warn' // Checks effect dependencies
|
'react-hooks/exhaustive-deps': 'warn' // Checks effect dependencies
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@heroicons/react": "^1.0.6",
|
"@heroicons/react": "^1.0.6",
|
||||||
|
"interweave": "^13.0.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-svg-pan-zoom": "^3.11.0",
|
"react-svg-pan-zoom": "^3.11.0",
|
||||||
|
|
15
pnpm-lock.yaml
generated
15
pnpm-lock.yaml
generated
|
@ -24,6 +24,7 @@ specifiers:
|
||||||
eslint-plugin-promise: ^6.0.0
|
eslint-plugin-promise: ^6.0.0
|
||||||
eslint-plugin-react: ^7.30.1
|
eslint-plugin-react: ^7.30.1
|
||||||
eslint-plugin-react-hooks: ^4.6.0
|
eslint-plugin-react-hooks: ^4.6.0
|
||||||
|
interweave: ^13.0.0
|
||||||
jsdom: ^20.0.0
|
jsdom: ^20.0.0
|
||||||
postcss: ^8.4.14
|
postcss: ^8.4.14
|
||||||
react: ^18.2.0
|
react: ^18.2.0
|
||||||
|
@ -38,6 +39,7 @@ specifiers:
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@heroicons/react': 1.0.6_react@18.2.0
|
'@heroicons/react': 1.0.6_react@18.2.0
|
||||||
|
interweave: 13.0.0_react@18.2.0
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0_react@18.2.0
|
react-dom: 18.2.0_react@18.2.0
|
||||||
react-svg-pan-zoom: 3.11.0_react@18.2.0
|
react-svg-pan-zoom: 3.11.0_react@18.2.0
|
||||||
|
@ -1522,6 +1524,10 @@ packages:
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/escape-html/1.0.3:
|
||||||
|
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/escape-string-regexp/1.0.5:
|
/escape-string-regexp/1.0.5:
|
||||||
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
|
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
|
||||||
engines: {node: '>=0.8.0'}
|
engines: {node: '>=0.8.0'}
|
||||||
|
@ -2177,6 +2183,15 @@ packages:
|
||||||
side-channel: 1.0.4
|
side-channel: 1.0.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/interweave/13.0.0_react@18.2.0:
|
||||||
|
resolution: {integrity: sha512-Mckwj+ix/VtrZu1bRBIIohwrsXj12ZTvJCoYUMZlJmgtvIaQCj0i77eSZ63ckbA1TsPrz2VOvLW9/kTgm5d+mw==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
|
dependencies:
|
||||||
|
escape-html: 1.0.3
|
||||||
|
react: 18.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/is-bigint/1.0.4:
|
/is-bigint/1.0.4:
|
||||||
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
|
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
|
@ -218,7 +218,9 @@ export function AddContainer(
|
||||||
isRigidBody: false,
|
isRigidBody: false,
|
||||||
isAnchor: false,
|
isAnchor: false,
|
||||||
XPositionReference: containerConfig.XPositionReference ?? XPositionReference.Left,
|
XPositionReference: containerConfig.XPositionReference ?? XPositionReference.Left,
|
||||||
style: containerConfig.Style
|
customSVG: containerConfig.CustomSVG,
|
||||||
|
style: containerConfig.Style,
|
||||||
|
userData: containerConfig.UserData
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create the container
|
// Create the container
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { Interweave, Node } from 'interweave';
|
||||||
import { XPositionReference } from '../../../Enums/XPositionReference';
|
import { XPositionReference } from '../../../Enums/XPositionReference';
|
||||||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { DIMENSION_MARGIN } from '../../../utils/default';
|
import { DIMENSION_MARGIN } from '../../../utils/default';
|
||||||
import { getDepth } from '../../../utils/itertools';
|
import { getDepth } from '../../../utils/itertools';
|
||||||
import { Dimension } from './Dimension';
|
import { Dimension } from './Dimension';
|
||||||
|
import IProperties from '../../../Interfaces/IProperties';
|
||||||
|
|
||||||
interface IContainerProps {
|
interface IContainerProps {
|
||||||
model: IContainerModel
|
model: IContainerModel
|
||||||
|
@ -33,6 +35,14 @@ export const Container: React.FC<IContainerProps> = (props: IContainerProps) =>
|
||||||
props.model.properties.style
|
props.model.properties.style
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const svg = (props.model.properties.customSVG != null)
|
||||||
|
? CreateReactCustomSVG(props.model.properties.customSVG, props.model.properties)
|
||||||
|
: (<rect
|
||||||
|
width={props.model.properties.width}
|
||||||
|
height={props.model.properties.height}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
|
</rect>);
|
||||||
// Dimension props
|
// Dimension props
|
||||||
const depth = getDepth(props.model);
|
const depth = getDepth(props.model);
|
||||||
const dimensionMargin = DIMENSION_MARGIN * (depth + 1);
|
const dimensionMargin = DIMENSION_MARGIN * (depth + 1);
|
||||||
|
@ -79,12 +89,7 @@ export const Container: React.FC<IContainerProps> = (props: IContainerProps) =>
|
||||||
text={text}
|
text={text}
|
||||||
/>
|
/>
|
||||||
{ dimensionChildren }
|
{ dimensionChildren }
|
||||||
<rect
|
{ svg }
|
||||||
width={props.model.properties.width}
|
|
||||||
height={props.model.properties.height}
|
|
||||||
style={style}
|
|
||||||
>
|
|
||||||
</rect>
|
|
||||||
<text
|
<text
|
||||||
x={xText}
|
x={xText}
|
||||||
y={yText}
|
y={yText}
|
||||||
|
@ -141,3 +146,63 @@ export function restoreX(x: number, width: number, xPositionReference = XPositio
|
||||||
}
|
}
|
||||||
return transformedX;
|
return transformedX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function CreateReactCustomSVG(customSVG: string, props: IProperties): React.ReactNode {
|
||||||
|
return <Interweave
|
||||||
|
tagName='g'
|
||||||
|
disableLineBreaks={true}
|
||||||
|
content={customSVG}
|
||||||
|
allowElements={true}
|
||||||
|
transform={(node, children) => transform(node, children, props)}
|
||||||
|
/>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function transform(node: HTMLElement, children: Node[], props: IProperties): React.ReactNode {
|
||||||
|
const supportedTags = ['line', 'path', 'rect'];
|
||||||
|
if (supportedTags.includes(node.tagName.toLowerCase())) {
|
||||||
|
const attributes: {[att: string]: string | object | null} = {};
|
||||||
|
node.getAttributeNames().forEach(attName => {
|
||||||
|
const attributeValue = node.getAttribute(attName);
|
||||||
|
if (attributeValue === null) {
|
||||||
|
attributes[attName] = attributeValue;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributeValue.startsWith('{userData.') && attributeValue.endsWith('}')) {
|
||||||
|
// support for userData
|
||||||
|
if (props.userData === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userDataKey = attributeValue.replace(/userData\./, '');
|
||||||
|
|
||||||
|
const prop = Object.entries(props.userData).find(([key]) => `{${key}}` === userDataKey);
|
||||||
|
if (prop !== undefined) {
|
||||||
|
attributes[camelize(attName)] = prop[1];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributeValue.startsWith('{{') && attributeValue.endsWith('}}')) {
|
||||||
|
// support for object
|
||||||
|
const stringObject = attributeValue.slice(1, -1);
|
||||||
|
const object: JSON = JSON.parse(stringObject);
|
||||||
|
attributes[camelize(attName)] = object;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prop = Object.entries(props).find(([key]) => `{${key}}` === attributeValue);
|
||||||
|
if (prop !== undefined) {
|
||||||
|
attributes[camelize(attName)] = prop[1];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
attributes[camelize(attName)] = attributeValue;
|
||||||
|
});
|
||||||
|
return React.createElement(node.tagName.toLowerCase(), attributes, children);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function camelize(str: string): any {
|
||||||
|
return str.split('-').map((word, index) => index > 0 ? word.charAt(0).toUpperCase() + word.slice(1) : word).join('');
|
||||||
|
}
|
||||||
|
|
|
@ -11,5 +11,7 @@ export interface IAvailableContainer {
|
||||||
DefaultY?: number
|
DefaultY?: number
|
||||||
AddMethod?: AddMethod
|
AddMethod?: AddMethod
|
||||||
XPositionReference?: XPositionReference
|
XPositionReference?: XPositionReference
|
||||||
|
CustomSVG?: string
|
||||||
Style: React.CSSProperties
|
Style: React.CSSProperties
|
||||||
|
UserData?: object
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,5 +20,7 @@ export default interface IProperties {
|
||||||
isRigidBody: boolean
|
isRigidBody: boolean
|
||||||
isAnchor: boolean
|
isAnchor: boolean
|
||||||
XPositionReference: XPositionReference
|
XPositionReference: XPositionReference
|
||||||
|
customSVG?: string
|
||||||
style?: React.CSSProperties
|
style?: React.CSSProperties
|
||||||
|
userData?: object
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,11 +76,23 @@ const GetSVGLayoutConfiguration = () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: 'Remplissage',
|
Type: 'Remplissage',
|
||||||
|
CustomSVG: `
|
||||||
|
<rect width="{width}" height="{height}" style="{style}"></rect>
|
||||||
|
<rect width="{width}" height="{height}" stroke="black" fill-opacity="0"></rect>
|
||||||
|
<line x1="0" y1="0" x2="{width}" y2="{height}" stroke="black" style='{{ "transform":"scaleY(0.5)"}}'></line>
|
||||||
|
<line x1="{width}" y1="0" x2="0" y2="{height}" stroke="black" style='{userData.styleLine}'></line>
|
||||||
|
`
|
||||||
|
,
|
||||||
Style: {
|
Style: {
|
||||||
fillOpacity: 1,
|
fillOpacity: 1,
|
||||||
strokeWidth: 2,
|
strokeWidth: 1,
|
||||||
stroke: '#bfdbfe',
|
|
||||||
fill: '#bfdbfe'
|
fill: '#bfdbfe'
|
||||||
|
},
|
||||||
|
UserData: {
|
||||||
|
styleLine: {
|
||||||
|
transform: "scaleY(0.5) translateY(100%)",
|
||||||
|
transformBox: "fill-box"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue