Browse Source

Merge pull request #13292 from ArtemDzhereleiko/AD/imp/scada/hint-flow-anim

Hint for dynamic settings and refactoring flow animation connectors SCADA symbols
pull/13344/head
Igor Kulikov 1 year ago
committed by GitHub
parent
commit
d5a89967c5
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 63
      application/src/main/data/json/system/scada_symbols/bottom-right-elbow-connector-hp.svg
  2. 69
      application/src/main/data/json/system/scada_symbols/bottom-tee-connector-hp.svg
  3. 74
      application/src/main/data/json/system/scada_symbols/cross-connector-hp.svg
  4. 112
      application/src/main/data/json/system/scada_symbols/horizontal-connector-hp.svg
  5. 75
      application/src/main/data/json/system/scada_symbols/left-bottom-elbow-connector-hp.svg
  6. 71
      application/src/main/data/json/system/scada_symbols/left-tee-connector-hp.svg
  7. 63
      application/src/main/data/json/system/scada_symbols/left-top-elbow-connector-hp.svg
  8. 118
      application/src/main/data/json/system/scada_symbols/long-horizontal-connector-hp.svg
  9. 119
      application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg
  10. 69
      application/src/main/data/json/system/scada_symbols/right-tee-connector-hp.svg
  11. 63
      application/src/main/data/json/system/scada_symbols/top-right-elbow-connector-hp.svg
  12. 69
      application/src/main/data/json/system/scada_symbols/top-tee-connector-hp.svg
  13. 115
      application/src/main/data/json/system/scada_symbols/vertical-connector-hp.svg
  14. 168
      ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts
  15. 6
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html
  16. 1
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.ts
  17. 10
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.html
  18. 64
      ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts
  19. 1
      ui-ngx/src/app/shared/models/constants.ts
  20. 3
      ui-ngx/src/app/shared/models/dynamic-form.models.ts
  21. 14
      ui-ngx/src/assets/locale/locale.constant-en_US.json

63
application/src/main/data/json/system/scada_symbols/bottom-right-elbow-connector-hp.svg

@ -3,11 +3,11 @@
"description": "Bottom right elbow connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"stateRenderFunction": "const {\n flowAnimation,\n animationDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst animation = ctx.tags.animationGroup[0];\nconst offset = Date.now() % 1000;\nconst duration = 1 / flowAnimationSpeed;\n\nconst prevFlowAnimation = animation.remember('flowAnimation');\nconst prevFlowDirection = animation.remember('flowDirection');\nconst prevFlowDuration = animation.remember('flowDuration');\n\nif (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n} else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n}\n\nfunction animateFlow(offset, flowDirection) {\n animation.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `-${dashWidth + (dashGap || dashWidth)}` : `${dashWidth + (dashGap || dashWidth)}`;\n\n animation.add(`<path style=\"stroke-dasharray: ${dashArray}; stroke-linecap: ${dashCap}; stroke-dashoffset: 0;\" d=\"${line}\" stroke-miterlimit=\"10\" fill=\"none\" stroke=\"${lineColor}\" stroke-width=\"${lineWidth}\"><animate attributeName=\"stroke-dashoffset\" values=\"${value};0\" dur=\"${duration}s\" begin=\"-${offset}ms\" calcMode=\"linear\" repeatCount=\"indefinite\" /></path>`);\n}\n",
"stateRenderFunction": "const {\n flowAnimation,\n animationDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst lineReversed = 'M 200,100 L 125,100 Q 100,100 100,125 L 100, 200';\nconst animation = ctx.tags.animationGroup[0];\nconst duration = 1 / flowAnimationSpeed;\n\nlet animateFlow = ctx.api.connectorAnimation(animation);\n\nif (flowAnimation) {\n if (!animateFlow) {\n animateFlow = ctx.api.connectorAnimate(animation, line, lineReversed).flowAppearance(lineWidth, lineColor, dashCap, dashWidth, dashGap).duration(duration).direction(flowDirection).play();\n } else {\n animateFlow.duration(duration).direction(flowDirection).play();\n }\n} else {\n if (animateFlow) {\n animateFlow.finish();\n }\n}\n",
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nelement.attr({'stroke-width': ctx.properties.mainLineSize});",
"actions": null
}
],
@ -53,8 +53,8 @@
},
{
"id": "animationDirection",
"name": "{i18n:scada.symbol.animation-direction}",
"hint": "{i18n:scada.symbol.animation-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
@ -130,36 +130,13 @@
}
],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"disabled": false,
"visible": true
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"min": 0,
"max": 99,
"step": 1,
"disabled": false,
"visible": true
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": false,
"fieldSuffix": "px",
"min": 0,
"max": 99,
@ -169,7 +146,7 @@
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"name": "{i18n:scada.symbol.line}",
"type": "color",
"default": "#1A1A1A",
"disabled": false,
@ -177,11 +154,11 @@
},
{
"id": "flowAnimationWidth",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 4,
"subLabel": "Width",
"divider": false,
"fieldSuffix": "px",
"min": 1,
"step": 1,
@ -190,8 +167,8 @@
},
{
"id": "flowAnimationColor",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "color",
"default": "#C8DFF7",
"disabled": false,
@ -199,13 +176,14 @@
},
{
"id": "flowStyleDash",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"required": true,
"subLabel": "{i18n:scada.symbol.dash}",
"divider": true,
"divider": false,
"fieldSuffix": "px",
"min": 0,
"step": 1,
@ -214,13 +192,14 @@
},
{
"id": "flowStyleGap",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"subLabel": "{i18n:scada.symbol.gap}",
"fieldSuffix": "px",
"min": 1,
"min": 0,
"step": 1,
"disabled": false,
"visible": true
@ -228,7 +207,7 @@
{
"id": "flowDashCap",
"name": "{i18n:scada.symbol.flow-dash-cap}",
"group": "{i18n:scada.symbol.animation}",
"group": "{i18n:scada.symbol.flow}",
"type": "select",
"default": "butt",
"items": [
@ -250,5 +229,5 @@
}
]
}]]></tb:metadata>
<path d="M200 100H132C115 100 100 115 100 132V200" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/><g tb:tag="animationGroup"/>
<path d="M 100,200 L 100,125 Q 100,100 125,100 L 200, 100" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/><g tb:tag="animationGroup"/>
</svg>

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

69
application/src/main/data/json/system/scada_symbols/bottom-tee-connector-hp.svg

@ -3,11 +3,11 @@
"description": "Bottom tee connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"stateRenderFunction": "const {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\n\nconst leftLine = \"M0 100H100\";\nconst rightLine = \"M100 100H200\";\nconst bottomLine = \"M 100,200 V 103\";\n\nprepareFlowAnimation('left', leftLine);\nprepareFlowAnimation('right', rightLine);\nprepareFlowAnimation('bottom', bottomLine);\n\nfunction prepareFlowAnimation(prefix, line) {\n const flowAnimation = ctx.values[prefix + 'Flow'];\n const flowDirection = ctx.values[prefix + 'FlowDirection'];\n const flowAnimationSpeed = ctx.values[prefix + 'FlowAnimationSpeed'];\n\n const animation = ctx.tags[prefix + 'Line'][0];\n const offset = Date.now() % 1000;\n const duration = 1 / flowAnimationSpeed;\n \n const prevFlowAnimation = animation.remember('flowAnimation');\n const prevFlowDirection = animation.remember('flowDirection');\n const prevFlowDuration = animation.remember('flowDuration');\n \n if (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n } else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n }\n}\n\nfunction animateFlow(group, offset, flowDirection, duration, line) {\n group.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n group.add(`<path style=\"stroke-dasharray: ${dashArray}; stroke-linecap: ${dashCap}; stroke-dashoffset: 0;\" d=\"${line}\" stroke-miterlimit=\"10\" fill=\"none\" stroke=\"${lineColor}\" stroke-width=\"${lineWidth}\"><animate attributeName=\"stroke-dashoffset\" values=\"${value};0\" dur=\"${duration}s\" begin=\"-${offset}ms\" calcMode=\"linear\" repeatCount=\"indefinite\" /></path>`);\n}",
"stateRenderFunction": "const {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\n\nconst leftLine = \"M0 100H100\";\nconst leftLineReversed = \"M 100,100 H 0\";\nconst rightLine = \"M100 100H200\";\nconst rightLineReversed = \"M 200,100 H 100\";\nconst bottomLine = \"M 100,200 V 103\";\nconst bottomLineReversed = \"M 100,103 V 200\";\n\nprepareFlowAnimation('left', leftLine, leftLineReversed);\nprepareFlowAnimation('right', rightLine, rightLineReversed);\nprepareFlowAnimation('bottom', bottomLine, bottomLineReversed);\n\nfunction prepareFlowAnimation(prefix, line, reversedLine) {\n const flowAnimation = ctx.values[prefix + 'Flow'];\n const flowDirection = ctx.values[prefix + 'FlowDirection'];\n const flowAnimationSpeed = ctx.values[prefix + 'FlowAnimationSpeed'];\n\n const animation = ctx.tags[prefix + 'Line'][0];\n const duration = 1 / flowAnimationSpeed;\n \n let animateFlow = ctx.api.connectorAnimation(animation);\n \n if (flowAnimation) {\n if (!animateFlow) {\n animateFlow = ctx.api.connectorAnimate(animation, line, reversedLine).flowAppearance(lineWidth, lineColor, dashCap, dashWidth, dashGap).duration(duration).direction(flowDirection).play();\n } else {\n animateFlow.duration(duration).direction(flowDirection).play();\n }\n } else {\n if (animateFlow) {\n animateFlow.finish();\n }\n }\n}\n",
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nelement.attr({'stroke-width': ctx.properties.mainLineSize});",
"actions": null
},
{
@ -58,8 +58,8 @@
},
{
"id": "leftFlowDirection",
"name": "{i18n:scada.symbol.animation-direction}",
"hint": "{i18n:scada.symbol.animation-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": "{i18n:scada.symbol.left-connector}",
"type": "value",
"valueType": "BOOLEAN",
@ -174,8 +174,8 @@
},
{
"id": "rightFlowDirection",
"name": "{i18n:scada.symbol.flow-direction}",
"hint": "{i18n:scada.symbol.flow-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": "{i18n:scada.symbol.right-connector}",
"type": "value",
"valueType": "BOOLEAN",
@ -290,8 +290,8 @@
},
{
"id": "bottomFlowDirection",
"name": "{i18n:scada.symbol.animation-direction}",
"hint": "{i18n:scada.symbol.animation-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": "{i18n:scada.symbol.bottom-connector}",
"type": "value",
"valueType": "BOOLEAN",
@ -367,36 +367,13 @@
}
],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"disabled": false,
"visible": true
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"min": 0,
"max": 99,
"step": 1,
"disabled": false,
"visible": true
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": false,
"fieldSuffix": "px",
"min": 0,
"max": 99,
@ -406,7 +383,7 @@
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"name": "{i18n:scada.symbol.line}",
"type": "color",
"default": "#1A1A1A",
"disabled": false,
@ -414,11 +391,11 @@
},
{
"id": "flowAnimationWidth",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 4,
"subLabel": "Width",
"divider": false,
"fieldSuffix": "px",
"min": 1,
"step": 1,
@ -427,8 +404,8 @@
},
{
"id": "flowAnimationColor",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "color",
"default": "#C8DFF7",
"disabled": false,
@ -436,13 +413,14 @@
},
{
"id": "flowStyleDash",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"required": true,
"subLabel": "{i18n:scada.symbol.dash}",
"divider": true,
"divider": false,
"fieldSuffix": "px",
"min": 0,
"step": 1,
@ -451,13 +429,14 @@
},
{
"id": "flowStyleGap",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"subLabel": "{i18n:scada.symbol.gap}",
"fieldSuffix": "px",
"min": 1,
"min": 0,
"step": 1,
"disabled": false,
"visible": true
@ -465,7 +444,7 @@
{
"id": "flowDashCap",
"name": "{i18n:scada.symbol.flow-dash-cap}",
"group": "{i18n:scada.symbol.animation}",
"group": "{i18n:scada.symbol.flow}",
"type": "select",
"default": "butt",
"items": [

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 16 KiB

74
application/src/main/data/json/system/scada_symbols/cross-connector-hp.svg

@ -3,11 +3,11 @@
"description": "Cross connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"stateRenderFunction": "const {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\n\nconst leftLine = \"M0 100H100\";\nconst topLine = \"M100 97L100 0\";\nconst rightLine = \"M100 100H200\";\nconst bottomLine = \"M 100,200 V 103\";\n\nprepareFlowAnimation('left', leftLine);\nprepareFlowAnimation('top', topLine);\nprepareFlowAnimation('right', rightLine);\nprepareFlowAnimation('bottom', bottomLine);\n\nfunction prepareFlowAnimation(prefix, line) {\n const flowAnimation = ctx.values[prefix + 'Flow'];\n const flowDirection = ctx.values[prefix + 'FlowDirection'];\n const flowAnimationSpeed = ctx.values[prefix + 'FlowAnimationSpeed'];\n\n const animation = ctx.tags[prefix + 'Line'][0];\n const offset = Date.now() % 1000;\n const duration = 1 / flowAnimationSpeed;\n \n const prevFlowAnimation = animation.remember('flowAnimation');\n const prevFlowDirection = animation.remember('flowDirection');\n const prevFlowDuration = animation.remember('flowDuration');\n \n if (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n } else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n }\n}\n\nfunction animateFlow(group, offset, flowDirection, duration, line) {\n group.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n group.add(`<path style=\"stroke-dasharray: ${dashArray}; stroke-linecap: ${dashCap}; stroke-dashoffset: 0;\" d=\"${line}\" stroke-miterlimit=\"10\" fill=\"none\" stroke=\"${lineColor}\" stroke-width=\"${lineWidth}\"><animate attributeName=\"stroke-dashoffset\" values=\"${value};0\" dur=\"${duration}s\" begin=\"-${offset}ms\" calcMode=\"linear\" repeatCount=\"indefinite\" /></path>`);\n}",
"stateRenderFunction": "const {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\n\nconst leftLine = \"M0 100H100\";\nconst leftLineReversed = \"M 100,100 H 0\";\nconst topLine = \"M100 97L100 0\";\nconst topLineReversed = \"M 100,0 V 97\";\nconst rightLine = \"M100 100H200\";\nconst rightLineReversed = \"M 200,100 H 100\";\nconst bottomLine = \"M 100,200 V 103\";\nconst bottomLineReversed = \"M 100,103 V 200\";\n\nprepareFlowAnimation('left', leftLine, leftLineReversed);\nprepareFlowAnimation('top', topLine, topLineReversed);\nprepareFlowAnimation('right', rightLine, rightLineReversed);\nprepareFlowAnimation('bottom', bottomLine, bottomLineReversed);\n\nfunction prepareFlowAnimation(prefix, line, reversedLine) {\n const flowAnimation = ctx.values[prefix + 'Flow'];\n const flowDirection = ctx.values[prefix + 'FlowDirection'];\n const flowAnimationSpeed = ctx.values[prefix + 'FlowAnimationSpeed'];\n\n const animation = ctx.tags[prefix + 'Line'][0];\n const duration = 1 / flowAnimationSpeed;\n \n let animateFlow = ctx.api.connectorAnimation(animation);\n \n if (flowAnimation) {\n if (!animateFlow) {\n animateFlow = ctx.api.connectorAnimate(animation, line, reversedLine).flowAppearance(lineWidth, lineColor, dashCap, dashWidth, dashGap).duration(duration).direction(flowDirection).play();\n } else {\n animateFlow.duration(duration).direction(flowDirection).play();\n }\n } else {\n if (animateFlow) {\n animateFlow.finish();\n }\n }\n}",
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nelement.attr({'stroke-width': ctx.properties.mainLineSize});",
"actions": null
},
{
@ -58,8 +58,8 @@
},
{
"id": "leftFlowDirection",
"name": "{i18n:scada.symbol.animation-direction}",
"hint": "{i18n:scada.symbol.animation-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": "{i18n:scada.symbol.left-connector}",
"type": "value",
"valueType": "BOOLEAN",
@ -174,8 +174,8 @@
},
{
"id": "topFlowDirection",
"name": "{i18n:scada.symbol.animation-direction}",
"hint": "{i18n:scada.symbol.animation-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": "{i18n:scada.symbol.top-connector}",
"type": "value",
"valueType": "BOOLEAN",
@ -290,8 +290,8 @@
},
{
"id": "rightFlowDirection",
"name": "{i18n:scada.symbol.flow-direction}",
"hint": "{i18n:scada.symbol.flow-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": "{i18n:scada.symbol.right-connector}",
"type": "value",
"valueType": "BOOLEAN",
@ -406,8 +406,8 @@
},
{
"id": "bottomFlowDirection",
"name": "{i18n:scada.symbol.animation-direction}",
"hint": "{i18n:scada.symbol.animation-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": "{i18n:scada.symbol.bottom-connector}",
"type": "value",
"valueType": "BOOLEAN",
@ -483,14 +483,6 @@
}
],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"disabled": false,
"visible": true
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
@ -500,29 +492,15 @@
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"condition": "return model.mainLine;",
"min": 0,
"max": 99,
"step": 1,
"disabled": false,
"visible": true
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"fieldSuffix": "px",
"min": 0,
"max": 99,
"step": 1,
"disabled": false,
"visible": true
"disabled": false
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"name": "{i18n:scada.symbol.line}",
"type": "color",
"default": "#1A1A1A",
"disabled": false,
@ -530,11 +508,11 @@
},
{
"id": "flowAnimationWidth",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 4,
"subLabel": "Width",
"divider": false,
"fieldSuffix": "px",
"min": 1,
"step": 1,
@ -543,8 +521,8 @@
},
{
"id": "flowAnimationColor",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "color",
"default": "#C8DFF7",
"disabled": false,
@ -552,13 +530,14 @@
},
{
"id": "flowStyleDash",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"required": true,
"subLabel": "{i18n:scada.symbol.dash}",
"divider": true,
"divider": false,
"fieldSuffix": "px",
"min": 0,
"step": 1,
@ -567,13 +546,14 @@
},
{
"id": "flowStyleGap",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"subLabel": "{i18n:scada.symbol.gap}",
"fieldSuffix": "px",
"min": 1,
"min": 0,
"step": 1,
"disabled": false,
"visible": true
@ -581,7 +561,7 @@
{
"id": "flowDashCap",
"name": "{i18n:scada.symbol.flow-dash-cap}",
"group": "{i18n:scada.symbol.animation}",
"group": "{i18n:scada.symbol.flow}",
"type": "select",
"default": "butt",
"items": [

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 19 KiB

112
application/src/main/data/json/system/scada_symbols/horizontal-connector-hp.svg

@ -3,7 +3,7 @@
"description": "Horizontal connector with an optional directional arrow to visually indicate flow.",
"widgetSizeX": 1,
"widgetSizeY": 1,
"stateRenderFunction": "const {\n flowAnimation,\n arrowDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst animation = ctx.tags.animationGroup[0];\nconst offset = Date.now() % 1000;\nconst duration = 1 / flowAnimationSpeed;\n\nconst prevFlowAnimation = animation.remember('flowAnimation');\nconst prevFlowDirection = animation.remember('flowDirection');\nconst prevFlowDuration = animation.remember('flowDuration');\n\nif (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n} else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n}\n\nfunction animateFlow(offset, flowDirection) {\n animation.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n animation.add(`<path style=\"stroke-dasharray: ${dashArray}; stroke-linecap: ${dashCap}; stroke-dashoffset: 0;\" d=\"${line}\" stroke-miterlimit=\"10\" fill=\"none\" stroke=\"${lineColor}\" stroke-width=\"${lineWidth}\"><animate attributeName=\"stroke-dashoffset\" values=\"${value};0\" dur=\"${duration}s\" begin=\"-${offset}ms\" calcMode=\"linear\" repeatCount=\"indefinite\" /></path>`);\n}\n",
"stateRenderFunction": "const {\n flowAnimation,\n arrowDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst lineReversed = 'M 200,100 H 0';\nconst animation = ctx.tags.animationGroup[0];\nconst duration = 1 / flowAnimationSpeed;\n\nlet animateFlow = ctx.api.connectorAnimation(animation);\n\nif (flowAnimation) {\n if (!animateFlow) {\n animateFlow = ctx.api.connectorAnimate(animation, line, lineReversed).flowAppearance(lineWidth, lineColor, dashCap, dashWidth, dashGap).duration(duration).direction(flowDirection).play();\n } else {\n animateFlow.duration(duration).direction(flowDirection).play();\n }\n} else {\n if (animateFlow) {\n animateFlow.finish();\n }\n}\n\n",
"tags": [
{
"tag": "arrow",
@ -12,7 +12,7 @@
},
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nelement.attr({'stroke-width': ctx.properties.mainLineSize});",
"actions": null
}
],
@ -53,18 +53,18 @@
"defaultWidgetActionSettings": null
},
{
"id": "arrowDirection",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"id": "flowAnimation",
"name": "{i18n:scada.symbol.flow-animation}",
"hint": "{i18n:scada.symbol.flow-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
@ -72,34 +72,38 @@
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowAnimation",
"name": "{i18n:scada.symbol.flow-animation}",
"hint": "{i18n:scada.symbol.flow-animation-hint}",
"id": "arrowDirection",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
@ -107,20 +111,16 @@
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
@ -166,36 +166,13 @@
}
],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"disabled": false,
"visible": true
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"min": 0,
"max": 99,
"step": 1,
"disabled": false,
"visible": true
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": false,
"fieldSuffix": "px",
"min": 0,
"max": 99,
@ -205,7 +182,7 @@
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"name": "{i18n:scada.symbol.line}",
"type": "color",
"default": "#1A1A1A",
"disabled": false,
@ -213,11 +190,10 @@
},
{
"id": "flowAnimationWidth",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 4,
"subLabel": "Width",
"fieldSuffix": "px",
"min": 1,
"step": 1,
@ -226,20 +202,23 @@
},
{
"id": "flowAnimationColor",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "color",
"default": "#C8DFF7"
"default": "#C8DFF7",
"disabled": false,
"visible": true
},
{
"id": "flowStyleDash",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"required": true,
"subLabel": "{i18n:scada.symbol.dash}",
"divider": true,
"divider": false,
"fieldSuffix": "px",
"min": 0,
"step": 1,
@ -248,13 +227,14 @@
},
{
"id": "flowStyleGap",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"subLabel": "{i18n:scada.symbol.gap}",
"fieldSuffix": "px",
"min": 1,
"min": 0,
"step": 1,
"disabled": false,
"visible": true
@ -262,7 +242,7 @@
{
"id": "flowDashCap",
"name": "{i18n:scada.symbol.flow-dash-cap}",
"group": "{i18n:scada.symbol.animation}",
"group": "{i18n:scada.symbol.flow}",
"type": "select",
"default": "butt",
"items": [

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

75
application/src/main/data/json/system/scada_symbols/left-bottom-elbow-connector-hp.svg

@ -3,11 +3,11 @@
"description": "Left bottom elbow connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"stateRenderFunction": "const {\n flowAnimation,\n animationDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst animation = ctx.tags.animationGroup[0];\nconst offset = Date.now() % 1000;\nconst duration = 1 / flowAnimationSpeed;\n\nconst prevFlowAnimation = animation.remember('flowAnimation');\nconst prevFlowDirection = animation.remember('flowDirection');\nconst prevFlowDuration = animation.remember('flowDuration');\n\nif (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n} else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n}\n\nfunction animateFlow(offset, flowDirection) {\n animation.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n animation.add(`<path style=\"stroke-dasharray: ${dashArray}; stroke-linecap: ${dashCap}; stroke-dashoffset: 0;\" d=\"${line}\" stroke-miterlimit=\"10\" fill=\"none\" stroke=\"${lineColor}\" stroke-width=\"${lineWidth}\"><animate attributeName=\"stroke-dashoffset\" values=\"${value};0\" dur=\"${duration}s\" begin=\"-${offset}ms\" calcMode=\"linear\" repeatCount=\"indefinite\" /></path>`);\n}\n",
"stateRenderFunction": "const {\n flowAnimation,\n animationDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst lineReversed = 'M 100,200 L 100,125 Q 100,100 75,100 L 0, 100';\nconst animation = ctx.tags.animationGroup[0];\nconst duration = 1 / flowAnimationSpeed;\n\nlet animateFlow = ctx.api.connectorAnimation(animation);\n\nif (flowAnimation) {\n if (!animateFlow) {\n animateFlow = ctx.api.connectorAnimate(animation, line, lineReversed).flowAppearance(lineWidth, lineColor, dashCap, dashWidth, dashGap).duration(duration).direction(flowDirection).play();\n } else {\n animateFlow.duration(duration).direction(flowDirection).play();\n }\n} else {\n if (animateFlow) {\n animateFlow.finish();\n }\n}\n",
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nelement.attr({'stroke-width': ctx.properties.mainLineSize});",
"actions": null
}
],
@ -32,8 +32,8 @@
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
"scope": "SHARED_SCOPE",
"key": "flow"
},
"getTimeSeries": {
"key": "state"
@ -44,8 +44,8 @@
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
@ -53,8 +53,8 @@
},
{
"id": "animationDirection",
"name": "{i18n:scada.symbol.animation-direction}",
"hint": "{i18n:scada.symbol.animation-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
@ -130,36 +130,13 @@
}
],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"disabled": false,
"visible": true
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"min": 0,
"max": 99,
"step": 1,
"disabled": false,
"visible": true
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": false,
"fieldSuffix": "px",
"min": 0,
"max": 99,
@ -169,7 +146,7 @@
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"name": "{i18n:scada.symbol.line}",
"type": "color",
"default": "#1A1A1A",
"disabled": false,
@ -177,11 +154,11 @@
},
{
"id": "flowAnimationWidth",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 4,
"subLabel": "Width",
"divider": false,
"fieldSuffix": "px",
"min": 1,
"step": 1,
@ -190,20 +167,23 @@
},
{
"id": "flowAnimationColor",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "color",
"default": "#C8DFF7"
"default": "#C8DFF7",
"disabled": false,
"visible": true
},
{
"id": "flowStyleDash",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"required": true,
"subLabel": "{i18n:scada.symbol.dash}",
"divider": true,
"divider": false,
"fieldSuffix": "px",
"min": 0,
"step": 1,
@ -212,13 +192,14 @@
},
{
"id": "flowStyleGap",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"subLabel": "{i18n:scada.symbol.gap}",
"fieldSuffix": "px",
"min": 1,
"min": 0,
"step": 1,
"disabled": false,
"visible": true
@ -226,7 +207,7 @@
{
"id": "flowDashCap",
"name": "{i18n:scada.symbol.flow-dash-cap}",
"group": "{i18n:scada.symbol.animation}",
"group": "{i18n:scada.symbol.flow}",
"type": "select",
"default": "butt",
"items": [
@ -248,5 +229,5 @@
}
]
}]]></tb:metadata>
<path d="M100 200L100 131C100 113.879 86.1208 100 69 100L0 100" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/><g tb:tag="animationGroup"/>
<path d="M 0,100 L 75,100 Q 100,100 100,125 L 100, 200" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/><g tb:tag="animationGroup"/>
</svg>

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

71
application/src/main/data/json/system/scada_symbols/left-tee-connector-hp.svg

@ -3,11 +3,11 @@
"description": "Left tee connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"stateRenderFunction": "const {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\n\nconst leftLine = \"M0 100H97\";\nconst topLine = \"M100 100L100 0\";\nconst bottomLine = \"M 100,200 V 100\";\n\nprepareFlowAnimation('left', leftLine);\nprepareFlowAnimation('top', topLine);\nprepareFlowAnimation('bottom', bottomLine);\n\nfunction prepareFlowAnimation(prefix, line) {\n const flowAnimation = ctx.values[prefix + 'Flow'];\n const flowDirection = ctx.values[prefix + 'FlowDirection'];\n const flowAnimationSpeed = ctx.values[prefix + 'FlowAnimationSpeed'];\n\n const animation = ctx.tags[prefix + 'Line'][0];\n const offset = Date.now() % 1000;\n const duration = 1 / flowAnimationSpeed;\n \n const prevFlowAnimation = animation.remember('flowAnimation');\n const prevFlowDirection = animation.remember('flowDirection');\n const prevFlowDuration = animation.remember('flowDuration');\n \n if (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n } else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n }\n}\n\nfunction animateFlow(group, offset, flowDirection, duration, line) {\n group.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n group.add(`<path style=\"stroke-dasharray: ${dashArray}; stroke-linecap: ${dashCap}; stroke-dashoffset: 0;\" d=\"${line}\" stroke-miterlimit=\"10\" fill=\"none\" stroke=\"${lineColor}\" stroke-width=\"${lineWidth}\"><animate attributeName=\"stroke-dashoffset\" values=\"${value};0\" dur=\"${duration}s\" begin=\"-${offset}ms\" calcMode=\"linear\" repeatCount=\"indefinite\" /></path>`);\n}",
"stateRenderFunction": "const {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\n\nconst leftLine = \"M0 100H97\";\nconst leftLineReversed = \"M 97,100 H 0\";\nconst topLine = \"M100 100L100 0\";\nconst topLineReversed = \"M 100,0 V 100\";\nconst bottomLine = \"M 100,200 V 100\";\nconst bottomLineReversed = \"M 100,100 V 200\";\n\nprepareFlowAnimation('left', leftLine, leftLineReversed);\nprepareFlowAnimation('top', topLine, topLineReversed);\nprepareFlowAnimation('bottom', bottomLine, bottomLineReversed);\n\nfunction prepareFlowAnimation(prefix, line, reversedLine) {\n const flowAnimation = ctx.values[prefix + 'Flow'];\n const flowDirection = ctx.values[prefix + 'FlowDirection'];\n const flowAnimationSpeed = ctx.values[prefix + 'FlowAnimationSpeed'];\n\n const animation = ctx.tags[prefix + 'Line'][0];\n const duration = 1 / flowAnimationSpeed;\n \n let animateFlow = ctx.api.connectorAnimation(animation);\n \n if (flowAnimation) {\n if (!animateFlow) {\n animateFlow = ctx.api.connectorAnimate(animation, line, reversedLine).flowAppearance(lineWidth, lineColor, dashCap, dashWidth, dashGap).duration(duration).direction(flowDirection).play();\n } else {\n animateFlow.duration(duration).direction(flowDirection).play();\n }\n } else {\n if (animateFlow) {\n animateFlow.finish();\n }\n }\n}",
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nelement.attr({'stroke-width': ctx.properties.mainLineSize});",
"actions": null
},
{
@ -58,8 +58,8 @@
},
{
"id": "leftFlowDirection",
"name": "{i18n:scada.symbol.animation-direction}",
"hint": "{i18n:scada.symbol.animation-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": "{i18n:scada.symbol.left-connector}",
"type": "value",
"valueType": "BOOLEAN",
@ -174,8 +174,8 @@
},
{
"id": "topFlowDirection",
"name": "{i18n:scada.symbol.animation-direction}",
"hint": "{i18n:scada.symbol.animation-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": "{i18n:scada.symbol.top-connector}",
"type": "value",
"valueType": "BOOLEAN",
@ -290,8 +290,8 @@
},
{
"id": "bottomFlowDirection",
"name": "{i18n:scada.symbol.flow-direction}",
"hint": "{i18n:scada.symbol.flow-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": "{i18n:scada.symbol.bottom-connector}",
"type": "value",
"valueType": "BOOLEAN",
@ -367,36 +367,13 @@
}
],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"disabled": false,
"visible": true
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"min": 0,
"max": 99,
"step": 1,
"disabled": false,
"visible": true
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": false,
"fieldSuffix": "px",
"min": 0,
"max": 99,
@ -406,7 +383,7 @@
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"name": "{i18n:scada.symbol.line}",
"type": "color",
"default": "#1A1A1A",
"disabled": false,
@ -414,11 +391,11 @@
},
{
"id": "flowAnimationWidth",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 4,
"subLabel": "Width",
"divider": false,
"fieldSuffix": "px",
"min": 1,
"step": 1,
@ -427,8 +404,8 @@
},
{
"id": "flowAnimationColor",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "color",
"default": "#C8DFF7",
"disabled": false,
@ -436,13 +413,14 @@
},
{
"id": "flowStyleDash",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"required": true,
"subLabel": "{i18n:scada.symbol.dash}",
"divider": true,
"divider": false,
"fieldSuffix": "px",
"min": 0,
"step": 1,
@ -451,13 +429,14 @@
},
{
"id": "flowStyleGap",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"subLabel": "{i18n:scada.symbol.gap}",
"fieldSuffix": "px",
"min": 1,
"min": 0,
"step": 1,
"disabled": false,
"visible": true
@ -465,7 +444,7 @@
{
"id": "flowDashCap",
"name": "{i18n:scada.symbol.flow-dash-cap}",
"group": "{i18n:scada.symbol.animation}",
"group": "{i18n:scada.symbol.flow}",
"type": "select",
"default": "butt",
"items": [
@ -487,5 +466,5 @@
}
]
}]]></tb:metadata>
<path d="M100 0V200" stroke="#1A1A1A" stroke-width="6" id="path6" tb:tag="line"/><path d="M100 113V87C100 87 100 100 86 100C100 100 100 113 100 113Z" fill="#1A1A1A" stroke="#1A1A1A" stroke-width="2" id="path8" tb:tag="line-color"/><path d="M0 100H85C93.2843 100 100 93.2843 100 85V0" stroke="#1A1A1A" stroke-width="6" id="path4" tb:tag="line"/><path d="M0 100H85C93.2843 100 100 106.716 100 115V200" stroke="#1A1A1A" stroke-width="6" id="path2" tb:tag="line"/><g tb:tag="animationGroup"><g tb:tag="leftLine"/><g tb:tag="topLine"/><g tb:tag="bottomLine"/></g>
<path d="M100 0V200" stroke="#1A1A1A" stroke-width="6" id="path6" tb:tag="line"/><path d="M100 113V87C100 87 100 100 86 100C100 100 100 113 100 113Z" fill="#1A1A1A" stroke="#1A1A1A" stroke-width="2" id="path8" tb:tag="line-color"/><path d="M0 100H85C93.2843 100 100 93.2843 100 85V0" stroke="#1A1A1A" stroke-width="6" id="path4" tb:tag="line"/><path d="M0 100H85C93.2843 100 100 106.716 100 115V200" stroke="#1A1A1A" stroke-width="6" id="path2" tb:tag="line"/><g tb:tag="animationGroup"><g tb:tag="leftLine"/><g tb:tag="topLine"> </g><g tb:tag="bottomLine"> </g></g>
</svg>

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 16 KiB

63
application/src/main/data/json/system/scada_symbols/left-top-elbow-connector-hp.svg

@ -3,11 +3,11 @@
"description": "Left top elbow connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"stateRenderFunction": "const {\n flowAnimation,\n animationDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst animation = ctx.tags.animationGroup[0];\nconst offset = Date.now() % 1000;\nconst duration = 1 / flowAnimationSpeed;\n\nconst prevFlowAnimation = animation.remember('flowAnimation');\nconst prevFlowDirection = animation.remember('flowDirection');\nconst prevFlowDuration = animation.remember('flowDuration');\n\nif (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n} else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n}\n\nfunction animateFlow(offset, flowDirection) {\n animation.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n animation.add(`<path style=\"stroke-dasharray: ${dashArray}; stroke-linecap: ${dashCap}; stroke-dashoffset: 0;\" d=\"${line}\" stroke-miterlimit=\"10\" fill=\"none\" stroke=\"${lineColor}\" stroke-width=\"${lineWidth}\"><animate attributeName=\"stroke-dashoffset\" values=\"${value};0\" dur=\"${duration}s\" begin=\"-${offset}ms\" calcMode=\"linear\" repeatCount=\"indefinite\" /></path>`);\n}\n",
"stateRenderFunction": "const {\n flowAnimation,\n animationDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst lineReversed = 'M 100,0 L 100,75 Q 100,100 75,100 L 0, 100';\nconst animation = ctx.tags.animationGroup[0];\nconst duration = 1 / flowAnimationSpeed;\n\nlet animateFlow = ctx.api.connectorAnimation(animation);\n\nif (flowAnimation) {\n if (!animateFlow) {\n animateFlow = ctx.api.connectorAnimate(animation, line, lineReversed).flowAppearance(lineWidth, lineColor, dashCap, dashWidth, dashGap).duration(duration).direction(flowDirection).play();\n } else {\n animateFlow.duration(duration).direction(flowDirection).play();\n }\n} else {\n if (animateFlow) {\n animateFlow.finish();\n }\n}\n",
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nelement.attr({'stroke-width': ctx.properties.mainLineSize});",
"actions": null
}
],
@ -53,8 +53,8 @@
},
{
"id": "animationDirection",
"name": "{i18n:scada.symbol.animation-direction}",
"hint": "{i18n:scada.symbol.animation-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
@ -130,36 +130,13 @@
}
],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"disabled": false,
"visible": true
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"min": 0,
"max": 99,
"step": 1,
"disabled": false,
"visible": true
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": false,
"fieldSuffix": "px",
"min": 0,
"max": 99,
@ -169,7 +146,7 @@
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"name": "{i18n:scada.symbol.line}",
"type": "color",
"default": "#1A1A1A",
"disabled": false,
@ -177,11 +154,11 @@
},
{
"id": "flowAnimationWidth",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 4,
"subLabel": "Width",
"divider": false,
"fieldSuffix": "px",
"min": 1,
"step": 1,
@ -190,8 +167,8 @@
},
{
"id": "flowAnimationColor",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "color",
"default": "#C8DFF7",
"disabled": false,
@ -199,13 +176,14 @@
},
{
"id": "flowStyleDash",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"required": true,
"subLabel": "{i18n:scada.symbol.dash}",
"divider": true,
"divider": false,
"fieldSuffix": "px",
"min": 0,
"step": 1,
@ -214,13 +192,14 @@
},
{
"id": "flowStyleGap",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"subLabel": "{i18n:scada.symbol.gap}",
"fieldSuffix": "px",
"min": 1,
"min": 0,
"step": 1,
"disabled": false,
"visible": true
@ -228,7 +207,7 @@
{
"id": "flowDashCap",
"name": "{i18n:scada.symbol.flow-dash-cap}",
"group": "{i18n:scada.symbol.animation}",
"group": "{i18n:scada.symbol.flow}",
"type": "select",
"default": "butt",
"items": [
@ -250,5 +229,5 @@
}
]
}]]></tb:metadata>
<path d="M0 100H69C86.1208 100 100 86.1208 100 69V0" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/><g tb:tag="animationGroup"/>
<path d="M 0,100 L 75,100 Q 100,100 100,75 L 100, 0" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/><g tb:tag="animationGroup"/>
</svg>

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

118
application/src/main/data/json/system/scada_symbols/long-horizontal-connector-hp.svg

@ -1,10 +1,9 @@
<svg width="400" height="200" fill="none" version="1.1" viewBox="0 0 400 200" xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg">
<tb:metadata><![CDATA[{
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="200" fill="none" version="1.1" viewBox="0 0 400 200"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Long horizontal connector",
"description": "Long horizontal connector with an optional directional arrow to visually indicate flow.",
"widgetSizeX": 2,
"widgetSizeY": 1,
"stateRenderFunction": "const {\n flowAnimation,\n arrowDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst animation = ctx.tags.animationGroup[0];\nconst offset = Date.now() % 1000;\nconst duration = 1 / flowAnimationSpeed;\n\nconst prevFlowAnimation = animation.remember('flowAnimation');\nconst prevFlowDirection = animation.remember('flowDirection');\nconst prevFlowDuration = animation.remember('flowDuration');\n\nif (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n} else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n}\n\nfunction animateFlow(offset, flowDirection) {\n animation.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n animation.add(`<path style=\"stroke-dasharray: ${dashArray}; stroke-linecap: ${dashCap}; stroke-dashoffset: 0;\" d=\"${line}\" stroke-miterlimit=\"10\" fill=\"none\" stroke=\"${lineColor}\" stroke-width=\"${lineWidth}\"><animate attributeName=\"stroke-dashoffset\" values=\"${value};0\" dur=\"${duration}s\" begin=\"-${offset}ms\" calcMode=\"linear\" repeatCount=\"indefinite\" /></path>`);\n}\n",
"stateRenderFunction": "const {\n flowAnimation,\n arrowDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst lineReversed = 'M 400,100 H 0';\nconst animation = ctx.tags.animationGroup[0];\nconst duration = 1 / flowAnimationSpeed;\n\nlet animateFlow = ctx.api.connectorAnimation(animation);\n\nif (flowAnimation) {\n if (!animateFlow) {\n animateFlow = ctx.api.connectorAnimate(animation, line, lineReversed).flowAppearance(lineWidth, lineColor, dashCap, dashWidth, dashGap).duration(duration).direction(flowDirection).play();\n } else {\n animateFlow.duration(duration).direction(flowDirection).play();\n }\n} else {\n if (animateFlow) {\n animateFlow.finish();\n }\n}\n",
"tags": [
{
"tag": "arrow",
@ -13,7 +12,7 @@
},
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nelement.attr({'stroke-width': ctx.properties.mainLineSize});",
"actions": null
}
],
@ -54,18 +53,18 @@
"defaultWidgetActionSettings": null
},
{
"id": "arrowDirection",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"id": "flowAnimation",
"name": "{i18n:scada.symbol.flow-animation}",
"hint": "{i18n:scada.symbol.flow-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
@ -73,34 +72,38 @@
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowAnimation",
"name": "{i18n:scada.symbol.flow-animation}",
"hint": "{i18n:scada.symbol.flow-animation-hint}",
"id": "arrowDirection",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
@ -108,20 +111,16 @@
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
@ -167,36 +166,13 @@
}
],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"disabled": false,
"visible": true
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"min": 0,
"max": 99,
"step": 1,
"disabled": false,
"visible": true
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": false,
"fieldSuffix": "px",
"min": 0,
"max": 99,
@ -206,7 +182,7 @@
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"name": "{i18n:scada.symbol.line}",
"type": "color",
"default": "#1A1A1A",
"disabled": false,
@ -214,11 +190,11 @@
},
{
"id": "flowAnimationWidth",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 4,
"subLabel": "Width",
"divider": false,
"fieldSuffix": "px",
"min": 1,
"step": 1,
@ -227,20 +203,23 @@
},
{
"id": "flowAnimationColor",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "color",
"default": "#C8DFF7"
"default": "#C8DFF7",
"disabled": false,
"visible": true
},
{
"id": "flowStyleDash",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"required": true,
"subLabel": "{i18n:scada.symbol.dash}",
"divider": true,
"divider": false,
"fieldSuffix": "px",
"min": 0,
"step": 1,
@ -249,13 +228,14 @@
},
{
"id": "flowStyleGap",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"subLabel": "{i18n:scada.symbol.gap}",
"fieldSuffix": "px",
"min": 1,
"min": 0,
"step": 1,
"disabled": false,
"visible": true
@ -263,7 +243,7 @@
{
"id": "flowDashCap",
"name": "{i18n:scada.symbol.flow-dash-cap}",
"group": "{i18n:scada.symbol.animation}",
"group": "{i18n:scada.symbol.flow}",
"type": "select",
"default": "butt",
"items": [
@ -285,5 +265,5 @@
}
]
}]]></tb:metadata>
<path d="m0 100h400" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/><path d="m229 100-58 29v-58z" fill="#1a1a1a" tb:tag="arrow"/><g tb:tag="animationGroup"/>
<path d="M0 100H400" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/><path d="m229 100-58 29v-58z" fill="#1a1a1a" tb:tag="arrow"/><g tb:tag="animationGroup"/>
</svg>

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

119
application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg

@ -3,7 +3,7 @@
"description": "Long vertical connector with an optional directional arrow to visually indicate flow.",
"widgetSizeX": 1,
"widgetSizeY": 2,
"stateRenderFunction": "const {\n flowAnimation,\n arrowDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst animation = ctx.tags.animationGroup[0];\nconst offset = Date.now() % 1000;\nconst duration = 1 / flowAnimationSpeed;\n\nconst prevFlowAnimation = animation.remember('flowAnimation');\nconst prevFlowDirection = animation.remember('flowDirection');\nconst prevFlowDuration = animation.remember('flowDuration');\n\nif (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n} else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n}\n\nfunction animateFlow(offset, flowDirection) {\n animation.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n animation.add(`<path style=\"stroke-dasharray: ${dashArray}; stroke-linecap: ${dashCap}; stroke-dashoffset: 0;\" d=\"${line}\" stroke-miterlimit=\"10\" fill=\"none\" stroke=\"${lineColor}\" stroke-width=\"${lineWidth}\"><animate attributeName=\"stroke-dashoffset\" values=\"${value};0\" dur=\"${duration}s\" begin=\"-${offset}ms\" calcMode=\"linear\" repeatCount=\"indefinite\" /></path>`);\n}\n",
"stateRenderFunction": "const {\n flowAnimation,\n arrowDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst lineReversed = 'M 100,0 V 400';\nconst animation = ctx.tags.animationGroup[0];\nconst duration = 1 / flowAnimationSpeed;\n\nlet animateFlow = ctx.api.connectorAnimation(animation);\n\nif (flowAnimation) {\n if (!animateFlow) {\n animateFlow = ctx.api.connectorAnimate(animation, line, lineReversed).flowAppearance(lineWidth, lineColor, dashCap, dashWidth, dashGap).duration(duration).direction(flowDirection).play();\n } else {\n animateFlow.duration(duration).direction(flowDirection).play();\n }\n} else {\n if (animateFlow) {\n animateFlow.finish();\n }\n}\n\n",
"tags": [
{
"tag": "arrow",
@ -12,7 +12,7 @@
},
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nelement.attr({'stroke-width': ctx.properties.mainLineSize});",
"actions": null
}
],
@ -53,18 +53,18 @@
"defaultWidgetActionSettings": null
},
{
"id": "arrowDirection",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"id": "flowAnimation",
"name": "{i18n:scada.symbol.flow-animation}",
"hint": "{i18n:scada.symbol.flow-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
@ -72,34 +72,38 @@
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowAnimation",
"name": "{i18n:scada.symbol.flow-animation}",
"hint": "{i18n:scada.symbol.flow-animation-hint}",
"id": "arrowDirection",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
@ -107,20 +111,16 @@
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
@ -166,36 +166,13 @@
}
],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"disabled": false,
"visible": true
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"min": 0,
"max": 99,
"step": 1,
"disabled": false,
"visible": true
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": false,
"fieldSuffix": "px",
"min": 0,
"max": 99,
@ -205,7 +182,7 @@
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"name": "{i18n:scada.symbol.line}",
"type": "color",
"default": "#1A1A1A",
"disabled": false,
@ -213,11 +190,11 @@
},
{
"id": "flowAnimationWidth",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 4,
"subLabel": "Width",
"divider": false,
"fieldSuffix": "px",
"min": 1,
"step": 1,
@ -226,20 +203,23 @@
},
{
"id": "flowAnimationColor",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "color",
"default": "#C8DFF7"
"default": "#C8DFF7",
"disabled": false,
"visible": true
},
{
"id": "flowStyleDash",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"required": true,
"subLabel": "{i18n:scada.symbol.dash}",
"divider": true,
"divider": false,
"fieldSuffix": "px",
"min": 0,
"step": 1,
@ -248,21 +228,20 @@
},
{
"id": "flowStyleGap",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"subLabel": "{i18n:scada.symbol.gap}",
"fieldSuffix": "px",
"min": 1,
"step": 1,
"disabled": false,
"visible": true
"min": 0,
"step": 1
},
{
"id": "flowDashCap",
"name": "{i18n:scada.symbol.flow-dash-cap}",
"group": "{i18n:scada.symbol.animation}",
"group": "{i18n:scada.symbol.flow}",
"type": "select",
"default": "butt",
"items": [
@ -284,5 +263,5 @@
}
]
}]]></tb:metadata>
<path d="m100 400v-400" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/><path d="m100 171 29 58h-58l29-58z" fill="#1A1A1A" tb:tag="arrow"/><g tb:tag="animationGroup"/>
<path d="M 100,400 V 0" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/><path d="m100 171 29 58h-58l29-58z" fill="#1A1A1A" tb:tag="arrow"/><g tb:tag="animationGroup"/>
</svg>

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

69
application/src/main/data/json/system/scada_symbols/right-tee-connector-hp.svg

@ -3,11 +3,11 @@
"description": "Right tee connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"stateRenderFunction": "const {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\n\nconst topLine = \"M100 100L100 0\";\nconst rightLine = \"M103 100H200\";\nconst bottomLine = \"M 100,200 V 100\";\n\nprepareFlowAnimation('top', topLine);\nprepareFlowAnimation('right', rightLine);\nprepareFlowAnimation('bottom', bottomLine);\n\nfunction prepareFlowAnimation(prefix, line) {\n const flowAnimation = ctx.values[prefix + 'Flow'];\n const flowDirection = ctx.values[prefix + 'FlowDirection'];\n const flowAnimationSpeed = ctx.values[prefix + 'FlowAnimationSpeed'];\n\n const animation = ctx.tags[prefix + 'Line'][0];\n const offset = Date.now() % 1000;\n const duration = 1 / flowAnimationSpeed;\n \n const prevFlowAnimation = animation.remember('flowAnimation');\n const prevFlowDirection = animation.remember('flowDirection');\n const prevFlowDuration = animation.remember('flowDuration');\n \n if (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n } else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n }\n}\n\nfunction animateFlow(group, offset, flowDirection, duration, line) {\n group.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n group.add(`<path style=\"stroke-dasharray: ${dashArray}; stroke-linecap: ${dashCap}; stroke-dashoffset: 0;\" d=\"${line}\" stroke-miterlimit=\"10\" fill=\"none\" stroke=\"${lineColor}\" stroke-width=\"${lineWidth}\"><animate attributeName=\"stroke-dashoffset\" values=\"${value};0\" dur=\"${duration}s\" begin=\"-${offset}ms\" calcMode=\"linear\" repeatCount=\"indefinite\" /></path>`);\n}",
"stateRenderFunction": "const {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\n\nconst topLine = \"M100 100L100 0\";\nconst topLineReversed = \"M 100,0 V 100\";\nconst rightLine = \"M103 100H200\";\nconst rightLineReversed = \"M 200,100 H 103\";\nconst bottomLine = \"M 100,200 V 100\";\nconst bottomLineReversed = \"M 100,100 V 200\";\n\nprepareFlowAnimation('top', topLine, topLineReversed);\nprepareFlowAnimation('right', rightLine, rightLineReversed);\nprepareFlowAnimation('bottom', bottomLine, bottomLineReversed);\n\nfunction prepareFlowAnimation(prefix, line, reversedLine) {\n const flowAnimation = ctx.values[prefix + 'Flow'];\n const flowDirection = ctx.values[prefix + 'FlowDirection'];\n const flowAnimationSpeed = ctx.values[prefix + 'FlowAnimationSpeed'];\n\n const animation = ctx.tags[prefix + 'Line'][0];\n const duration = 1 / flowAnimationSpeed;\n \n let animateFlow = ctx.api.connectorAnimation(animation);\n \n if (flowAnimation) {\n if (!animateFlow) {\n animateFlow = ctx.api.connectorAnimate(animation, line, reversedLine).flowAppearance(lineWidth, lineColor, dashCap, dashWidth, dashGap).duration(duration).direction(flowDirection).play();\n } else {\n animateFlow.duration(duration).direction(flowDirection).play();\n }\n } else {\n if (animateFlow) {\n animateFlow.finish();\n }\n }\n}",
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nelement.attr({'stroke-width': ctx.properties.mainLineSize});",
"actions": null
},
{
@ -58,8 +58,8 @@
},
{
"id": "topFlowDirection",
"name": "{i18n:scada.symbol.animation-direction}",
"hint": "{i18n:scada.symbol.animation-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": "{i18n:scada.symbol.top-connector}",
"type": "value",
"valueType": "BOOLEAN",
@ -174,8 +174,8 @@
},
{
"id": "rightFlowDirection",
"name": "{i18n:scada.symbol.flow-direction}",
"hint": "{i18n:scada.symbol.flow-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": "{i18n:scada.symbol.right-connector}",
"type": "value",
"valueType": "BOOLEAN",
@ -290,8 +290,8 @@
},
{
"id": "bottomFlowDirection",
"name": "{i18n:scada.symbol.animation-direction}",
"hint": "{i18n:scada.symbol.animation-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": "{i18n:scada.symbol.bottom-connector}",
"type": "value",
"valueType": "BOOLEAN",
@ -367,36 +367,13 @@
}
],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"disabled": false,
"visible": true
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"min": 0,
"max": 99,
"step": 1,
"disabled": false,
"visible": true
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": false,
"fieldSuffix": "px",
"min": 0,
"max": 99,
@ -406,7 +383,7 @@
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"name": "{i18n:scada.symbol.line}",
"type": "color",
"default": "#1A1A1A",
"disabled": false,
@ -414,11 +391,11 @@
},
{
"id": "flowAnimationWidth",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 4,
"subLabel": "Width",
"divider": false,
"fieldSuffix": "px",
"min": 1,
"step": 1,
@ -427,8 +404,8 @@
},
{
"id": "flowAnimationColor",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "color",
"default": "#C8DFF7",
"disabled": false,
@ -436,13 +413,14 @@
},
{
"id": "flowStyleDash",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"required": true,
"subLabel": "{i18n:scada.symbol.dash}",
"divider": true,
"divider": false,
"fieldSuffix": "px",
"min": 0,
"step": 1,
@ -451,13 +429,14 @@
},
{
"id": "flowStyleGap",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"subLabel": "{i18n:scada.symbol.gap}",
"fieldSuffix": "px",
"min": 1,
"min": 0,
"step": 1,
"disabled": false,
"visible": true
@ -465,7 +444,7 @@
{
"id": "flowDashCap",
"name": "{i18n:scada.symbol.flow-dash-cap}",
"group": "{i18n:scada.symbol.animation}",
"group": "{i18n:scada.symbol.flow}",
"type": "select",
"default": "butt",
"items": [

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 16 KiB

63
application/src/main/data/json/system/scada_symbols/top-right-elbow-connector-hp.svg

@ -3,11 +3,11 @@
"description": "Top right elbow connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"stateRenderFunction": "const {\n flowAnimation,\n animationDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst animation = ctx.tags.animationGroup[0];\nconst offset = Date.now() % 1000;\nconst duration = 1 / flowAnimationSpeed;\n\nconst prevFlowAnimation = animation.remember('flowAnimation');\nconst prevFlowDirection = animation.remember('flowDirection');\nconst prevFlowDuration = animation.remember('flowDuration');\n\nif (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n} else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n}\n\nfunction animateFlow(offset, flowDirection) {\n animation.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `-${dashWidth + (dashGap || dashWidth)}` : `${dashWidth + (dashGap || dashWidth)}`;\n\n animation.add(`<path style=\"stroke-dasharray: ${dashArray}; stroke-linecap: ${dashCap}; stroke-dashoffset: 0;\" d=\"${line}\" stroke-miterlimit=\"10\" fill=\"none\" stroke=\"${lineColor}\" stroke-width=\"${lineWidth}\"><animate attributeName=\"stroke-dashoffset\" values=\"${value};0\" dur=\"${duration}s\" begin=\"-${offset}ms\" calcMode=\"linear\" repeatCount=\"indefinite\" /></path>`);\n}\n",
"stateRenderFunction": "const {\n flowAnimation,\n animationDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst lineReversed = 'M 200,100 L 125,100 Q 100,100 100,75 L 100, 0';\nconst animation = ctx.tags.animationGroup[0];\nconst duration = 1 / flowAnimationSpeed;\n\nlet animateFlow = ctx.api.connectorAnimation(animation);\n\nif (flowAnimation) {\n if (!animateFlow) {\n animateFlow = ctx.api.connectorAnimate(animation, line, lineReversed).flowAppearance(lineWidth, lineColor, dashCap, dashWidth, dashGap).duration(duration).direction(flowDirection).play();\n } else {\n animateFlow.duration(duration).direction(flowDirection).play();\n }\n} else {\n if (animateFlow) {\n animateFlow.finish();\n }\n}\n",
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nelement.attr({'stroke-width': ctx.properties.mainLineSize});",
"actions": null
}
],
@ -53,8 +53,8 @@
},
{
"id": "animationDirection",
"name": "{i18n:scada.symbol.animation-direction}",
"hint": "{i18n:scada.symbol.animation-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
@ -130,36 +130,13 @@
}
],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"disabled": false,
"visible": true
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"min": 0,
"max": 99,
"step": 1,
"disabled": false,
"visible": true
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": false,
"fieldSuffix": "px",
"min": 0,
"max": 99,
@ -169,7 +146,7 @@
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"name": "{i18n:scada.symbol.line}",
"type": "color",
"default": "#1A1A1A",
"disabled": false,
@ -177,11 +154,11 @@
},
{
"id": "flowAnimationWidth",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 4,
"subLabel": "Width",
"divider": false,
"fieldSuffix": "px",
"min": 1,
"step": 1,
@ -190,8 +167,8 @@
},
{
"id": "flowAnimationColor",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "color",
"default": "#C8DFF7",
"disabled": false,
@ -199,13 +176,14 @@
},
{
"id": "flowStyleDash",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"required": true,
"subLabel": "{i18n:scada.symbol.dash}",
"divider": true,
"divider": false,
"fieldSuffix": "px",
"min": 0,
"step": 1,
@ -214,13 +192,14 @@
},
{
"id": "flowStyleGap",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"subLabel": "{i18n:scada.symbol.gap}",
"fieldSuffix": "px",
"min": 1,
"min": 0,
"step": 1,
"disabled": false,
"visible": true
@ -228,7 +207,7 @@
{
"id": "flowDashCap",
"name": "{i18n:scada.symbol.flow-dash-cap}",
"group": "{i18n:scada.symbol.animation}",
"group": "{i18n:scada.symbol.flow}",
"type": "select",
"default": "butt",
"items": [
@ -250,5 +229,5 @@
}
]
}]]></tb:metadata>
<path d="M100 0V69C100 86.1208 113.879 100 131 100H200" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/><g tb:tag="animationGroup"/>
<path d="M 100,0 L 100,75 Q 100,100 125,100 L 200, 100" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/><g tb:tag="animationGroup"/>
</svg>

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

69
application/src/main/data/json/system/scada_symbols/top-tee-connector-hp.svg

@ -3,11 +3,11 @@
"description": "Top tee connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"stateRenderFunction": "const {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\n\nconst leftLine = \"M0 100H100\";\nconst topLine = \"M100 97L100 0\";\nconst rightLine = \"M100 100H200\";\n\nprepareFlowAnimation('left', leftLine);\nprepareFlowAnimation('top', topLine);\nprepareFlowAnimation('right', rightLine);\n\nfunction prepareFlowAnimation(prefix, line) {\n const flowAnimation = ctx.values[prefix + 'Flow'];\n const flowDirection = ctx.values[prefix + 'FlowDirection'];\n const flowAnimationSpeed = ctx.values[prefix + 'FlowAnimationSpeed'];\n\n const animation = ctx.tags[prefix + 'Line'][0];\n const offset = Date.now() % 1000;\n const duration = 1 / flowAnimationSpeed;\n \n const prevFlowAnimation = animation.remember('flowAnimation');\n const prevFlowDirection = animation.remember('flowDirection');\n const prevFlowDuration = animation.remember('flowDuration');\n \n if (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n } else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n }\n}\n\nfunction animateFlow(group, offset, flowDirection, duration, line) {\n group.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n group.add(`<path style=\"stroke-dasharray: ${dashArray}; stroke-linecap: ${dashCap}; stroke-dashoffset: 0;\" d=\"${line}\" stroke-miterlimit=\"10\" fill=\"none\" stroke=\"${lineColor}\" stroke-width=\"${lineWidth}\"><animate attributeName=\"stroke-dashoffset\" values=\"${value};0\" dur=\"${duration}s\" begin=\"-${offset}ms\" calcMode=\"linear\" repeatCount=\"indefinite\" /></path>`);\n}",
"stateRenderFunction": "const {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\n\nconst leftLine = \"M0 100H100\";\nconst leftLineReversed = \"M 100,100 H 0\";\nconst topLine = \"M100 97L100 0\";\nconst topLineReversed = \"M 100,0 V 97\";\nconst rightLine = \"M100 100H200\";\nconst rightLineReversed = \"M 200,100 H 100\";\n\nprepareFlowAnimation('left', leftLine, leftLineReversed);\nprepareFlowAnimation('top', topLine, topLineReversed);\nprepareFlowAnimation('right', rightLine, rightLineReversed);\n\nfunction prepareFlowAnimation(prefix, line, reversedLine) {\n const flowAnimation = ctx.values[prefix + 'Flow'];\n const flowDirection = ctx.values[prefix + 'FlowDirection'];\n const flowAnimationSpeed = ctx.values[prefix + 'FlowAnimationSpeed'];\n\n const animation = ctx.tags[prefix + 'Line'][0];\n const duration = 1 / flowAnimationSpeed;\n \n let animateFlow = ctx.api.connectorAnimation(animation);\n \n if (flowAnimation) {\n if (!animateFlow) {\n animateFlow = ctx.api.connectorAnimate(animation, line, reversedLine).flowAppearance(lineWidth, lineColor, dashCap, dashWidth, dashGap).duration(duration).direction(flowDirection).play();\n } else {\n animateFlow.duration(duration).direction(flowDirection).play();\n }\n } else {\n if (animateFlow) {\n animateFlow.finish();\n }\n }\n}",
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nelement.attr({'stroke-width': ctx.properties.mainLineSize});",
"actions": null
},
{
@ -58,8 +58,8 @@
},
{
"id": "leftFlowDirection",
"name": "{i18n:scada.symbol.animation-direction}",
"hint": "{i18n:scada.symbol.animation-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": "{i18n:scada.symbol.left-connector}",
"type": "value",
"valueType": "BOOLEAN",
@ -174,8 +174,8 @@
},
{
"id": "topFlowDirection",
"name": "{i18n:scada.symbol.animation-direction}",
"hint": "{i18n:scada.symbol.animation-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": "{i18n:scada.symbol.top-connector}",
"type": "value",
"valueType": "BOOLEAN",
@ -290,8 +290,8 @@
},
{
"id": "rightFlowDirection",
"name": "{i18n:scada.symbol.flow-direction}",
"hint": "{i18n:scada.symbol.flow-direction-hint}",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": "{i18n:scada.symbol.right-connector}",
"type": "value",
"valueType": "BOOLEAN",
@ -367,36 +367,13 @@
}
],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"disabled": false,
"visible": true
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"min": 0,
"max": 99,
"step": 1,
"disabled": false,
"visible": true
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": false,
"fieldSuffix": "px",
"min": 0,
"max": 99,
@ -406,7 +383,7 @@
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"name": "{i18n:scada.symbol.line}",
"type": "color",
"default": "#1A1A1A",
"disabled": false,
@ -414,11 +391,11 @@
},
{
"id": "flowAnimationWidth",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 4,
"subLabel": "Width",
"divider": false,
"fieldSuffix": "px",
"min": 1,
"step": 1,
@ -427,8 +404,8 @@
},
{
"id": "flowAnimationColor",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "color",
"default": "#C8DFF7",
"disabled": false,
@ -436,13 +413,14 @@
},
{
"id": "flowStyleDash",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"required": true,
"subLabel": "{i18n:scada.symbol.dash}",
"divider": true,
"divider": false,
"fieldSuffix": "px",
"min": 0,
"step": 1,
@ -451,13 +429,14 @@
},
{
"id": "flowStyleGap",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"subLabel": "{i18n:scada.symbol.gap}",
"fieldSuffix": "px",
"min": 1,
"min": 0,
"step": 1,
"disabled": false,
"visible": true
@ -465,7 +444,7 @@
{
"id": "flowDashCap",
"name": "{i18n:scada.symbol.flow-dash-cap}",
"group": "{i18n:scada.symbol.animation}",
"group": "{i18n:scada.symbol.flow}",
"type": "select",
"default": "butt",
"items": [

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 16 KiB

115
application/src/main/data/json/system/scada_symbols/vertical-connector-hp.svg

@ -3,7 +3,7 @@
"description": "Vertical connector with an optional directional arrow to visually indicate flow.",
"widgetSizeX": 1,
"widgetSizeY": 1,
"stateRenderFunction": "const {\n flowAnimation,\n arrowDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst animation = ctx.tags.animationGroup[0];\nconst offset = Date.now() % 1000;\nconst duration = 1 / flowAnimationSpeed;\n\nconst prevFlowAnimation = animation.remember('flowAnimation');\nconst prevFlowDirection = animation.remember('flowDirection');\nconst prevFlowDuration = animation.remember('flowDuration');\n\nif (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n} else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n}\n\nfunction animateFlow(offset, flowDirection) {\n animation.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n animation.add(`<path style=\"stroke-dasharray: ${dashArray}; stroke-linecap: ${dashCap}; stroke-dashoffset: 0;\" d=\"${line}\" stroke-miterlimit=\"10\" fill=\"none\" stroke=\"${lineColor}\" stroke-width=\"${lineWidth}\"><animate attributeName=\"stroke-dashoffset\" values=\"${value};0\" dur=\"${duration}s\" begin=\"-${offset}ms\" calcMode=\"linear\" repeatCount=\"indefinite\" /></path>`);\n}\n",
"stateRenderFunction": "const {\n flowAnimation,\n arrowDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst lineReversed = 'M 100,0 V 200';\nconst animation = ctx.tags.animationGroup[0];\nconst duration = 1 / flowAnimationSpeed;\n\nlet animateFlow = ctx.api.connectorAnimation(animation);\n\nif (flowAnimation) {\n if (!animateFlow) {\n animateFlow = ctx.api.connectorAnimate(animation, line, lineReversed).flowAppearance(lineWidth, lineColor, dashCap, dashWidth, dashGap).duration(duration).direction(flowDirection).play();\n } else {\n animateFlow.duration(duration).direction(flowDirection).play();\n }\n} else {\n if (animateFlow) {\n animateFlow.finish();\n }\n}\n",
"tags": [
{
"tag": "arrow",
@ -12,7 +12,7 @@
},
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nelement.attr({'stroke-width': ctx.properties.mainLineSize});",
"actions": null
}
],
@ -53,18 +53,18 @@
"defaultWidgetActionSettings": null
},
{
"id": "arrowDirection",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"id": "flowAnimation",
"name": "{i18n:scada.symbol.flow-animation}",
"hint": "{i18n:scada.symbol.flow-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
@ -72,34 +72,38 @@
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowAnimation",
"name": "{i18n:scada.symbol.flow-animation}",
"hint": "{i18n:scada.symbol.flow-animation-hint}",
"id": "arrowDirection",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
@ -107,20 +111,16 @@
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
@ -166,36 +166,13 @@
}
],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"disabled": false,
"visible": true
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"min": 0,
"max": 99,
"step": 1,
"disabled": false,
"visible": true
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": false,
"fieldSuffix": "px",
"min": 0,
"max": 99,
@ -205,7 +182,7 @@
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"name": "{i18n:scada.symbol.line}",
"type": "color",
"default": "#1A1A1A",
"disabled": false,
@ -213,11 +190,11 @@
},
{
"id": "flowAnimationWidth",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 4,
"subLabel": "Width",
"divider": false,
"fieldSuffix": "px",
"min": 1,
"step": 1,
@ -226,20 +203,23 @@
},
{
"id": "flowAnimationColor",
"name": "{i18n:scada.symbol.flow}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line}",
"group": "{i18n:scada.symbol.flow}",
"type": "color",
"default": "#C8DFF7"
"default": "#C8DFF7",
"disabled": false,
"visible": true
},
{
"id": "flowStyleDash",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"required": true,
"subLabel": "{i18n:scada.symbol.dash}",
"divider": true,
"divider": false,
"fieldSuffix": "px",
"min": 0,
"step": 1,
@ -248,13 +228,14 @@
},
{
"id": "flowStyleGap",
"name": "{i18n:scada.symbol.flow-style}",
"group": "{i18n:scada.symbol.animation}",
"name": "{i18n:scada.symbol.flow-line-style}",
"hint": "{i18n:scada.symbol.flow-style-hint}",
"group": "{i18n:scada.symbol.flow}",
"type": "number",
"default": 10,
"subLabel": "{i18n:scada.symbol.gap}",
"fieldSuffix": "px",
"min": 1,
"min": 0,
"step": 1,
"disabled": false,
"visible": true
@ -262,7 +243,7 @@
{
"id": "flowDashCap",
"name": "{i18n:scada.symbol.flow-dash-cap}",
"group": "{i18n:scada.symbol.animation}",
"group": "{i18n:scada.symbol.flow}",
"type": "select",
"default": "butt",
"items": [
@ -284,5 +265,5 @@
}
]
}]]></tb:metadata>
<path d="M100 200L100 0" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/><path d="M100 71L129 129H71L100 71Z" fill="#1A1A1A" tb:tag="arrow"/><g tb:tag="animationGroup"/>
<path d="M 100,200 V 0" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/><path d="M100 71L129 129H71L100 71Z" fill="#1A1A1A" tb:tag="arrow"/><g tb:tag="animationGroup"/>
</svg>

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

168
ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts

@ -83,6 +83,10 @@ export interface ScadaSymbolApi {
cssAnimation: (element: Element) => ScadaSymbolAnimation | undefined;
resetCssAnimation: (element: Element) => void;
finishCssAnimation: (element: Element) => void;
connectorAnimation:(element: Element) => ConnectorScadaSymbolAnimation | undefined;
connectorAnimate:(element: Element, path: string, reversedPath: string) => ConnectorScadaSymbolAnimation;
resetConnectorAnimation: (element: Element) => void;
finishConnectorAnimation: (element: Element) => void;
disable: (element: Element | Element[]) => void;
enable: (element: Element | Element[]) => void;
callAction: (event: Event, behaviorId: string, value?: any, observer?: Partial<Observer<void>>) => void;
@ -186,6 +190,8 @@ const tbNamespaceRegex = /<svg.*(xmlns:tb="https:\/\/thingsboard.io\/svg").*>/gm
const tbTagRegex = /tb:tag="([^"]*)"/gms;
const syncTime = Date.now();
const generateElementId = () => {
const id = guid();
const firstChar = id.charAt(0);
@ -485,6 +491,7 @@ export class ScadaSymbolObject {
private settings: ScadaSymbolObjectSettings;
private context: ScadaSymbolContext;
private cssAnimations: CssScadaSymbolAnimations;
private connectorAnimations: ScadaSymbolFlowConnectorAnimations;
private svgShape: Svg;
private box: Box;
@ -604,6 +611,7 @@ export class ScadaSymbolObject {
private init() {
this.cssAnimations = new CssScadaSymbolAnimations(this.svgShape, this.raf);
this.connectorAnimations = new ScadaSymbolFlowConnectorAnimations();
this.context = {
api: {
generateElementId: () => generateElementId(),
@ -615,6 +623,10 @@ export class ScadaSymbolObject {
cssAnimation: this.cssAnimation.bind(this),
resetCssAnimation: this.resetCssAnimation.bind(this),
finishCssAnimation: this.finishCssAnimation.bind(this),
connectorAnimation: this.connectorAnimation.bind(this),
connectorAnimate: this.connectorAnimate.bind(this),
resetConnectorAnimation: this.resetConnectorAnimation.bind(this),
finishConnectorAnimation: this.finishConnectorAnimation.bind(this),
disable: this.disableElement.bind(this),
enable: this.enableElement.bind(this),
callAction: this.callAction.bind(this),
@ -959,6 +971,22 @@ export class ScadaSymbolObject {
this.cssAnimations.finishAnimation(element);
}
private connectorAnimate(element: Element, path: string, reversedPath: string): ConnectorScadaSymbolAnimation {
return this.connectorAnimations.animate(element, path, reversedPath);
}
private connectorAnimation(element: Element): ConnectorScadaSymbolAnimation | undefined {
return this.connectorAnimations.animation(element);
}
private resetConnectorAnimation(element: Element) {
this.connectorAnimations.resetAnimation(element);
}
private finishConnectorAnimation(element: Element) {
this.connectorAnimations.finishAnimation(element);
}
private disableElement(e: Element | Element[]) {
this.elements(e).forEach(element => {
element.attr({'pointer-events': 'none'});
@ -1108,6 +1136,20 @@ interface ScadaSymbolAnimation {
}
const scadaSymbolConnectorFlowAnimationId = 'scadaSymbolConnectorFlowAnimation';
type StrokeLineCap = 'butt' | 'round '| 'square';
interface ConnectorScadaSymbolAnimation {
play(): void;
stop(): void;
finish(): void;
flowAppearance(width: number, color: string, lineCap: StrokeLineCap, dashWidth: number, dashGap: number): ConnectorScadaSymbolAnimation;
duration(speed: number): ConnectorScadaSymbolAnimation;
direction(direction: boolean): ConnectorScadaSymbolAnimation;
}
class CssScadaSymbolAnimations {
constructor(private svgShape: Svg,
private raf: RafService) {}
@ -1159,6 +1201,132 @@ class CssScadaSymbolAnimations {
}
}
class ScadaSymbolFlowConnectorAnimations {
constructor() {}
public animate(element: Element, path = '', reversedPath = ''): ConnectorScadaSymbolAnimation {
this.checkOldAnimation(element);
return this.setupAnimation(element, this.createAnimation(element, path, reversedPath));
}
public animation(element: Element): ConnectorScadaSymbolAnimation | undefined {
return element.remember(scadaSymbolConnectorFlowAnimationId);
}
public resetAnimation(element: Element) {
const animation: ConnectorScadaSymbolAnimation = element.remember(scadaSymbolConnectorFlowAnimationId);
if (animation) {
animation.stop();
element.remember(scadaSymbolConnectorFlowAnimationId, null);
}
}
public finishAnimation(element: Element) {
const animation: ConnectorScadaSymbolAnimation = element.remember(scadaSymbolConnectorFlowAnimationId);
if (animation) {
animation.finish();
element.remember(scadaSymbolConnectorFlowAnimationId, null);
}
}
private setupAnimation(element: Element, animation: ConnectorScadaSymbolAnimation): ConnectorScadaSymbolAnimation {
element.remember(scadaSymbolConnectorFlowAnimationId, animation);
return animation;
}
private checkOldAnimation(element: Element) {
const previousAnimation: ConnectorScadaSymbolAnimation = element.remember(scadaSymbolConnectorFlowAnimationId);
if (previousAnimation) {
previousAnimation.finish();
}
}
private createAnimation(element: Element, path: string, reversedPath: string): ConnectorScadaSymbolAnimation {
return new FlowConnectorAnimation(element, path, reversedPath);
}
}
class FlowConnectorAnimation implements ConnectorScadaSymbolAnimation {
private readonly _path: string;
private readonly _reversedPath: string;
private readonly _animation: Element;
private _duration: number = 1;
private _lineColor: string = '#C8DFF7';
private _lineWidth: number = 4;
private _strokeLineCap: StrokeLineCap = 'butt';
private _dashWidth: number = 10;
private _dashGap: number = 10;
private _direction: boolean = true;
constructor(private element: Element,
path: string,
pathReversed: string) {
this._path = path;
this._reversedPath = pathReversed;
const dashArray = `${this._dashWidth} ${this._dashGap}`;
const values = `${this._dashWidth + this._dashGap};0`;
this._animation = SVG(
`<path d="${this._path}" stroke-dasharray="${dashArray}" stroke-linecap="${this._strokeLineCap}" fill="none" stroke="${this._lineColor}" stroke-width="${this._lineWidth}">` +
`<animate attributeName="stroke-dashoffset" values="${values}" dur="${this._duration}s" begin="indefinite" calcMode="linear" repeatCount="indefinite"></animate></path>`
);
}
public play() {
if (!this.element.node.childElementCount) {
this.element.add(this._animation);
}
const animateElement = this.element.node.getElementsByTagName('animate')[0];
const offset = ((Date.now() - syncTime) % 1000) * -1;
(animateElement as SVGAnimationElement).beginElementAt(offset);
}
public stop() {
const animateElement = this.element.node.getElementsByTagName('animate')[0];
(animateElement as SVGAnimationElement)?.endElement();
}
public finish() {
this.element.findOne('path')?.remove();
}
public flowAppearance(width: number, color: string, linecap: StrokeLineCap, dashWidth: number, dashGap: number): this {
const totalLength = (this._animation.node as SVGPathElement).getTotalLength();
let offset = 0;
if ((totalLength % 100) !== 0) {
const clientWidth = totalLength < 100 ? 100 : this.element.node.ownerSVGElement.clientWidth;
const clientWidthDash = clientWidth / (dashWidth + dashGap);
const totalLengthDash = totalLength / clientWidthDash;
offset = ((dashWidth + dashGap) - totalLengthDash) / 2;
}
this._lineColor = color;
this._lineWidth = width;
this._strokeLineCap = linecap;
this._dashWidth = dashWidth - offset;
this._dashGap = dashGap - offset;
const dashArray = `${this._dashWidth} ${this._dashGap}`;
const values = `${this._dashWidth + (this._dashGap || this._dashWidth)};0`;
this._animation.stroke({width, color, linecap, dasharray: dashArray});
this._animation.findOne('animate').attr('values', values);
return this;
}
public duration(speed: number): this {
this._duration = speed;
this._animation.findOne('animate').attr('dur', `${speed}s`);
return this;
}
public direction(direction: boolean): this {
this._direction = direction;
this._animation.attr('d', direction ? this._path : this._reversedPath);
return this;
}
}
interface ScadaSymbolAnimationKeyframe {
stop: string;
style: any;

6
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html

@ -30,6 +30,12 @@
<input required matInput formControlName="name" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
</div>
<div class="tb-form-row">
<div class="fixed-title-width" translate>scada.behavior.hint</div>
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="hint" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
</div>
<div class="tb-form-row">
<div class="fixed-title-width" translate>dynamic-form.property.group-title</div>
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">

1
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.ts

@ -104,6 +104,7 @@ export class DynamicFormPropertyPanelComponent implements OnInit {
{
id: [this.property.id, [Validators.required]],
name: [this.property.name, [Validators.required]],
hint: [this.property.hint, []],
group: [this.property.group, []],
type: [this.property.type, [Validators.required]],
arrayItemType: [this.property.arrayItemType, [Validators.required]],

10
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.html

@ -91,9 +91,15 @@
<div [formGroup]="propertiesFormGroup" class="tb-form-row space-between overflow-auto" [class]="propertyRow.rowClass">
<mat-slide-toggle *ngIf="propertyRow.switch && propertyRow.switch.visible"
class="mat-slide fixed-title-width margin" formControlName="{{ propertyRow.switch.id }}">
{{ propertyRow.label | customTranslate }}
<div tb-hint-tooltip-icon="{{ propertyRow.hint | customTranslate }}">
{{ propertyRow.label | customTranslate }}
</div>
</mat-slide-toggle>
<div *ngIf="!propertyRow.switch" class="fixed-title-width fixed-title-height">{{ propertyRow.label | customTranslate }}</div>
<div *ngIf="!propertyRow.switch" class="fixed-title-width fixed-title-height">
<div tb-hint-tooltip-icon="{{ propertyRow.hint | customTranslate }}">
{{ propertyRow.label | customTranslate }}
</div>
</div>
<div *ngIf="propertyRow.properties.length" class="tb-flex" [class]="propertyRow.propertiesRowClass">
<ng-container *ngFor="let property of propertyRow.properties">
<ng-container *ngIf="property.visible">

64
ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts

@ -1129,6 +1129,8 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags
const scadaSymbolAnimationLink = HelpLinks.linksMap.scadaSymbolDevAnimation;
const scadaSymbolAnimation = `<a href="${scadaSymbolAnimationLink}" target="_blank">ScadaSymbolAnimation</a>`;
const connectorScadaSymbolAnimationLink = HelpLinks.linksMap.scadaSymbolDevConnectorAnimation;
const connectorScadaSymbolAnimation = `<a href="${connectorScadaSymbolAnimationLink}" target="_blank">ConnectorScadaSymbolAnimation</a>`;
const properties: TbEditorCompletion = {
meta: 'object',
@ -1229,6 +1231,68 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags
},
]
},
connectorAnimate: {
meta: 'function',
description: 'Finishes any previous connector animation and starts a new connector animation for the SVG element along the specified path.',
args: [
{
name: 'element',
description: 'SVG element',
type: 'Element'
},
{
name: 'path',
description: 'Path defining the animation trajectory',
type: 'string'
},
{
name: 'reversedPath',
description: 'Path for the reversed animation trajectory',
type: 'string'
}
],
return: {
description: `Instance of ${connectorScadaSymbolAnimation} class with API to setup and control the connector animation.`,
type: connectorScadaSymbolAnimation
}
},
connectorAnimation: {
meta: 'function',
description: 'Gets the current connector animation applied to the SVG element.',
args: [
{
name: 'element',
description: 'SVG element',
type: 'Element'
}
],
return: {
description: `Instance of ${connectorScadaSymbolAnimation} class with API to setup and control the connector animation, or undefined if no animation is applied.`,
type: 'ConnectorScadaSymbolAnimation | undefined'
}
},
resetConnectorAnimation: {
meta: 'function',
description: 'Stops the connector animation if any and restores the SVG element to its initial state, removes the connector animation instance.',
args: [
{
name: 'element',
description: 'SVG element',
type: 'Element'
}
]
},
finishConnectorAnimation: {
meta: 'function',
description: 'Finishes the connector animation if any, updates the SVG element state according to the end animation values, removes the connector animation instance.',
args: [
{
name: 'element',
description: 'SVG element',
type: 'Element'
}
]
},
generateElementId: {
meta: 'function',
description: 'Generates new unique element id.',

1
ui-ngx/src/app/shared/models/constants.ts

@ -194,6 +194,7 @@ export const HelpLinks = {
scada: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/scada`,
scadaSymbolDev: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/scada/scada-symbols-dev-guide/`,
scadaSymbolDevAnimation: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/scada/scada-symbols-dev-guide/#scadasymbolanimation`,
scadaSymbolDevConnectorAnimation: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/scada/scada-symbols-dev-guide/#connectorscadasymbolanimation`,
domains: `${helpBaseUrl}/docs${docPlatformPrefix}/domains`,
mobileApplication: `${helpBaseUrl}/docs${docPlatformPrefix}/mobile-center/applications/`,
mobileBundle: `${helpBaseUrl}/docs${docPlatformPrefix}/mobile-center/mobile-center/`,

3
ui-ngx/src/app/shared/models/dynamic-form.models.ts

@ -87,6 +87,7 @@ export type PropertyConditionFunction = (property: FormProperty, model: any) =>
export interface FormPropertyBase {
id: string;
name: string;
hint?: string;
group?: string;
type: FormPropertyType;
default: any;
@ -237,6 +238,7 @@ export interface FormPropertyContainerBase {
}
export interface FormPropertyRow extends FormPropertyContainerBase {
hint?: string;
properties?: FormProperty[];
switch?: FormProperty;
rowClass?: string;
@ -362,6 +364,7 @@ const toPropertyContainers = (properties: FormProperty[],
if (!propertyRow) {
propertyRow = {
label: property.name,
hint: property.hint,
type: FormPropertyContainerType.row,
properties: [],
rowClass: property.rowClass,

14
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -3438,15 +3438,15 @@
"arrow-presence": "Arrow presence",
"arrow-presence-hint": "Indicates whether arrow is present in connector.",
"arrow-present": "Arrow present",
"arrow-direction": "Arrow/Animation direction",
"arrow-direction": "Flow direction",
"arrow-direction-hint": "Indicates flow direction.",
"animation-direction": "Flow animation direction",
"animation-direction-hint": "Indicates animation flow direction.",
"flow-animation": "Flow animation",
"flow-animation-hint": "Indicates whether animation is present in connector.",
"flow-animation": "Flow presence",
"flow-animation-hint": "Indicates whether fluid is flowing in connector.",
"flow": "Flow",
"flow-style": "Flow style",
"flow-dash-cap": "Flow dash cap",
"flow-line": "Line",
"flow-line-style": "Line style",
"flow-style-hint": "Set the Dash and Gap values so that their sum is divisible by 100 without a remainder for perfect animation synchronization.",
"flow-dash-cap": "Dash cap",
"dash-cap-butt": "Butt",
"dash-cap-round": "Round",
"dash-cap-square": "Square",

Loading…
Cancel
Save