verben-workflow-ui 0.5.66 → 0.5.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/src/lib/components/form-group/form-group.component.mjs +4 -4
- package/esm2022/src/lib/components/forms/forms.component.mjs +6 -6
- package/esm2022/src/lib/components/workflow-designer/designer-canvas/designer-canvas.component.mjs +3 -3
- package/esm2022/src/lib/components/workflow-designer/stage-node/stage-node.component.mjs +3 -3
- package/fesm2022/verben-workflow-ui-src-lib-components-form-group.mjs +3 -3
- package/fesm2022/verben-workflow-ui-src-lib-components-form-group.mjs.map +1 -1
- package/fesm2022/verben-workflow-ui-src-lib-components-forms.mjs +6 -6
- package/fesm2022/verben-workflow-ui-src-lib-components-forms.mjs.map +1 -1
- package/fesm2022/verben-workflow-ui-src-lib-components-workflow-designer.mjs +4 -4
- package/fesm2022/verben-workflow-ui-src-lib-components-workflow-designer.mjs.map +1 -1
- package/package.json +35 -35
|
@@ -1595,11 +1595,11 @@ class StageNodeComponent {
|
|
|
1595
1595
|
console.log('Stage properties updated:', stageData);
|
|
1596
1596
|
}
|
|
1597
1597
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: StageNodeComponent, deps: [{ token: WorkflowDesignerState }, { token: WorkflowDataService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1598
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: StageNodeComponent, selector: "svg:g[lib-stage-node]", inputs: { node: "node", isStartNode: "isStartNode", isSubflow: "isSubflow", stageData: "stageData" }, outputs: { stagePropertiesUpdated: "stagePropertiesUpdated", parallelExecutionToggled: "parallelExecutionToggled", showShieldDialog: "showShieldDialog", showEscalationDialog: "showEscalationDialog", showFormDialog: "showFormDialog" }, ngImport: i0, template: "<svg:g>\n <!-- Shape: Rectangle for stage, Hexagon for subflow -->\n @if (isSubflow) {\n <svg:path [attr.d]=\"\n 'M 1 ' +\n node.height / 4 +\n ' L ' +\n node.width / 2 +\n ' 1 L ' +\n (node.width - 1) +\n ' ' +\n node.height / 4 +\n ' V ' +\n (node.height * 3) / 4 +\n ' L ' +\n node.width / 2 +\n ' ' +\n (node.height - 1) +\n ' L 1 ' +\n (node.height * 3) / 4 +\n ' Z'\n \" fill=\"transparent\" stroke=\"#D36CFF\" stroke-width=\"1\"
|
|
1598
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: StageNodeComponent, selector: "svg:g[lib-stage-node]", inputs: { node: "node", isStartNode: "isStartNode", isSubflow: "isSubflow", stageData: "stageData" }, outputs: { stagePropertiesUpdated: "stagePropertiesUpdated", parallelExecutionToggled: "parallelExecutionToggled", showShieldDialog: "showShieldDialog", showEscalationDialog: "showEscalationDialog", showFormDialog: "showFormDialog" }, ngImport: i0, template: "<svg:g>\n <!-- Shape: Rectangle for stage, Hexagon for subflow -->\n @if (isSubflow) {\n <svg:path [attr.d]=\"\n 'M 1 ' +\n node.height / 4 +\n ' L ' +\n node.width / 2 +\n ' 1 L ' +\n (node.width - 1) +\n ' ' +\n node.height / 4 +\n ' V ' +\n (node.height * 3) / 4 +\n ' L ' +\n node.width / 2 +\n ' ' +\n (node.height - 1) +\n ' L 1 ' +\n (node.height * 3) / 4 +\n ' Z'\n \" fill=\"transparent\" stroke=\"#D36CFF\" stroke-width=\"1\"></svg:path>\n } @else {\n <svg:rect x=\"0\" y=\"0\" [attr.width]=\"node.width\" [attr.height]=\"node.height\" fill=\"transparent\" stroke=\"#D36CFF\"\n stroke-width=\"1\"></svg:rect>\n }\n\n <!-- Top-left icon: Stage form -->\n <svg:g (click)=\"toggleFormPopup($event)\" class=\"stage-icon\" transform=\"translate(6, 6)\">\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect x=\"-2\" y=\"-2\" width=\"24\" height=\"24\" fill=\"transparent\" style=\"cursor: pointer\" />\n\n <svg:path\n d=\"M16.5 20.475V17.475H13.5V16.475H16.5V13.475H17.5V16.475H20.5V17.475H17.5V20.475H16.5ZM3.5 17.5V16.5H4.5V17.5H3.5ZM6.5 17.5V16.5H11.517C11.5057 16.6767 11.5043 16.845 11.513 17.005C11.521 17.165 11.531 17.33 11.543 17.5H6.5ZM3.5 13.5V12.5H4.5V13.5H3.5ZM6.5 13.5V12.5H13.804C13.6127 12.6387 13.4333 12.7913 13.266 12.958C13.0993 13.1247 12.9377 13.3053 12.781 13.5H6.5ZM3.5 9.5V8.5H4.5V9.5H3.5ZM6.5 9.5V8.5H18.5V9.5H6.5ZM3.5 5.5V4.5H4.5V5.5H3.5ZM6.5 5.5V4.5H18.5V5.5H6.5Z\"\n [attr.fill]=\"node.stageData?.formId ? '#D36CFF' : 'black'\" />\n </svg:g>\n\n <!-- Top-right icon: Shield -->\n <svg:g (click)=\"toggleShieldPopup($event)\" class=\"stage-icon\"\n [attr.transform]=\"'translate(' + (node.width - 30) + ', 6)'\">\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect x=\"-2\" y=\"-2\" width=\"24\" height=\"24\" fill=\"transparent\" style=\"cursor: pointer\" />\n\n <svg:path d=\"M7.5 12H16.5V13.5H7.5V12ZM7.5 7.5H16.5V9H7.5V7.5Z\" fill=\"black\" />\n <svg:path\n d=\"M12 22.5L7.36801 20.0303C6.0474 19.3279 4.94303 18.2791 4.17348 16.9964C3.40393 15.7138 2.99825 14.2458 3.00001 12.75V3C3.00001 2.60218 3.15804 2.22064 3.43935 1.93934C3.72065 1.65804 4.10218 1.5 4.50001 1.5H19.5C19.8978 1.5 20.2794 1.65804 20.5607 1.93934C20.842 2.22064 21 2.60218 21 3V12.75C21.0018 14.2458 20.5961 15.7138 19.8265 16.9964C19.057 18.2791 17.9526 19.3279 16.632 20.0303L12 22.5ZM4.50001 3V12.75C4.49917 13.9738 4.83141 15.1747 5.46111 16.224C6.09082 17.2733 6.99423 18.1315 8.07451 18.7065L12 20.7997L15.9255 18.7073C17.0059 18.1322 17.9094 17.2739 18.5391 16.2244C19.1688 15.175 19.501 13.9739 19.5 12.75V3H4.50001Z\"\n fill=\"black\" />\n </svg:g>\n\n @if (node.stageData?.Code) {\n <!-- Left-center icon: Thunderbolt -->\n <svg:g (click)=\"toggleActionPopup('left', $event)\" class=\"stage-icon\" transform=\"translate(6, 38)\">\n <svg:path d=\"M11 15H6L13 1V9H18L11 23V15Z\" fill=\"black\" />\n </svg:g>\n\n <!-- Right-center icon: Thunderbolt -->\n <svg:g (click)=\"toggleActionPopup('right', $event)\" class=\"stage-icon\"\n [attr.transform]=\"'translate(' + (node.width - 30) + ', 38)'\">\n <svg:path d=\"M11 15H6L13 1V9H18L11 23V15Z\" fill=\"black\" />\n </svg:g>\n }\n\n <!-- Bottom-left icon: Double slash text -->\n <svg:g *ngIf=\"hasMultipleConnectedStages\" (click)=\"toggleCodePopup($event)\" class=\"stage-icon\"\n [attr.transform]=\"'translate(6, ' + (node.height - 16) + ')'\">\n <svg:text font-family=\"'Plus Jakarta Sans', sans-serif\" font-weight=\"500\" font-size=\"16px\"\n dominant-baseline=\"middle\" [attr.fill]=\"isParallelExecution ? '#D36CFF' : 'black'\">\n //\n </svg:text>\n </svg:g>\n\n <!-- Bottom-right icon: Timer -->\n <svg:g (click)=\"toggleTimerPopup($event)\" class=\"stage-icon\" [attr.transform]=\"\n 'translate(' + (node.width - 30) + ', ' + (node.height - 30) + ')'\n \">\n <svg:path\n d=\"M11.5 3C14.0196 3 16.4359 4.00089 18.2175 5.78249C19.9991 7.56408 21 9.98044 21 12.5C21 15.0196 19.9991 17.4359 18.2175 19.2175C16.4359 20.9991 14.0196 22 11.5 22C8.98044 22 6.56408 20.9991 4.78249 19.2175C3.00089 17.4359 2 15.0196 2 12.5C2 9.98044 3.00089 7.56408 4.78249 5.78249C6.56408 4.00089 8.98044 3 11.5 3ZM11.5 4C9.24566 4 7.08365 4.89553 5.48959 6.48959C3.89553 8.08365 3 10.2457 3 12.5C3 14.7543 3.89553 16.9163 5.48959 18.5104C7.08365 20.1045 9.24566 21 11.5 21C12.6162 21 13.7215 20.7801 14.7528 20.353C15.7841 19.9258 16.7211 19.2997 17.5104 18.5104C18.2997 17.7211 18.9258 16.7841 19.353 15.7528C19.7801 14.7215 20 13.6162 20 12.5C20 10.2457 19.1045 8.08365 17.5104 6.48959C15.9163 4.89553 13.7543 4 11.5 4ZM11 7H12V12.42L16.7 15.13L16.2 16L11 13V7Z\"\n fill=\"black\" />\n </svg:g>\n\n <!-- Label in the center -->\n <svg:text [attr.x]=\"node.width / 2\" [attr.y]=\"node.height / 2\" text-anchor=\"middle\" dominant-baseline=\"middle\"\n [attr.font-size]=\"isSubflow ? '10' : '14'\" fill=\"#000000\">\n {{ truncatedDisplayName }}\n </svg:text>\n</svg:g>\n\n<!-- <lib-stage-dialog\n [visible]=\"showShieldPopup()\"\n [stageData]=\"node.stageData || {}\"\n (closed)=\"showShieldPopup.set(false)\"\n (saved)=\"onStagePropertiesSaved($event)\"\n></lib-stage-dialog> -->\n", styles: [":host{pointer-events:none}.stage-icon{cursor:pointer;pointer-events:all}.stage-icon:hover path{fill:#d36cff}\n"], dependencies: [{ kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
1599
1599
|
}
|
|
1600
1600
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: StageNodeComponent, decorators: [{
|
|
1601
1601
|
type: Component,
|
|
1602
|
-
args: [{ selector: 'svg:g[lib-stage-node]', template: "<svg:g>\n <!-- Shape: Rectangle for stage, Hexagon for subflow -->\n @if (isSubflow) {\n <svg:path [attr.d]=\"\n 'M 1 ' +\n node.height / 4 +\n ' L ' +\n node.width / 2 +\n ' 1 L ' +\n (node.width - 1) +\n ' ' +\n node.height / 4 +\n ' V ' +\n (node.height * 3) / 4 +\n ' L ' +\n node.width / 2 +\n ' ' +\n (node.height - 1) +\n ' L 1 ' +\n (node.height * 3) / 4 +\n ' Z'\n \" fill=\"transparent\" stroke=\"#D36CFF\" stroke-width=\"1\"
|
|
1602
|
+
args: [{ selector: 'svg:g[lib-stage-node]', template: "<svg:g>\n <!-- Shape: Rectangle for stage, Hexagon for subflow -->\n @if (isSubflow) {\n <svg:path [attr.d]=\"\n 'M 1 ' +\n node.height / 4 +\n ' L ' +\n node.width / 2 +\n ' 1 L ' +\n (node.width - 1) +\n ' ' +\n node.height / 4 +\n ' V ' +\n (node.height * 3) / 4 +\n ' L ' +\n node.width / 2 +\n ' ' +\n (node.height - 1) +\n ' L 1 ' +\n (node.height * 3) / 4 +\n ' Z'\n \" fill=\"transparent\" stroke=\"#D36CFF\" stroke-width=\"1\"></svg:path>\n } @else {\n <svg:rect x=\"0\" y=\"0\" [attr.width]=\"node.width\" [attr.height]=\"node.height\" fill=\"transparent\" stroke=\"#D36CFF\"\n stroke-width=\"1\"></svg:rect>\n }\n\n <!-- Top-left icon: Stage form -->\n <svg:g (click)=\"toggleFormPopup($event)\" class=\"stage-icon\" transform=\"translate(6, 6)\">\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect x=\"-2\" y=\"-2\" width=\"24\" height=\"24\" fill=\"transparent\" style=\"cursor: pointer\" />\n\n <svg:path\n d=\"M16.5 20.475V17.475H13.5V16.475H16.5V13.475H17.5V16.475H20.5V17.475H17.5V20.475H16.5ZM3.5 17.5V16.5H4.5V17.5H3.5ZM6.5 17.5V16.5H11.517C11.5057 16.6767 11.5043 16.845 11.513 17.005C11.521 17.165 11.531 17.33 11.543 17.5H6.5ZM3.5 13.5V12.5H4.5V13.5H3.5ZM6.5 13.5V12.5H13.804C13.6127 12.6387 13.4333 12.7913 13.266 12.958C13.0993 13.1247 12.9377 13.3053 12.781 13.5H6.5ZM3.5 9.5V8.5H4.5V9.5H3.5ZM6.5 9.5V8.5H18.5V9.5H6.5ZM3.5 5.5V4.5H4.5V5.5H3.5ZM6.5 5.5V4.5H18.5V5.5H6.5Z\"\n [attr.fill]=\"node.stageData?.formId ? '#D36CFF' : 'black'\" />\n </svg:g>\n\n <!-- Top-right icon: Shield -->\n <svg:g (click)=\"toggleShieldPopup($event)\" class=\"stage-icon\"\n [attr.transform]=\"'translate(' + (node.width - 30) + ', 6)'\">\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect x=\"-2\" y=\"-2\" width=\"24\" height=\"24\" fill=\"transparent\" style=\"cursor: pointer\" />\n\n <svg:path d=\"M7.5 12H16.5V13.5H7.5V12ZM7.5 7.5H16.5V9H7.5V7.5Z\" fill=\"black\" />\n <svg:path\n d=\"M12 22.5L7.36801 20.0303C6.0474 19.3279 4.94303 18.2791 4.17348 16.9964C3.40393 15.7138 2.99825 14.2458 3.00001 12.75V3C3.00001 2.60218 3.15804 2.22064 3.43935 1.93934C3.72065 1.65804 4.10218 1.5 4.50001 1.5H19.5C19.8978 1.5 20.2794 1.65804 20.5607 1.93934C20.842 2.22064 21 2.60218 21 3V12.75C21.0018 14.2458 20.5961 15.7138 19.8265 16.9964C19.057 18.2791 17.9526 19.3279 16.632 20.0303L12 22.5ZM4.50001 3V12.75C4.49917 13.9738 4.83141 15.1747 5.46111 16.224C6.09082 17.2733 6.99423 18.1315 8.07451 18.7065L12 20.7997L15.9255 18.7073C17.0059 18.1322 17.9094 17.2739 18.5391 16.2244C19.1688 15.175 19.501 13.9739 19.5 12.75V3H4.50001Z\"\n fill=\"black\" />\n </svg:g>\n\n @if (node.stageData?.Code) {\n <!-- Left-center icon: Thunderbolt -->\n <svg:g (click)=\"toggleActionPopup('left', $event)\" class=\"stage-icon\" transform=\"translate(6, 38)\">\n <svg:path d=\"M11 15H6L13 1V9H18L11 23V15Z\" fill=\"black\" />\n </svg:g>\n\n <!-- Right-center icon: Thunderbolt -->\n <svg:g (click)=\"toggleActionPopup('right', $event)\" class=\"stage-icon\"\n [attr.transform]=\"'translate(' + (node.width - 30) + ', 38)'\">\n <svg:path d=\"M11 15H6L13 1V9H18L11 23V15Z\" fill=\"black\" />\n </svg:g>\n }\n\n <!-- Bottom-left icon: Double slash text -->\n <svg:g *ngIf=\"hasMultipleConnectedStages\" (click)=\"toggleCodePopup($event)\" class=\"stage-icon\"\n [attr.transform]=\"'translate(6, ' + (node.height - 16) + ')'\">\n <svg:text font-family=\"'Plus Jakarta Sans', sans-serif\" font-weight=\"500\" font-size=\"16px\"\n dominant-baseline=\"middle\" [attr.fill]=\"isParallelExecution ? '#D36CFF' : 'black'\">\n //\n </svg:text>\n </svg:g>\n\n <!-- Bottom-right icon: Timer -->\n <svg:g (click)=\"toggleTimerPopup($event)\" class=\"stage-icon\" [attr.transform]=\"\n 'translate(' + (node.width - 30) + ', ' + (node.height - 30) + ')'\n \">\n <svg:path\n d=\"M11.5 3C14.0196 3 16.4359 4.00089 18.2175 5.78249C19.9991 7.56408 21 9.98044 21 12.5C21 15.0196 19.9991 17.4359 18.2175 19.2175C16.4359 20.9991 14.0196 22 11.5 22C8.98044 22 6.56408 20.9991 4.78249 19.2175C3.00089 17.4359 2 15.0196 2 12.5C2 9.98044 3.00089 7.56408 4.78249 5.78249C6.56408 4.00089 8.98044 3 11.5 3ZM11.5 4C9.24566 4 7.08365 4.89553 5.48959 6.48959C3.89553 8.08365 3 10.2457 3 12.5C3 14.7543 3.89553 16.9163 5.48959 18.5104C7.08365 20.1045 9.24566 21 11.5 21C12.6162 21 13.7215 20.7801 14.7528 20.353C15.7841 19.9258 16.7211 19.2997 17.5104 18.5104C18.2997 17.7211 18.9258 16.7841 19.353 15.7528C19.7801 14.7215 20 13.6162 20 12.5C20 10.2457 19.1045 8.08365 17.5104 6.48959C15.9163 4.89553 13.7543 4 11.5 4ZM11 7H12V12.42L16.7 15.13L16.2 16L11 13V7Z\"\n fill=\"black\" />\n </svg:g>\n\n <!-- Label in the center -->\n <svg:text [attr.x]=\"node.width / 2\" [attr.y]=\"node.height / 2\" text-anchor=\"middle\" dominant-baseline=\"middle\"\n [attr.font-size]=\"isSubflow ? '10' : '14'\" fill=\"#000000\">\n {{ truncatedDisplayName }}\n </svg:text>\n</svg:g>\n\n<!-- <lib-stage-dialog\n [visible]=\"showShieldPopup()\"\n [stageData]=\"node.stageData || {}\"\n (closed)=\"showShieldPopup.set(false)\"\n (saved)=\"onStagePropertiesSaved($event)\"\n></lib-stage-dialog> -->\n", styles: [":host{pointer-events:none}.stage-icon{cursor:pointer;pointer-events:all}.stage-icon:hover path{fill:#d36cff}\n"] }]
|
|
1603
1603
|
}], ctorParameters: () => [{ type: WorkflowDesignerState }, { type: WorkflowDataService }], propDecorators: { node: [{
|
|
1604
1604
|
type: Input
|
|
1605
1605
|
}], isStartNode: [{
|
|
@@ -3628,11 +3628,11 @@ class DesignerCanvasComponent {
|
|
|
3628
3628
|
});
|
|
3629
3629
|
}
|
|
3630
3630
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DesignerCanvasComponent, deps: [{ token: WorkflowDesignerState }, { token: WorkflowDataService }], target: i0.ɵɵFactoryTarget.Component });
|
|
3631
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: DesignerCanvasComponent, selector: "lib-designer-canvas", inputs: { selectedTool: "selectedTool" }, outputs: { clickedPosition: "clickedPosition", subflowSelected: "subflowSelected", stagePropertiesUpdated: "stagePropertiesUpdated", actionPropertiesUpdated: "actionPropertiesUpdated" }, host: { listeners: { "window:mouseup": "onWindowMouseUp($event)" } }, viewQueries: [{ propertyName: "canvasRef", first: true, predicate: ["canvas"], descendants: true, static: true }], ngImport: i0, template: "<div class=\"canvas-container\">\n <svg #canvas [attr.width]=\"canvasWidth\" [attr.height]=\"canvasHeight\" class=\"designer-canvas\"\n (click)=\"onCanvasClick($event)\">\n <defs>\n <!-- Grid pattern definition -->\n\n <pattern id=\"grid\" [attr.width]=\"gridSize\" [attr.height]=\"gridSize\" patternUnits=\"userSpaceOnUse\">\n <path d=\"M 20 0 L 0 0 0 20\" fill=\"none\" stroke=\"#e2e8f0\" stroke-width=\"0.5\" />\n </pattern>\n\n <!-- Arrow head marker definition -->\n <marker id=\"arrowhead\" markerWidth=\"10\" markerHeight=\"7\" refX=\"9\" refY=\"3.5\" orient=\"auto\">\n <polygon points=\"0 0, 10 3.5, 0 7\" fill=\"#D36CFF\" />\n </marker>\n\n <!-- Connection point styles -->\n <circle id=\"connection-point-template\" r=\"5\" fill=\"#D36CFF\" stroke=\"#FFFFFF\" stroke-width=\"1\" />\n\n <!-- Dashed line style for connection preview -->\n <pattern id=\"dashed-line\" width=\"10\" height=\"10\" patternUnits=\"userSpaceOnUse\">\n <line x1=\"0\" y1=\"5\" x2=\"10\" y2=\"5\" stroke=\"#D36CFF\" stroke-width=\"2\" stroke-dasharray=\"5,5\" />\n </pattern>\n\n @for (swimlane of state.swimlanes; track swimlane.order) {\n <clipPath [attr.id]=\"'swimlane-clip-' + swimlane.order\">\n <rect x=\"0\" y=\"0\" [attr.width]=\"canvasWidth\" height=\"263\" />\n </clipPath>\n }\n </defs>\n\n <!-- Background grid -->\n <rect width=\"100%\" height=\"100%\" fill=\"url(#grid)\" />\n\n <!-- Display a message when no swimlanes exist -->\n @if (state.swimlanes.length === 0) {\n <text x=\"50%\" y=\"50%\" font-family=\"sans-serif\" font-size=\"16\" fill=\"#94a3b8\" text-anchor=\"middle\">\n Select the Swimlane tool and click on the canvas to add a swimlane\n </text>\n }\n\n <!-- This is where workflow elements will be added later -->\n @for (swimlane of state.swimlanes; track swimlane.order) {\n <!-- <g\n [attr.transform]=\"'translate(0,' + swimlane.order * 263 + ')'\"\n [attr.clip-path]=\"'url(#swimlane-clip-' + swimlane.order + ')'\"\n >\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"canvasWidth\"\n [attr.height]=\"263\"\n fill=\"#ffffff\"\n stroke=\"#e2e8f0\"\n stroke-width=\"1\"\n ></rect>\n\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"canvasWidth\"\n height=\"40\"\n fill=\"#f8fafc\"\n stroke=\"#e2e8f0\"\n stroke-width=\"1\"\n ></rect>\n\n <text\n x=\"20\"\n y=\"25\"\n font-family=\"sans-serif\"\n font-size=\"14\"\n fill=\"#333333\"\n font-weight=\"bold\"\n >\n {{ swimlane.label }}\n </text>\n\n <g\n class=\"edit-swimlane-button\"\n [attr.transform]=\"'translate(100, 20)'\"\n (click)=\"\n onEditSwimlane($event, swimlane, swimlane.order);\n $event.stopPropagation()\n \"\n >\n <rect\n x=\"-5\"\n y=\"-15\"\n width=\"40\"\n height=\"20\"\n fill=\"#f3e8ff\"\n rx=\"3\"\n ry=\"3\"\n stroke=\"#d8b4fe\"\n stroke-width=\"1\"\n ></rect>\n <text\n x=\"15\"\n y=\"0\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#7e22ce\"\n text-anchor=\"middle\"\n >\n Edit\n </text>\n </g>\n\n <g [attr.transform]=\"'translate(200, 20)'\">\n @for (tag of swimlane.tags.slice(0, 3); track tag.Name; let i = $index)\n {\n <text\n [attr.x]=\"i * 100\"\n y=\"5\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#666666\"\n >\n {{ tag.Name }}\n </text>\n } @if (swimlane.tags.length > 3) {\n <text\n [attr.x]=\"3 * 100 + 10\"\n y=\"5\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#666666\"\n >\n +{{ swimlane.tags.length - 3 }} more\n </text>\n }\n </g>\n </g> -->\n\n <g [attr.transform]=\"'translate(0,' + swimlane.order * 263 + ')'\">\n <!-- Swimlane container - remove stroke to make it purely background -->\n <rect x=\"0\" y=\"0\" [attr.width]=\"canvasWidth\" [attr.height]=\"263\"\n [attr.fill]=\"swimlane.order % 2 === 0 ? '#FCF3FF' : '#ECFFF5'\" stroke=\"none\"></rect>\n\n <!-- Vertical swimlane label on the left -->\n <g class=\"swimlane-label\">\n <!-- Purple background for the label -->\n <rect x=\"0.5\" y=\"0.5\" width=\"43\" height=\"215\" rx=\"10.5\" fill=\"white\" stroke=\"#D36CFF\"></rect>\n\n <!-- Plus button at the top -->\n <g (click)=\"\n onEditSwimlane($event, swimlane, swimlane.order);\n $event.stopPropagation()\n \">\n <rect x=\"12.5\" y=\"10\" width=\"18\" height=\"18\" fill=\"transparent\" style=\"cursor: pointer\"></rect>\n\n <path d=\"M22.52 12.174V17.08H27.47V18.51H22.52V23.394H21.2V18.51H16.25V17.08H21.2V12.174H22.52Z\"\n fill=\"#0000FF\" class=\"plus-icon\" style=\"cursor: pointer\"></path>\n </g>\n\n <!-- Swimlane label -->\n <text x=\"22\" y=\"106\" font-family=\"sans-serif\" font-size=\"14\" fill=\"#333333\" text-anchor=\"middle\"\n transform=\"rotate(-90, 22, 100)\">\n {{\n swimlane.label.length > 12\n ? swimlane.label.substring(0, 10) + \"...\"\n : swimlane.label\n }}\n </text>\n\n <!-- Tag count indicator at the bottom -->\n <rect x=\"11.5\" y=\"187\" width=\"21\" height=\"19\" rx=\"9.5\" fill=\"#9E9E9E\"></rect>\n <text x=\"24\" y=\"206\" font-family=\"sans-serif\" font-size=\"12\" fill=\"black\" text-anchor=\"middle\"\n transform=\"rotate(-90, 22, 200)\">\n {{ swimlane.tags.length }}\n </text>\n </g>\n </g>\n\n }\n\n <!-- Nodes -->\n @for (swimlane of state.swimlanes; track swimlane.order) {\n <!-- Render nodes in this swimlane -->\n @for (node of swimlane.nodes; track node.id) {\n <g class=\"node-element\" [attr.transform]=\"\n 'translate(' + node.x + ',' + (node.y + swimlane.order * 263) + ')'\"\n (mouseenter)=\"showConnectionPoints(node.id, swimlane.order)\"\n (mouseleave)=\"hideConnectionPoints(node.id)\">\n <!-- Start node indicator (circle and arrow) for the first node -->\n @if (node.isStartNode) {\n <!-- Circle -->\n <circle cx=\"-50\" cy=\"50\" r=\"30\" fill=\"none\" stroke=\"#D36CFF\" stroke-width=\"1\"></circle>\n <!-- Form icon for start node -->\n <svg:g (click)=\"\n toggleStartNodeFormPopup($event, node.x, node.y, swimlane.order)\n \" class=\"stage-icon\" transform=\"translate(-45, 42)\" style=\"cursor: pointer; pointer-events: all\">\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect x=\"-2\" y=\"-2\" width=\"24\" height=\"24\" fill=\"transparent\" style=\"cursor: pointer\" />\n <svg:path\n d=\"M16.5 20.475V17.475H13.5V16.475H16.5V13.475H17.5V16.475H20.5V17.475H17.5V20.475H16.5ZM3.5 17.5V16.5H4.5V17.5H3.5ZM6.5 17.5V16.5H11.517C11.5057 16.6767 11.5043 16.845 11.513 17.005C11.521 17.165 11.531 17.33 11.543 17.5H6.5ZM3.5 13.5V12.5H4.5V13.5H3.5ZM6.5 13.5V12.5H13.804C13.6127 12.6387 13.4333 12.7913 13.266 12.958C13.0993 13.1247 12.9377 13.3053 12.781 13.5H6.5ZM3.5 9.5V8.5H4.5V9.5H3.5ZM6.5 9.5V8.5H18.5V9.5H6.5ZM3.5 5.5V4.5H4.5V5.5H3.5ZM6.5 5.5V4.5H18.5V5.5H6.5Z\"\n [attr.fill]=\"state.workflowFormId ? '#D36CFF' : 'black'\" transform=\"scale(0.7)\" />\n </svg:g>\n <!-- Arrow from circle to node -->\n <path d=\"M -20 50 L 0 50\" stroke=\"#D36CFF\" stroke-width=\"1\" marker-end=\"url(#arrowhead)\"></path>\n }\n\n <!-- Exit point indicator (circle and arrow) -->\n @if (node.stageData?.IsExitPoint) {\n <!-- Arrow from node to circle -->\n <path [attr.d]=\"'M ' + node.width + ' 50 L ' + (node.width + 20) + ' 50'\" stroke=\"#D36CFF\" stroke-width=\"1\"\n marker-end=\"url(#arrowhead)\"></path>\n <!-- Circle -->\n <circle [attr.cx]=\"node.width + 50\" cy=\"50\" r=\"30\" fill=\"none\" stroke=\"#D36CFF\" stroke-width=\"2\"></circle>\n }\n\n <!-- Stage node -->\n @if (node.type === 'stage') {\n <!-- <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></rect> -->\n <svg:g lib-stage-node [node]=\"node\" [isStartNode]=\"node.isStartNode\"\n (stagePropertiesUpdated)=\"onStagePropertiesUpdated($event)\" (showShieldDialog)=\"onShowShieldDialog($event)\"\n (showEscalationDialog)=\"onShowEscalationDialog($event)\" (showFormDialog)=\"\n toggleStartNodeFormPopup($event, node.x, node.y, swimlane.order, node)\n \"></svg:g>\n }\n\n <!-- Decision node -->\n @if (node.type === 'decision') {\n <path [attr.d]=\"\n 'M 0 ' +\n node.height / 2 +\n ' L ' +\n node.width / 2 +\n ' 0' +\n ' L ' +\n node.width +\n ' ' +\n node.height / 2 +\n ' L ' +\n node.width / 2 +\n ' ' +\n node.height +\n ' Z'\n \" fill=\"none\" stroke=\"#D36CFF\" stroke-width=\"1\"></path>\n\n <!-- Decision node condition indicator icon -->\n <svg:g (click)=\"showDecisionConditionsPopup($event, node, swimlane.order)\" class=\"decision-condition-icon\"\n [attr.transform]=\"\n 'translate(' +\n (node.width / 2 - 10) +\n ',' +\n (node.height / 2 - 10) +\n ')'\n \">\n <svg:rect x=\"-5\" y=\"-5\" width=\"30\" height=\"30\" fill=\"transparent\" style=\"cursor: pointer\" />\n <svg:path d=\"M10 0H20V2H10V0ZM10 8H20V10H10V8ZM10 4H20V6H10V4ZM0 0H8V2H0V0ZM0 8H8V10H0V8ZM0 4H8V6H0V4Z\"\n fill=\"#D36CFF\" />\n </svg:g>\n }\n\n <!-- Form node -->\n @if (node.type === 'form') {\n <path\n d=\"M95.0625 50.5591V95.0625C95.0625 97.9716 93.9069 100.762 91.8498 102.819C89.7928 104.876 87.0028 106.031 84.0938 106.031H32.9062C29.9972 106.031 27.2072 104.876 25.1502 102.819C23.0931 100.762 21.9375 97.9716 21.9375 95.0625V21.9375C21.9375 19.0284 23.0931 16.2385 25.1502 14.1814C27.2072 12.1244 29.9972 10.9688 32.9062 10.9688H55.4722C57.4109 10.969 59.2701 11.7392 60.6412 13.1099L92.9213 45.3901C94.292 46.7611 95.0622 48.6204 95.0625 50.5591Z\"\n transform=\"scale(0.7)\" fill=\"none\" stroke=\"#D36CFF\" stroke-linejoin=\"round\" />\n <path\n d=\"M58.5 12.7969V40.2188C58.5 42.1581 59.2704 44.0181 60.6418 45.3895C62.0131 46.7608 63.8731 47.5312 65.8125 47.5312H93.2344\"\n transform=\"scale(0.7)\" stroke=\"#D36CFF\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n <text x=\"50%\" y=\"50%\" text-anchor=\"middle\" dominant-baseline=\"middle\" font-family=\"sans-serif\" font-size=\"14\"\n fill=\"#D36CFF\">\n Form\n </text>\n }\n\n <!-- Subflow node -->\n @if (node.type === 'subflow') {\n <svg:g lib-stage-node [node]=\"node\" [isStartNode]=\"node.isStartNode\" [isSubflow]=\"true\"\n (stagePropertiesUpdated)=\"onStagePropertiesUpdated($event)\" (showShieldDialog)=\"onShowShieldDialog($event)\"\n (showEscalationDialog)=\"onShowEscalationDialog($event)\" (showFormDialog)=\"\n toggleStartNodeFormPopup($event, node.x, node.y, swimlane.order, node)\n \"></svg:g>\n }\n\n <!-- Inner drag-handle: inset 12px so the node edge ring stays drag-free.\n Connection point <use> elements are rendered after this so they are\n topmost in SVG z-order and win pointer events at the node edges. -->\n <rect class=\"node-drag-handle\"\n [attr.x]=\"12\" [attr.y]=\"12\"\n [attr.width]=\"node.width - 24\" [attr.height]=\"node.height - 24\"\n fill=\"transparent\"\n [style.cursor]=\"getCursorForNode(node.id)\"\n (mousedown)=\"onNodeMouseDown($event, node, swimlane.order)\" />\n\n <!-- Connection points for this node (rendered last = topmost) -->\n @for (point of node.connectionPoints || []; track point.id) {\n @if (isConnectionPointVisible(node.id)) {\n <use [attr.href]=\"'#connection-point-template'\" [attr.x]=\"point.x\" [attr.y]=\"point.y\" [attr.id]=\"point.id\"\n (mousedown)=\"startConnectionDrag($event, point, swimlane.order)\" />\n }\n }\n </g>\n <!-- A transparent hover area for improved hover detection -->\n <rect [attr.x]=\"node.x - 10\" [attr.y]=\"node.y - 10\" [attr.width]=\"node.width + 20\" [attr.height]=\"node.height + 20\"\n fill=\"transparent\" (mouseover)=\"showConnectionPoints(node.id, swimlane.order)\"\n (mouseout)=\"hideConnectionPoints(node.id)\" style=\"pointer-events: none\" />\n } }\n\n <!-- Connections -->\n @for (connection of state.connections; track connection.id) {\n <g class=\"connection-element\">\n <path [attr.d]=\"getConnectionPathForSavedConnection(connection)\" stroke=\"#D36CFF\" stroke-width=\"2\" fill=\"none\"\n marker-end=\"url(#arrowhead)\"></path>\n\n <!-- Connection Label -->\n @if (connection.sourceNodeId && connection.targetNodeId) {\n <foreignObject [attr.x]=\"getLabelPosition(connection).x\" [attr.y]=\"getLabelPosition(connection).y\" width=\"150\"\n height=\"40\" style=\"overflow: visible; pointer-events: all;\" [attr.transform]=\"'translate(-75, -10)'\">\n <wf-label-editor [label]=\"connection.label || 'New Action'\" [x]=\"0\" [y]=\"0\" [isConnection]=\"true\"\n (labelChange)=\"saveConnectionLabel(connection, $event)\" (openDialog)=\"onShowActionDialog(connection)\" />\n </foreignObject>\n }\n </g>\n }\n\n <!-- Connection preview line -->\n @if (state.isConnectionDragging()) {\n <g>\n <path [attr.d]=\"getConnectionPath()\" stroke=\"#D36CFF\" stroke-width=\"2\" stroke-dasharray=\"5,5\" fill=\"none\"></path>\n </g>\n }\n </svg>\n\n <div *ngIf=\"popupVisible\" [style.position]=\"'absolute'\" [style.left.px]=\"popupX\" [style.top.px]=\"popupY\">\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '99' }\" [enableMouseLeave]=\"false\"\n (dropdownOpenChange)=\"$event ? null : hideConnectionPopup()\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div class=\"p-3 bg-white border border-gray-200 rounded shadow-md\" dropdown-content>\n <h4 class=\"mb-2 font-medium\">Create Connection</h4>\n <div class=\"flex flex-col gap-2\">\n <ng-container *ngFor=\"let type of allowedNodeTypes\">\n <button class=\"px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50\"\n (click)=\"createNodeConnection(type)\">\n Create {{ type | titlecase }}\n </button>\n </ng-container>\n <button class=\"px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50\"\n (click)=\"hideConnectionPopup()\">\n Cancel\n </button>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <div *ngIf=\"showStartNodeFormPopup\" [style.position]=\"'absolute'\" [style.left.px]=\"startNodeFormPopupX\"\n [style.top.px]=\"startNodeFormPopupY\">\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\" dropdown-content\n style=\"background-color: white; z-index: 1000\">\n <h4 class=\"mb-2 font-medium\">Select Workflow Form</h4>\n <div *ngIf=\"isLoadingStartNodeForms\" class=\"text-center py-2\">\n Loading forms...\n </div>\n <div *ngIf=\"!isLoadingStartNodeForms\" class=\"max-h-48 overflow-y-auto\">\n <div class=\"mb-3\">\n <button\n class=\"w-full px-3 py-2 bg-gray-50 border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectStartNodeForm(null)\">\n Clear form selection\n </button>\n </div>\n <div *ngFor=\"let form of startNodeFormsList\" class=\"mb-1\">\n <button class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n [ngClass]=\"{ 'bg-blue-50 !border-blue-500 !border-2 font-semibold': form?.Code === activeNodeForFormSelection?.stageData?.formId}\"\n (click)=\"selectStartNodeForm(form)\">\n {{ form.Name }}\n </button>\n </div>\n <div *ngIf=\"startNodeFormsList.length === 0\" class=\"text-center py-2 text-gray-500\">\n No forms available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <div *ngIf=\"showSubflowPopup\" [style.position]=\"'absolute'\" [style.left.px]=\"subflowPopupX\"\n [style.top.px]=\"subflowPopupY\">\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\" dropdown-content\n style=\"background-color: white; z-index: 1000\">\n <h4 class=\"mb-2 font-medium\">Select Workflow</h4>\n <div *ngIf=\"isLoadingWorkflows\" class=\"text-center py-2\">\n Loading workflows...\n </div>\n <div *ngIf=\"!isLoadingWorkflows\" class=\"max-h-48 overflow-y-auto\" style=\"max-height: 192px; overflow-y: auto;\">\n <div *ngFor=\"let workflow of workflowsList\" class=\"mb-1\">\n <button class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectSubflowWorkflow(workflow)\">\n {{ workflow.Name }}\n </button>\n </div>\n <div *ngIf=\"workflowsList.length === 0\" class=\"text-center py-2 text-gray-500\">\n No workflows available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <lib-conditions-popup [visible]=\"showDecisionConditionDialog\" [decisionNodeId]=\"activeDecisionNodeId\"\n [newConnectionId]=\"activeConnectionId\" [popupX]=\"decisionConditionPopupX\" [popupY]=\"decisionConditionPopupY\"\n (closed)=\"onDecisionConditionCancelled()\" (saved)=\"onDecisionConditionSaved($event)\"></lib-conditions-popup>\n\n <!-- <lib-decision-popup\n [visible]=\"showConditionsDialog\"\n [decisionNodeId]=\"activeDecisionNodeId\"\n [popupX]=\"decisionConditionPopupX\"\n [popupY]=\"decisionConditionPopupY\"\n (closed)=\"onDecisionConditionCancelled()\"\n ></lib-decision-popup> -->\n\n <lib-stage-dialog [visible]=\"showStageDialogLocal\" [stageData]=\"activeStageData || {}\"\n (closed)=\"onStageDialogClosed()\" (saved)=\"onStageDialogSaved($event)\"></lib-stage-dialog>\n\n <lib-action-dialog [visible]=\"showActionDialogLocal\" [actionData]=\"activeActionData\" (closed)=\"onActionDialogClosed()\"\n (saved)=\"onActionDialogSaved($event)\" (deleted)=\"onActionDialogDeleted($event)\"></lib-action-dialog>\n\n\n <lib-escalation-dialog [visible]=\"showEscalationDialogLocal\" [type]=\"activeEscalationType()!\"\n [stageCode]=\"activeStageCode() || ''\" [workflowCode]=\"state.workflow?.Code || ''\"\n (closed)=\"onEscalationDialogClosed()\"></lib-escalation-dialog>\n</div>\n", styles: [".canvas-container{flex:1;overflow:auto;background-color:#fff}.designer-canvas{min-width:100%;min-height:100%;cursor:default}.designer-canvas:focus{outline:none}.edit-swimlane-button{cursor:pointer}.edit-swimlane-button:hover rect{fill:#ddd6fe}.decision-condition-icon{cursor:pointer}.decision-condition-icon:hover path{fill:#c084fc}.swimlane-label{z-index:10;cursor:default}.plus-icon:hover{fill:#d36cff}.swimlane-container{pointer-events:none}.swimlane-container>*{pointer-events:auto}.node-element{z-index:10}.connection-element{z-index:5}.node-drag-handle{pointer-events:all;-webkit-user-select:none;user-select:none}\n"], dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i4.VerbenPopUpComponent, selector: "verben-pop-Up", inputs: ["dropdownOpen", "dropdownWidth", "color", "customStyles", "popUpClass", "border", "borderRadius", "enableMouseLeave", "cdkPosition"], outputs: ["dropdownOpenChange", "close"] }, { kind: "component", type: i5.LabelEditorComponent, selector: "wf-label-editor", inputs: ["label", "x", "y", "isConnection", "isSwimlane"], outputs: ["labelChange", "openDialog"] }, { kind: "component", type: StageNodeComponent, selector: "svg:g[lib-stage-node]", inputs: ["node", "isStartNode", "isSubflow", "stageData"], outputs: ["stagePropertiesUpdated", "parallelExecutionToggled", "showShieldDialog", "showEscalationDialog", "showFormDialog"] }, { kind: "component", type: StageDialogComponent, selector: "lib-stage-dialog", inputs: ["visible", "stageData"], outputs: ["closed", "saved"] }, { kind: "component", type: ConditionsPopupComponent, selector: "lib-conditions-popup", inputs: ["visible", "decisionNodeId", "newConnectionId", "popupX", "popupY"], outputs: ["closed", "saved"] }, { kind: "component", type: EscalationDialogComponent, selector: "lib-escalation-dialog", inputs: ["visible", "workflowCode", "stageCode", "type"], outputs: ["closed", "created"] }, { kind: "component", type: ActionDialogComponent, selector: "lib-action-dialog", inputs: ["visible", "actionData"], outputs: ["closed", "saved", "deleted"] }, { kind: "pipe", type: i3.TitleCasePipe, name: "titlecase" }] });
|
|
3631
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: DesignerCanvasComponent, selector: "lib-designer-canvas", inputs: { selectedTool: "selectedTool" }, outputs: { clickedPosition: "clickedPosition", subflowSelected: "subflowSelected", stagePropertiesUpdated: "stagePropertiesUpdated", actionPropertiesUpdated: "actionPropertiesUpdated" }, host: { listeners: { "window:mouseup": "onWindowMouseUp($event)" } }, viewQueries: [{ propertyName: "canvasRef", first: true, predicate: ["canvas"], descendants: true, static: true }], ngImport: i0, template: "<div class=\"canvas-container\">\n <svg #canvas [attr.width]=\"canvasWidth\" [attr.height]=\"canvasHeight\" class=\"designer-canvas\"\n (click)=\"onCanvasClick($event)\">\n <defs>\n <!-- Grid pattern definition -->\n\n <pattern id=\"grid\" [attr.width]=\"gridSize\" [attr.height]=\"gridSize\" patternUnits=\"userSpaceOnUse\">\n <path d=\"M 20 0 L 0 0 0 20\" fill=\"none\" stroke=\"#e2e8f0\" stroke-width=\"0.5\" />\n </pattern>\n\n <!-- Arrow head marker definition -->\n <marker id=\"arrowhead\" markerWidth=\"10\" markerHeight=\"7\" refX=\"9\" refY=\"3.5\" orient=\"auto\">\n <polygon points=\"0 0, 10 3.5, 0 7\" fill=\"#D36CFF\" />\n </marker>\n\n <!-- Connection point styles -->\n <circle id=\"connection-point-template\" r=\"5\" fill=\"#D36CFF\" stroke=\"#FFFFFF\" stroke-width=\"1\" />\n\n <!-- Dashed line style for connection preview -->\n <pattern id=\"dashed-line\" width=\"10\" height=\"10\" patternUnits=\"userSpaceOnUse\">\n <line x1=\"0\" y1=\"5\" x2=\"10\" y2=\"5\" stroke=\"#D36CFF\" stroke-width=\"2\" stroke-dasharray=\"5,5\" />\n </pattern>\n\n @for (swimlane of state.swimlanes; track swimlane.order) {\n <clipPath [attr.id]=\"'swimlane-clip-' + swimlane.order\">\n <rect x=\"0\" y=\"0\" [attr.width]=\"canvasWidth\" height=\"263\" />\n </clipPath>\n }\n </defs>\n\n <!-- Background grid -->\n <rect width=\"100%\" height=\"100%\" fill=\"url(#grid)\" />\n\n <!-- Display a message when no swimlanes exist -->\n @if (state.swimlanes.length === 0) {\n <text x=\"50%\" y=\"50%\" font-family=\"sans-serif\" font-size=\"16\" fill=\"#94a3b8\" text-anchor=\"middle\">\n Select the Swimlane tool and click on the canvas to add a swimlane\n </text>\n }\n\n <!-- This is where workflow elements will be added later -->\n @for (swimlane of state.swimlanes; track swimlane.order) {\n <!-- <g\n [attr.transform]=\"'translate(0,' + swimlane.order * 263 + ')'\"\n [attr.clip-path]=\"'url(#swimlane-clip-' + swimlane.order + ')'\"\n >\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"canvasWidth\"\n [attr.height]=\"263\"\n fill=\"#ffffff\"\n stroke=\"#e2e8f0\"\n stroke-width=\"1\"\n ></rect>\n\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"canvasWidth\"\n height=\"40\"\n fill=\"#f8fafc\"\n stroke=\"#e2e8f0\"\n stroke-width=\"1\"\n ></rect>\n\n <text\n x=\"20\"\n y=\"25\"\n font-family=\"sans-serif\"\n font-size=\"14\"\n fill=\"#333333\"\n font-weight=\"bold\"\n >\n {{ swimlane.label }}\n </text>\n\n <g\n class=\"edit-swimlane-button\"\n [attr.transform]=\"'translate(100, 20)'\"\n (click)=\"\n onEditSwimlane($event, swimlane, swimlane.order);\n $event.stopPropagation()\n \"\n >\n <rect\n x=\"-5\"\n y=\"-15\"\n width=\"40\"\n height=\"20\"\n fill=\"#f3e8ff\"\n rx=\"3\"\n ry=\"3\"\n stroke=\"#d8b4fe\"\n stroke-width=\"1\"\n ></rect>\n <text\n x=\"15\"\n y=\"0\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#7e22ce\"\n text-anchor=\"middle\"\n >\n Edit\n </text>\n </g>\n\n <g [attr.transform]=\"'translate(200, 20)'\">\n @for (tag of swimlane.tags.slice(0, 3); track tag.Name; let i = $index)\n {\n <text\n [attr.x]=\"i * 100\"\n y=\"5\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#666666\"\n >\n {{ tag.Name }}\n </text>\n } @if (swimlane.tags.length > 3) {\n <text\n [attr.x]=\"3 * 100 + 10\"\n y=\"5\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#666666\"\n >\n +{{ swimlane.tags.length - 3 }} more\n </text>\n }\n </g>\n </g> -->\n\n <g [attr.transform]=\"'translate(0,' + swimlane.order * 263 + ')'\">\n <!-- Swimlane container - remove stroke to make it purely background -->\n <rect x=\"0\" y=\"0\" [attr.width]=\"canvasWidth\" [attr.height]=\"263\"\n [attr.fill]=\"swimlane.order % 2 === 0 ? '#FCF3FF' : '#ECFFF5'\" stroke=\"none\"></rect>\n\n <!-- Vertical swimlane label on the left -->\n <g class=\"swimlane-label\">\n <!-- Purple background for the label -->\n <rect x=\"0.5\" y=\"0.5\" width=\"43\" height=\"215\" rx=\"10.5\" fill=\"white\" stroke=\"#D36CFF\"></rect>\n\n <!-- Plus button at the top -->\n <g (click)=\"\n onEditSwimlane($event, swimlane, swimlane.order);\n $event.stopPropagation()\n \">\n <rect x=\"12.5\" y=\"10\" width=\"18\" height=\"18\" fill=\"transparent\" style=\"cursor: pointer\"></rect>\n\n <path d=\"M22.52 12.174V17.08H27.47V18.51H22.52V23.394H21.2V18.51H16.25V17.08H21.2V12.174H22.52Z\"\n fill=\"#0000FF\" class=\"plus-icon\" style=\"cursor: pointer\"></path>\n </g>\n\n <!-- Swimlane label -->\n <text x=\"22\" y=\"106\" font-family=\"sans-serif\" font-size=\"14\" fill=\"#333333\" text-anchor=\"middle\"\n transform=\"rotate(-90, 22, 100)\">\n {{\n swimlane.label.length > 12\n ? swimlane.label.substring(0, 10) + \"...\"\n : swimlane.label\n }}\n </text>\n\n <!-- Tag count indicator at the bottom -->\n <rect x=\"11.5\" y=\"187\" width=\"21\" height=\"19\" rx=\"9.5\" fill=\"#9E9E9E\"></rect>\n <text x=\"24\" y=\"206\" font-family=\"sans-serif\" font-size=\"12\" fill=\"black\" text-anchor=\"middle\"\n transform=\"rotate(-90, 22, 200)\">\n {{ swimlane.tags.length }}\n </text>\n </g>\n </g>\n\n }\n\n <!-- Nodes -->\n @for (swimlane of state.swimlanes; track swimlane.order) {\n <!-- Render nodes in this swimlane -->\n @for (node of swimlane.nodes; track node.id) {\n <g class=\"node-element\" [attr.transform]=\"\n 'translate(' + node.x + ',' + (node.y + swimlane.order * 263) + ')'\"\n (mouseenter)=\"showConnectionPoints(node.id, swimlane.order)\"\n (mouseleave)=\"hideConnectionPoints(node.id)\">\n <!-- Start node indicator (circle and arrow) for the first node -->\n @if (node.isStartNode) {\n <!-- Circle -->\n <circle cx=\"-50\" cy=\"50\" r=\"30\" fill=\"none\" stroke=\"#D36CFF\" stroke-width=\"1\"></circle>\n <!-- Form icon for start node -->\n <svg:g (click)=\"\n toggleStartNodeFormPopup($event, node.x, node.y, swimlane.order)\n \" class=\"stage-icon\" transform=\"translate(-45, 42)\" style=\"cursor: pointer; pointer-events: all\">\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect x=\"-2\" y=\"-2\" width=\"24\" height=\"24\" fill=\"transparent\" style=\"cursor: pointer\" />\n <svg:path\n d=\"M16.5 20.475V17.475H13.5V16.475H16.5V13.475H17.5V16.475H20.5V17.475H17.5V20.475H16.5ZM3.5 17.5V16.5H4.5V17.5H3.5ZM6.5 17.5V16.5H11.517C11.5057 16.6767 11.5043 16.845 11.513 17.005C11.521 17.165 11.531 17.33 11.543 17.5H6.5ZM3.5 13.5V12.5H4.5V13.5H3.5ZM6.5 13.5V12.5H13.804C13.6127 12.6387 13.4333 12.7913 13.266 12.958C13.0993 13.1247 12.9377 13.3053 12.781 13.5H6.5ZM3.5 9.5V8.5H4.5V9.5H3.5ZM6.5 9.5V8.5H18.5V9.5H6.5ZM3.5 5.5V4.5H4.5V5.5H3.5ZM6.5 5.5V4.5H18.5V5.5H6.5Z\"\n [attr.fill]=\"state.workflowFormId ? '#D36CFF' : 'black'\" transform=\"scale(0.7)\" />\n </svg:g>\n <!-- Arrow from circle to node -->\n <path d=\"M -20 50 L 0 50\" stroke=\"#D36CFF\" stroke-width=\"1\" marker-end=\"url(#arrowhead)\"></path>\n }\n\n <!-- Exit point indicator (circle and arrow) -->\n @if (node.stageData?.IsExitPoint) {\n <!-- Arrow from node to circle -->\n <path [attr.d]=\"'M ' + node.width + ' 50 L ' + (node.width + 20) + ' 50'\" stroke=\"#D36CFF\" stroke-width=\"1\"\n marker-end=\"url(#arrowhead)\"></path>\n <!-- Circle -->\n <circle [attr.cx]=\"node.width + 50\" cy=\"50\" r=\"30\" fill=\"none\" stroke=\"#D36CFF\" stroke-width=\"2\"></circle>\n }\n\n <!-- Inner drag-handle: inset 12px so the node edge ring stays drag-free.\n Rendered BEFORE the node shapes/icons so the clickable icons paint on\n top of it and win pointer events; the node body still falls through to\n this handle because its shape/label use pointer-events:none.\n Connection point <use> elements are rendered after everything so they\n stay topmost and win pointer events at the node edges. -->\n <rect class=\"node-drag-handle\"\n [attr.x]=\"12\" [attr.y]=\"12\"\n [attr.width]=\"node.width - 24\" [attr.height]=\"node.height - 24\"\n fill=\"transparent\"\n [style.cursor]=\"getCursorForNode(node.id)\"\n (mousedown)=\"onNodeMouseDown($event, node, swimlane.order)\" />\n\n <!-- Stage node -->\n @if (node.type === 'stage') {\n <!-- <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></rect> -->\n <svg:g lib-stage-node [node]=\"node\" [isStartNode]=\"node.isStartNode\"\n (stagePropertiesUpdated)=\"onStagePropertiesUpdated($event)\" (showShieldDialog)=\"onShowShieldDialog($event)\"\n (showEscalationDialog)=\"onShowEscalationDialog($event)\" (showFormDialog)=\"\n toggleStartNodeFormPopup($event, node.x, node.y, swimlane.order, node)\n \"></svg:g>\n }\n\n <!-- Decision node -->\n @if (node.type === 'decision') {\n <path [attr.d]=\"\n 'M 0 ' +\n node.height / 2 +\n ' L ' +\n node.width / 2 +\n ' 0' +\n ' L ' +\n node.width +\n ' ' +\n node.height / 2 +\n ' L ' +\n node.width / 2 +\n ' ' +\n node.height +\n ' Z'\n \" fill=\"none\" stroke=\"#D36CFF\" stroke-width=\"1\" style=\"pointer-events: none\"></path>\n\n <!-- Decision node condition indicator icon -->\n <svg:g (click)=\"showDecisionConditionsPopup($event, node, swimlane.order)\" class=\"decision-condition-icon\"\n [attr.transform]=\"\n 'translate(' +\n (node.width / 2 - 10) +\n ',' +\n (node.height / 2 - 10) +\n ')'\n \">\n <svg:rect x=\"-5\" y=\"-5\" width=\"30\" height=\"30\" fill=\"transparent\" style=\"cursor: pointer\" />\n <svg:path d=\"M10 0H20V2H10V0ZM10 8H20V10H10V8ZM10 4H20V6H10V4ZM0 0H8V2H0V0ZM0 8H8V10H0V8ZM0 4H8V6H0V4Z\"\n fill=\"#D36CFF\" />\n </svg:g>\n }\n\n <!-- Form node -->\n @if (node.type === 'form') {\n <path\n d=\"M95.0625 50.5591V95.0625C95.0625 97.9716 93.9069 100.762 91.8498 102.819C89.7928 104.876 87.0028 106.031 84.0938 106.031H32.9062C29.9972 106.031 27.2072 104.876 25.1502 102.819C23.0931 100.762 21.9375 97.9716 21.9375 95.0625V21.9375C21.9375 19.0284 23.0931 16.2385 25.1502 14.1814C27.2072 12.1244 29.9972 10.9688 32.9062 10.9688H55.4722C57.4109 10.969 59.2701 11.7392 60.6412 13.1099L92.9213 45.3901C94.292 46.7611 95.0622 48.6204 95.0625 50.5591Z\"\n transform=\"scale(0.7)\" fill=\"none\" stroke=\"#D36CFF\" stroke-linejoin=\"round\" style=\"pointer-events: none\" />\n <path\n d=\"M58.5 12.7969V40.2188C58.5 42.1581 59.2704 44.0181 60.6418 45.3895C62.0131 46.7608 63.8731 47.5312 65.8125 47.5312H93.2344\"\n transform=\"scale(0.7)\" stroke=\"#D36CFF\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"pointer-events: none\" />\n <text x=\"50%\" y=\"50%\" text-anchor=\"middle\" dominant-baseline=\"middle\" font-family=\"sans-serif\" font-size=\"14\"\n fill=\"#D36CFF\" style=\"pointer-events: none\">\n Form\n </text>\n }\n\n <!-- Subflow node -->\n @if (node.type === 'subflow') {\n <svg:g lib-stage-node [node]=\"node\" [isStartNode]=\"node.isStartNode\" [isSubflow]=\"true\"\n (stagePropertiesUpdated)=\"onStagePropertiesUpdated($event)\" (showShieldDialog)=\"onShowShieldDialog($event)\"\n (showEscalationDialog)=\"onShowEscalationDialog($event)\" (showFormDialog)=\"\n toggleStartNodeFormPopup($event, node.x, node.y, swimlane.order, node)\n \"></svg:g>\n }\n\n <!-- Connection points for this node (rendered last = topmost) -->\n @for (point of node.connectionPoints || []; track point.id) {\n @if (isConnectionPointVisible(node.id)) {\n <use [attr.href]=\"'#connection-point-template'\" [attr.x]=\"point.x\" [attr.y]=\"point.y\" [attr.id]=\"point.id\"\n (mousedown)=\"startConnectionDrag($event, point, swimlane.order)\" />\n }\n }\n </g>\n <!-- A transparent hover area for improved hover detection -->\n <rect [attr.x]=\"node.x - 10\" [attr.y]=\"node.y - 10\" [attr.width]=\"node.width + 20\" [attr.height]=\"node.height + 20\"\n fill=\"transparent\" (mouseover)=\"showConnectionPoints(node.id, swimlane.order)\"\n (mouseout)=\"hideConnectionPoints(node.id)\" style=\"pointer-events: none\" />\n } }\n\n <!-- Connections -->\n @for (connection of state.connections; track connection.id) {\n <g class=\"connection-element\">\n <path [attr.d]=\"getConnectionPathForSavedConnection(connection)\" stroke=\"#D36CFF\" stroke-width=\"2\" fill=\"none\"\n marker-end=\"url(#arrowhead)\"></path>\n\n <!-- Connection Label -->\n @if (connection.sourceNodeId && connection.targetNodeId) {\n <foreignObject [attr.x]=\"getLabelPosition(connection).x\" [attr.y]=\"getLabelPosition(connection).y\" width=\"150\"\n height=\"40\" style=\"overflow: visible; pointer-events: all;\" [attr.transform]=\"'translate(-75, -10)'\">\n <wf-label-editor [label]=\"connection.label || 'New Action'\" [x]=\"0\" [y]=\"0\" [isConnection]=\"true\"\n (labelChange)=\"saveConnectionLabel(connection, $event)\" (openDialog)=\"onShowActionDialog(connection)\" />\n </foreignObject>\n }\n </g>\n }\n\n <!-- Connection preview line -->\n @if (state.isConnectionDragging()) {\n <g>\n <path [attr.d]=\"getConnectionPath()\" stroke=\"#D36CFF\" stroke-width=\"2\" stroke-dasharray=\"5,5\" fill=\"none\"></path>\n </g>\n }\n </svg>\n\n <div *ngIf=\"popupVisible\" [style.position]=\"'absolute'\" [style.left.px]=\"popupX\" [style.top.px]=\"popupY\">\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '99' }\" [enableMouseLeave]=\"false\"\n (dropdownOpenChange)=\"$event ? null : hideConnectionPopup()\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div class=\"p-3 bg-white border border-gray-200 rounded shadow-md\" dropdown-content>\n <h4 class=\"mb-2 font-medium\">Create Connection</h4>\n <div class=\"flex flex-col gap-2\">\n <ng-container *ngFor=\"let type of allowedNodeTypes\">\n <button class=\"px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50\"\n (click)=\"createNodeConnection(type)\">\n Create {{ type | titlecase }}\n </button>\n </ng-container>\n <button class=\"px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50\"\n (click)=\"hideConnectionPopup()\">\n Cancel\n </button>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <div *ngIf=\"showStartNodeFormPopup\" [style.position]=\"'absolute'\" [style.left.px]=\"startNodeFormPopupX\"\n [style.top.px]=\"startNodeFormPopupY\">\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\" dropdown-content\n style=\"background-color: white; z-index: 1000\">\n <h4 class=\"mb-2 font-medium\">Select Workflow Form</h4>\n <div *ngIf=\"isLoadingStartNodeForms\" class=\"text-center py-2\">\n Loading forms...\n </div>\n <div *ngIf=\"!isLoadingStartNodeForms\" class=\"max-h-48 overflow-y-auto\" style=\"max-height: 192px; overflow-y: auto;\">\n <div class=\"mb-3\">\n <button\n class=\"w-full px-3 py-2 bg-gray-50 border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectStartNodeForm(null)\">\n Clear form selection\n </button>\n </div>\n <div *ngFor=\"let form of startNodeFormsList\" class=\"mb-1\">\n <button class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n [ngClass]=\"{ 'bg-blue-50 !border-blue-500 !border-2 font-semibold': form?.Code === activeNodeForFormSelection?.stageData?.formId}\"\n (click)=\"selectStartNodeForm(form)\">\n {{ form.Name }}\n </button>\n </div>\n <div *ngIf=\"startNodeFormsList.length === 0\" class=\"text-center py-2 text-gray-500\">\n No forms available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <div *ngIf=\"showSubflowPopup\" [style.position]=\"'absolute'\" [style.left.px]=\"subflowPopupX\"\n [style.top.px]=\"subflowPopupY\">\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\" dropdown-content\n style=\"background-color: white; z-index: 1000\">\n <h4 class=\"mb-2 font-medium\">Select Workflow</h4>\n <div *ngIf=\"isLoadingWorkflows\" class=\"text-center py-2\">\n Loading workflows...\n </div>\n <div *ngIf=\"!isLoadingWorkflows\" class=\"max-h-48 overflow-y-auto\" style=\"max-height: 192px; overflow-y: auto;\">\n <div *ngFor=\"let workflow of workflowsList\" class=\"mb-1\">\n <button class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectSubflowWorkflow(workflow)\">\n {{ workflow.Name }}\n </button>\n </div>\n <div *ngIf=\"workflowsList.length === 0\" class=\"text-center py-2 text-gray-500\">\n No workflows available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <lib-conditions-popup [visible]=\"showDecisionConditionDialog\" [decisionNodeId]=\"activeDecisionNodeId\"\n [newConnectionId]=\"activeConnectionId\" [popupX]=\"decisionConditionPopupX\" [popupY]=\"decisionConditionPopupY\"\n (closed)=\"onDecisionConditionCancelled()\" (saved)=\"onDecisionConditionSaved($event)\"></lib-conditions-popup>\n\n <!-- <lib-decision-popup\n [visible]=\"showConditionsDialog\"\n [decisionNodeId]=\"activeDecisionNodeId\"\n [popupX]=\"decisionConditionPopupX\"\n [popupY]=\"decisionConditionPopupY\"\n (closed)=\"onDecisionConditionCancelled()\"\n ></lib-decision-popup> -->\n\n <lib-stage-dialog [visible]=\"showStageDialogLocal\" [stageData]=\"activeStageData || {}\"\n (closed)=\"onStageDialogClosed()\" (saved)=\"onStageDialogSaved($event)\"></lib-stage-dialog>\n\n <lib-action-dialog [visible]=\"showActionDialogLocal\" [actionData]=\"activeActionData\" (closed)=\"onActionDialogClosed()\"\n (saved)=\"onActionDialogSaved($event)\" (deleted)=\"onActionDialogDeleted($event)\"></lib-action-dialog>\n\n\n <lib-escalation-dialog [visible]=\"showEscalationDialogLocal\" [type]=\"activeEscalationType()!\"\n [stageCode]=\"activeStageCode() || ''\" [workflowCode]=\"state.workflow?.Code || ''\"\n (closed)=\"onEscalationDialogClosed()\"></lib-escalation-dialog>\n</div>\n", styles: [".canvas-container{flex:1;overflow:auto;background-color:#fff}.designer-canvas{min-width:100%;min-height:100%;cursor:default}.designer-canvas:focus{outline:none}.edit-swimlane-button{cursor:pointer}.edit-swimlane-button:hover rect{fill:#ddd6fe}.decision-condition-icon{cursor:pointer}.decision-condition-icon:hover path{fill:#c084fc}.swimlane-label{z-index:10;cursor:default}.plus-icon:hover{fill:#d36cff}.swimlane-container{pointer-events:none}.swimlane-container>*{pointer-events:auto}.node-element{z-index:10}.connection-element{z-index:5}.node-drag-handle{pointer-events:all;-webkit-user-select:none;user-select:none}\n"], dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i4.VerbenPopUpComponent, selector: "verben-pop-Up", inputs: ["dropdownOpen", "dropdownWidth", "color", "customStyles", "popUpClass", "border", "borderRadius", "enableMouseLeave", "cdkPosition"], outputs: ["dropdownOpenChange", "close"] }, { kind: "component", type: i5.LabelEditorComponent, selector: "wf-label-editor", inputs: ["label", "x", "y", "isConnection", "isSwimlane"], outputs: ["labelChange", "openDialog"] }, { kind: "component", type: StageNodeComponent, selector: "svg:g[lib-stage-node]", inputs: ["node", "isStartNode", "isSubflow", "stageData"], outputs: ["stagePropertiesUpdated", "parallelExecutionToggled", "showShieldDialog", "showEscalationDialog", "showFormDialog"] }, { kind: "component", type: StageDialogComponent, selector: "lib-stage-dialog", inputs: ["visible", "stageData"], outputs: ["closed", "saved"] }, { kind: "component", type: ConditionsPopupComponent, selector: "lib-conditions-popup", inputs: ["visible", "decisionNodeId", "newConnectionId", "popupX", "popupY"], outputs: ["closed", "saved"] }, { kind: "component", type: EscalationDialogComponent, selector: "lib-escalation-dialog", inputs: ["visible", "workflowCode", "stageCode", "type"], outputs: ["closed", "created"] }, { kind: "component", type: ActionDialogComponent, selector: "lib-action-dialog", inputs: ["visible", "actionData"], outputs: ["closed", "saved", "deleted"] }, { kind: "pipe", type: i3.TitleCasePipe, name: "titlecase" }] });
|
|
3632
3632
|
}
|
|
3633
3633
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DesignerCanvasComponent, decorators: [{
|
|
3634
3634
|
type: Component,
|
|
3635
|
-
args: [{ selector: 'lib-designer-canvas', template: "<div class=\"canvas-container\">\n <svg #canvas [attr.width]=\"canvasWidth\" [attr.height]=\"canvasHeight\" class=\"designer-canvas\"\n (click)=\"onCanvasClick($event)\">\n <defs>\n <!-- Grid pattern definition -->\n\n <pattern id=\"grid\" [attr.width]=\"gridSize\" [attr.height]=\"gridSize\" patternUnits=\"userSpaceOnUse\">\n <path d=\"M 20 0 L 0 0 0 20\" fill=\"none\" stroke=\"#e2e8f0\" stroke-width=\"0.5\" />\n </pattern>\n\n <!-- Arrow head marker definition -->\n <marker id=\"arrowhead\" markerWidth=\"10\" markerHeight=\"7\" refX=\"9\" refY=\"3.5\" orient=\"auto\">\n <polygon points=\"0 0, 10 3.5, 0 7\" fill=\"#D36CFF\" />\n </marker>\n\n <!-- Connection point styles -->\n <circle id=\"connection-point-template\" r=\"5\" fill=\"#D36CFF\" stroke=\"#FFFFFF\" stroke-width=\"1\" />\n\n <!-- Dashed line style for connection preview -->\n <pattern id=\"dashed-line\" width=\"10\" height=\"10\" patternUnits=\"userSpaceOnUse\">\n <line x1=\"0\" y1=\"5\" x2=\"10\" y2=\"5\" stroke=\"#D36CFF\" stroke-width=\"2\" stroke-dasharray=\"5,5\" />\n </pattern>\n\n @for (swimlane of state.swimlanes; track swimlane.order) {\n <clipPath [attr.id]=\"'swimlane-clip-' + swimlane.order\">\n <rect x=\"0\" y=\"0\" [attr.width]=\"canvasWidth\" height=\"263\" />\n </clipPath>\n }\n </defs>\n\n <!-- Background grid -->\n <rect width=\"100%\" height=\"100%\" fill=\"url(#grid)\" />\n\n <!-- Display a message when no swimlanes exist -->\n @if (state.swimlanes.length === 0) {\n <text x=\"50%\" y=\"50%\" font-family=\"sans-serif\" font-size=\"16\" fill=\"#94a3b8\" text-anchor=\"middle\">\n Select the Swimlane tool and click on the canvas to add a swimlane\n </text>\n }\n\n <!-- This is where workflow elements will be added later -->\n @for (swimlane of state.swimlanes; track swimlane.order) {\n <!-- <g\n [attr.transform]=\"'translate(0,' + swimlane.order * 263 + ')'\"\n [attr.clip-path]=\"'url(#swimlane-clip-' + swimlane.order + ')'\"\n >\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"canvasWidth\"\n [attr.height]=\"263\"\n fill=\"#ffffff\"\n stroke=\"#e2e8f0\"\n stroke-width=\"1\"\n ></rect>\n\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"canvasWidth\"\n height=\"40\"\n fill=\"#f8fafc\"\n stroke=\"#e2e8f0\"\n stroke-width=\"1\"\n ></rect>\n\n <text\n x=\"20\"\n y=\"25\"\n font-family=\"sans-serif\"\n font-size=\"14\"\n fill=\"#333333\"\n font-weight=\"bold\"\n >\n {{ swimlane.label }}\n </text>\n\n <g\n class=\"edit-swimlane-button\"\n [attr.transform]=\"'translate(100, 20)'\"\n (click)=\"\n onEditSwimlane($event, swimlane, swimlane.order);\n $event.stopPropagation()\n \"\n >\n <rect\n x=\"-5\"\n y=\"-15\"\n width=\"40\"\n height=\"20\"\n fill=\"#f3e8ff\"\n rx=\"3\"\n ry=\"3\"\n stroke=\"#d8b4fe\"\n stroke-width=\"1\"\n ></rect>\n <text\n x=\"15\"\n y=\"0\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#7e22ce\"\n text-anchor=\"middle\"\n >\n Edit\n </text>\n </g>\n\n <g [attr.transform]=\"'translate(200, 20)'\">\n @for (tag of swimlane.tags.slice(0, 3); track tag.Name; let i = $index)\n {\n <text\n [attr.x]=\"i * 100\"\n y=\"5\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#666666\"\n >\n {{ tag.Name }}\n </text>\n } @if (swimlane.tags.length > 3) {\n <text\n [attr.x]=\"3 * 100 + 10\"\n y=\"5\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#666666\"\n >\n +{{ swimlane.tags.length - 3 }} more\n </text>\n }\n </g>\n </g> -->\n\n <g [attr.transform]=\"'translate(0,' + swimlane.order * 263 + ')'\">\n <!-- Swimlane container - remove stroke to make it purely background -->\n <rect x=\"0\" y=\"0\" [attr.width]=\"canvasWidth\" [attr.height]=\"263\"\n [attr.fill]=\"swimlane.order % 2 === 0 ? '#FCF3FF' : '#ECFFF5'\" stroke=\"none\"></rect>\n\n <!-- Vertical swimlane label on the left -->\n <g class=\"swimlane-label\">\n <!-- Purple background for the label -->\n <rect x=\"0.5\" y=\"0.5\" width=\"43\" height=\"215\" rx=\"10.5\" fill=\"white\" stroke=\"#D36CFF\"></rect>\n\n <!-- Plus button at the top -->\n <g (click)=\"\n onEditSwimlane($event, swimlane, swimlane.order);\n $event.stopPropagation()\n \">\n <rect x=\"12.5\" y=\"10\" width=\"18\" height=\"18\" fill=\"transparent\" style=\"cursor: pointer\"></rect>\n\n <path d=\"M22.52 12.174V17.08H27.47V18.51H22.52V23.394H21.2V18.51H16.25V17.08H21.2V12.174H22.52Z\"\n fill=\"#0000FF\" class=\"plus-icon\" style=\"cursor: pointer\"></path>\n </g>\n\n <!-- Swimlane label -->\n <text x=\"22\" y=\"106\" font-family=\"sans-serif\" font-size=\"14\" fill=\"#333333\" text-anchor=\"middle\"\n transform=\"rotate(-90, 22, 100)\">\n {{\n swimlane.label.length > 12\n ? swimlane.label.substring(0, 10) + \"...\"\n : swimlane.label\n }}\n </text>\n\n <!-- Tag count indicator at the bottom -->\n <rect x=\"11.5\" y=\"187\" width=\"21\" height=\"19\" rx=\"9.5\" fill=\"#9E9E9E\"></rect>\n <text x=\"24\" y=\"206\" font-family=\"sans-serif\" font-size=\"12\" fill=\"black\" text-anchor=\"middle\"\n transform=\"rotate(-90, 22, 200)\">\n {{ swimlane.tags.length }}\n </text>\n </g>\n </g>\n\n }\n\n <!-- Nodes -->\n @for (swimlane of state.swimlanes; track swimlane.order) {\n <!-- Render nodes in this swimlane -->\n @for (node of swimlane.nodes; track node.id) {\n <g class=\"node-element\" [attr.transform]=\"\n 'translate(' + node.x + ',' + (node.y + swimlane.order * 263) + ')'\"\n (mouseenter)=\"showConnectionPoints(node.id, swimlane.order)\"\n (mouseleave)=\"hideConnectionPoints(node.id)\">\n <!-- Start node indicator (circle and arrow) for the first node -->\n @if (node.isStartNode) {\n <!-- Circle -->\n <circle cx=\"-50\" cy=\"50\" r=\"30\" fill=\"none\" stroke=\"#D36CFF\" stroke-width=\"1\"></circle>\n <!-- Form icon for start node -->\n <svg:g (click)=\"\n toggleStartNodeFormPopup($event, node.x, node.y, swimlane.order)\n \" class=\"stage-icon\" transform=\"translate(-45, 42)\" style=\"cursor: pointer; pointer-events: all\">\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect x=\"-2\" y=\"-2\" width=\"24\" height=\"24\" fill=\"transparent\" style=\"cursor: pointer\" />\n <svg:path\n d=\"M16.5 20.475V17.475H13.5V16.475H16.5V13.475H17.5V16.475H20.5V17.475H17.5V20.475H16.5ZM3.5 17.5V16.5H4.5V17.5H3.5ZM6.5 17.5V16.5H11.517C11.5057 16.6767 11.5043 16.845 11.513 17.005C11.521 17.165 11.531 17.33 11.543 17.5H6.5ZM3.5 13.5V12.5H4.5V13.5H3.5ZM6.5 13.5V12.5H13.804C13.6127 12.6387 13.4333 12.7913 13.266 12.958C13.0993 13.1247 12.9377 13.3053 12.781 13.5H6.5ZM3.5 9.5V8.5H4.5V9.5H3.5ZM6.5 9.5V8.5H18.5V9.5H6.5ZM3.5 5.5V4.5H4.5V5.5H3.5ZM6.5 5.5V4.5H18.5V5.5H6.5Z\"\n [attr.fill]=\"state.workflowFormId ? '#D36CFF' : 'black'\" transform=\"scale(0.7)\" />\n </svg:g>\n <!-- Arrow from circle to node -->\n <path d=\"M -20 50 L 0 50\" stroke=\"#D36CFF\" stroke-width=\"1\" marker-end=\"url(#arrowhead)\"></path>\n }\n\n <!-- Exit point indicator (circle and arrow) -->\n @if (node.stageData?.IsExitPoint) {\n <!-- Arrow from node to circle -->\n <path [attr.d]=\"'M ' + node.width + ' 50 L ' + (node.width + 20) + ' 50'\" stroke=\"#D36CFF\" stroke-width=\"1\"\n marker-end=\"url(#arrowhead)\"></path>\n <!-- Circle -->\n <circle [attr.cx]=\"node.width + 50\" cy=\"50\" r=\"30\" fill=\"none\" stroke=\"#D36CFF\" stroke-width=\"2\"></circle>\n }\n\n <!-- Stage node -->\n @if (node.type === 'stage') {\n <!-- <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></rect> -->\n <svg:g lib-stage-node [node]=\"node\" [isStartNode]=\"node.isStartNode\"\n (stagePropertiesUpdated)=\"onStagePropertiesUpdated($event)\" (showShieldDialog)=\"onShowShieldDialog($event)\"\n (showEscalationDialog)=\"onShowEscalationDialog($event)\" (showFormDialog)=\"\n toggleStartNodeFormPopup($event, node.x, node.y, swimlane.order, node)\n \"></svg:g>\n }\n\n <!-- Decision node -->\n @if (node.type === 'decision') {\n <path [attr.d]=\"\n 'M 0 ' +\n node.height / 2 +\n ' L ' +\n node.width / 2 +\n ' 0' +\n ' L ' +\n node.width +\n ' ' +\n node.height / 2 +\n ' L ' +\n node.width / 2 +\n ' ' +\n node.height +\n ' Z'\n \" fill=\"none\" stroke=\"#D36CFF\" stroke-width=\"1\"></path>\n\n <!-- Decision node condition indicator icon -->\n <svg:g (click)=\"showDecisionConditionsPopup($event, node, swimlane.order)\" class=\"decision-condition-icon\"\n [attr.transform]=\"\n 'translate(' +\n (node.width / 2 - 10) +\n ',' +\n (node.height / 2 - 10) +\n ')'\n \">\n <svg:rect x=\"-5\" y=\"-5\" width=\"30\" height=\"30\" fill=\"transparent\" style=\"cursor: pointer\" />\n <svg:path d=\"M10 0H20V2H10V0ZM10 8H20V10H10V8ZM10 4H20V6H10V4ZM0 0H8V2H0V0ZM0 8H8V10H0V8ZM0 4H8V6H0V4Z\"\n fill=\"#D36CFF\" />\n </svg:g>\n }\n\n <!-- Form node -->\n @if (node.type === 'form') {\n <path\n d=\"M95.0625 50.5591V95.0625C95.0625 97.9716 93.9069 100.762 91.8498 102.819C89.7928 104.876 87.0028 106.031 84.0938 106.031H32.9062C29.9972 106.031 27.2072 104.876 25.1502 102.819C23.0931 100.762 21.9375 97.9716 21.9375 95.0625V21.9375C21.9375 19.0284 23.0931 16.2385 25.1502 14.1814C27.2072 12.1244 29.9972 10.9688 32.9062 10.9688H55.4722C57.4109 10.969 59.2701 11.7392 60.6412 13.1099L92.9213 45.3901C94.292 46.7611 95.0622 48.6204 95.0625 50.5591Z\"\n transform=\"scale(0.7)\" fill=\"none\" stroke=\"#D36CFF\" stroke-linejoin=\"round\" />\n <path\n d=\"M58.5 12.7969V40.2188C58.5 42.1581 59.2704 44.0181 60.6418 45.3895C62.0131 46.7608 63.8731 47.5312 65.8125 47.5312H93.2344\"\n transform=\"scale(0.7)\" stroke=\"#D36CFF\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n <text x=\"50%\" y=\"50%\" text-anchor=\"middle\" dominant-baseline=\"middle\" font-family=\"sans-serif\" font-size=\"14\"\n fill=\"#D36CFF\">\n Form\n </text>\n }\n\n <!-- Subflow node -->\n @if (node.type === 'subflow') {\n <svg:g lib-stage-node [node]=\"node\" [isStartNode]=\"node.isStartNode\" [isSubflow]=\"true\"\n (stagePropertiesUpdated)=\"onStagePropertiesUpdated($event)\" (showShieldDialog)=\"onShowShieldDialog($event)\"\n (showEscalationDialog)=\"onShowEscalationDialog($event)\" (showFormDialog)=\"\n toggleStartNodeFormPopup($event, node.x, node.y, swimlane.order, node)\n \"></svg:g>\n }\n\n <!-- Inner drag-handle: inset 12px so the node edge ring stays drag-free.\n Connection point <use> elements are rendered after this so they are\n topmost in SVG z-order and win pointer events at the node edges. -->\n <rect class=\"node-drag-handle\"\n [attr.x]=\"12\" [attr.y]=\"12\"\n [attr.width]=\"node.width - 24\" [attr.height]=\"node.height - 24\"\n fill=\"transparent\"\n [style.cursor]=\"getCursorForNode(node.id)\"\n (mousedown)=\"onNodeMouseDown($event, node, swimlane.order)\" />\n\n <!-- Connection points for this node (rendered last = topmost) -->\n @for (point of node.connectionPoints || []; track point.id) {\n @if (isConnectionPointVisible(node.id)) {\n <use [attr.href]=\"'#connection-point-template'\" [attr.x]=\"point.x\" [attr.y]=\"point.y\" [attr.id]=\"point.id\"\n (mousedown)=\"startConnectionDrag($event, point, swimlane.order)\" />\n }\n }\n </g>\n <!-- A transparent hover area for improved hover detection -->\n <rect [attr.x]=\"node.x - 10\" [attr.y]=\"node.y - 10\" [attr.width]=\"node.width + 20\" [attr.height]=\"node.height + 20\"\n fill=\"transparent\" (mouseover)=\"showConnectionPoints(node.id, swimlane.order)\"\n (mouseout)=\"hideConnectionPoints(node.id)\" style=\"pointer-events: none\" />\n } }\n\n <!-- Connections -->\n @for (connection of state.connections; track connection.id) {\n <g class=\"connection-element\">\n <path [attr.d]=\"getConnectionPathForSavedConnection(connection)\" stroke=\"#D36CFF\" stroke-width=\"2\" fill=\"none\"\n marker-end=\"url(#arrowhead)\"></path>\n\n <!-- Connection Label -->\n @if (connection.sourceNodeId && connection.targetNodeId) {\n <foreignObject [attr.x]=\"getLabelPosition(connection).x\" [attr.y]=\"getLabelPosition(connection).y\" width=\"150\"\n height=\"40\" style=\"overflow: visible; pointer-events: all;\" [attr.transform]=\"'translate(-75, -10)'\">\n <wf-label-editor [label]=\"connection.label || 'New Action'\" [x]=\"0\" [y]=\"0\" [isConnection]=\"true\"\n (labelChange)=\"saveConnectionLabel(connection, $event)\" (openDialog)=\"onShowActionDialog(connection)\" />\n </foreignObject>\n }\n </g>\n }\n\n <!-- Connection preview line -->\n @if (state.isConnectionDragging()) {\n <g>\n <path [attr.d]=\"getConnectionPath()\" stroke=\"#D36CFF\" stroke-width=\"2\" stroke-dasharray=\"5,5\" fill=\"none\"></path>\n </g>\n }\n </svg>\n\n <div *ngIf=\"popupVisible\" [style.position]=\"'absolute'\" [style.left.px]=\"popupX\" [style.top.px]=\"popupY\">\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '99' }\" [enableMouseLeave]=\"false\"\n (dropdownOpenChange)=\"$event ? null : hideConnectionPopup()\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div class=\"p-3 bg-white border border-gray-200 rounded shadow-md\" dropdown-content>\n <h4 class=\"mb-2 font-medium\">Create Connection</h4>\n <div class=\"flex flex-col gap-2\">\n <ng-container *ngFor=\"let type of allowedNodeTypes\">\n <button class=\"px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50\"\n (click)=\"createNodeConnection(type)\">\n Create {{ type | titlecase }}\n </button>\n </ng-container>\n <button class=\"px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50\"\n (click)=\"hideConnectionPopup()\">\n Cancel\n </button>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <div *ngIf=\"showStartNodeFormPopup\" [style.position]=\"'absolute'\" [style.left.px]=\"startNodeFormPopupX\"\n [style.top.px]=\"startNodeFormPopupY\">\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\" dropdown-content\n style=\"background-color: white; z-index: 1000\">\n <h4 class=\"mb-2 font-medium\">Select Workflow Form</h4>\n <div *ngIf=\"isLoadingStartNodeForms\" class=\"text-center py-2\">\n Loading forms...\n </div>\n <div *ngIf=\"!isLoadingStartNodeForms\" class=\"max-h-48 overflow-y-auto\">\n <div class=\"mb-3\">\n <button\n class=\"w-full px-3 py-2 bg-gray-50 border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectStartNodeForm(null)\">\n Clear form selection\n </button>\n </div>\n <div *ngFor=\"let form of startNodeFormsList\" class=\"mb-1\">\n <button class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n [ngClass]=\"{ 'bg-blue-50 !border-blue-500 !border-2 font-semibold': form?.Code === activeNodeForFormSelection?.stageData?.formId}\"\n (click)=\"selectStartNodeForm(form)\">\n {{ form.Name }}\n </button>\n </div>\n <div *ngIf=\"startNodeFormsList.length === 0\" class=\"text-center py-2 text-gray-500\">\n No forms available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <div *ngIf=\"showSubflowPopup\" [style.position]=\"'absolute'\" [style.left.px]=\"subflowPopupX\"\n [style.top.px]=\"subflowPopupY\">\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\" dropdown-content\n style=\"background-color: white; z-index: 1000\">\n <h4 class=\"mb-2 font-medium\">Select Workflow</h4>\n <div *ngIf=\"isLoadingWorkflows\" class=\"text-center py-2\">\n Loading workflows...\n </div>\n <div *ngIf=\"!isLoadingWorkflows\" class=\"max-h-48 overflow-y-auto\" style=\"max-height: 192px; overflow-y: auto;\">\n <div *ngFor=\"let workflow of workflowsList\" class=\"mb-1\">\n <button class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectSubflowWorkflow(workflow)\">\n {{ workflow.Name }}\n </button>\n </div>\n <div *ngIf=\"workflowsList.length === 0\" class=\"text-center py-2 text-gray-500\">\n No workflows available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <lib-conditions-popup [visible]=\"showDecisionConditionDialog\" [decisionNodeId]=\"activeDecisionNodeId\"\n [newConnectionId]=\"activeConnectionId\" [popupX]=\"decisionConditionPopupX\" [popupY]=\"decisionConditionPopupY\"\n (closed)=\"onDecisionConditionCancelled()\" (saved)=\"onDecisionConditionSaved($event)\"></lib-conditions-popup>\n\n <!-- <lib-decision-popup\n [visible]=\"showConditionsDialog\"\n [decisionNodeId]=\"activeDecisionNodeId\"\n [popupX]=\"decisionConditionPopupX\"\n [popupY]=\"decisionConditionPopupY\"\n (closed)=\"onDecisionConditionCancelled()\"\n ></lib-decision-popup> -->\n\n <lib-stage-dialog [visible]=\"showStageDialogLocal\" [stageData]=\"activeStageData || {}\"\n (closed)=\"onStageDialogClosed()\" (saved)=\"onStageDialogSaved($event)\"></lib-stage-dialog>\n\n <lib-action-dialog [visible]=\"showActionDialogLocal\" [actionData]=\"activeActionData\" (closed)=\"onActionDialogClosed()\"\n (saved)=\"onActionDialogSaved($event)\" (deleted)=\"onActionDialogDeleted($event)\"></lib-action-dialog>\n\n\n <lib-escalation-dialog [visible]=\"showEscalationDialogLocal\" [type]=\"activeEscalationType()!\"\n [stageCode]=\"activeStageCode() || ''\" [workflowCode]=\"state.workflow?.Code || ''\"\n (closed)=\"onEscalationDialogClosed()\"></lib-escalation-dialog>\n</div>\n", styles: [".canvas-container{flex:1;overflow:auto;background-color:#fff}.designer-canvas{min-width:100%;min-height:100%;cursor:default}.designer-canvas:focus{outline:none}.edit-swimlane-button{cursor:pointer}.edit-swimlane-button:hover rect{fill:#ddd6fe}.decision-condition-icon{cursor:pointer}.decision-condition-icon:hover path{fill:#c084fc}.swimlane-label{z-index:10;cursor:default}.plus-icon:hover{fill:#d36cff}.swimlane-container{pointer-events:none}.swimlane-container>*{pointer-events:auto}.node-element{z-index:10}.connection-element{z-index:5}.node-drag-handle{pointer-events:all;-webkit-user-select:none;user-select:none}\n"] }]
|
|
3635
|
+
args: [{ selector: 'lib-designer-canvas', template: "<div class=\"canvas-container\">\n <svg #canvas [attr.width]=\"canvasWidth\" [attr.height]=\"canvasHeight\" class=\"designer-canvas\"\n (click)=\"onCanvasClick($event)\">\n <defs>\n <!-- Grid pattern definition -->\n\n <pattern id=\"grid\" [attr.width]=\"gridSize\" [attr.height]=\"gridSize\" patternUnits=\"userSpaceOnUse\">\n <path d=\"M 20 0 L 0 0 0 20\" fill=\"none\" stroke=\"#e2e8f0\" stroke-width=\"0.5\" />\n </pattern>\n\n <!-- Arrow head marker definition -->\n <marker id=\"arrowhead\" markerWidth=\"10\" markerHeight=\"7\" refX=\"9\" refY=\"3.5\" orient=\"auto\">\n <polygon points=\"0 0, 10 3.5, 0 7\" fill=\"#D36CFF\" />\n </marker>\n\n <!-- Connection point styles -->\n <circle id=\"connection-point-template\" r=\"5\" fill=\"#D36CFF\" stroke=\"#FFFFFF\" stroke-width=\"1\" />\n\n <!-- Dashed line style for connection preview -->\n <pattern id=\"dashed-line\" width=\"10\" height=\"10\" patternUnits=\"userSpaceOnUse\">\n <line x1=\"0\" y1=\"5\" x2=\"10\" y2=\"5\" stroke=\"#D36CFF\" stroke-width=\"2\" stroke-dasharray=\"5,5\" />\n </pattern>\n\n @for (swimlane of state.swimlanes; track swimlane.order) {\n <clipPath [attr.id]=\"'swimlane-clip-' + swimlane.order\">\n <rect x=\"0\" y=\"0\" [attr.width]=\"canvasWidth\" height=\"263\" />\n </clipPath>\n }\n </defs>\n\n <!-- Background grid -->\n <rect width=\"100%\" height=\"100%\" fill=\"url(#grid)\" />\n\n <!-- Display a message when no swimlanes exist -->\n @if (state.swimlanes.length === 0) {\n <text x=\"50%\" y=\"50%\" font-family=\"sans-serif\" font-size=\"16\" fill=\"#94a3b8\" text-anchor=\"middle\">\n Select the Swimlane tool and click on the canvas to add a swimlane\n </text>\n }\n\n <!-- This is where workflow elements will be added later -->\n @for (swimlane of state.swimlanes; track swimlane.order) {\n <!-- <g\n [attr.transform]=\"'translate(0,' + swimlane.order * 263 + ')'\"\n [attr.clip-path]=\"'url(#swimlane-clip-' + swimlane.order + ')'\"\n >\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"canvasWidth\"\n [attr.height]=\"263\"\n fill=\"#ffffff\"\n stroke=\"#e2e8f0\"\n stroke-width=\"1\"\n ></rect>\n\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"canvasWidth\"\n height=\"40\"\n fill=\"#f8fafc\"\n stroke=\"#e2e8f0\"\n stroke-width=\"1\"\n ></rect>\n\n <text\n x=\"20\"\n y=\"25\"\n font-family=\"sans-serif\"\n font-size=\"14\"\n fill=\"#333333\"\n font-weight=\"bold\"\n >\n {{ swimlane.label }}\n </text>\n\n <g\n class=\"edit-swimlane-button\"\n [attr.transform]=\"'translate(100, 20)'\"\n (click)=\"\n onEditSwimlane($event, swimlane, swimlane.order);\n $event.stopPropagation()\n \"\n >\n <rect\n x=\"-5\"\n y=\"-15\"\n width=\"40\"\n height=\"20\"\n fill=\"#f3e8ff\"\n rx=\"3\"\n ry=\"3\"\n stroke=\"#d8b4fe\"\n stroke-width=\"1\"\n ></rect>\n <text\n x=\"15\"\n y=\"0\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#7e22ce\"\n text-anchor=\"middle\"\n >\n Edit\n </text>\n </g>\n\n <g [attr.transform]=\"'translate(200, 20)'\">\n @for (tag of swimlane.tags.slice(0, 3); track tag.Name; let i = $index)\n {\n <text\n [attr.x]=\"i * 100\"\n y=\"5\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#666666\"\n >\n {{ tag.Name }}\n </text>\n } @if (swimlane.tags.length > 3) {\n <text\n [attr.x]=\"3 * 100 + 10\"\n y=\"5\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#666666\"\n >\n +{{ swimlane.tags.length - 3 }} more\n </text>\n }\n </g>\n </g> -->\n\n <g [attr.transform]=\"'translate(0,' + swimlane.order * 263 + ')'\">\n <!-- Swimlane container - remove stroke to make it purely background -->\n <rect x=\"0\" y=\"0\" [attr.width]=\"canvasWidth\" [attr.height]=\"263\"\n [attr.fill]=\"swimlane.order % 2 === 0 ? '#FCF3FF' : '#ECFFF5'\" stroke=\"none\"></rect>\n\n <!-- Vertical swimlane label on the left -->\n <g class=\"swimlane-label\">\n <!-- Purple background for the label -->\n <rect x=\"0.5\" y=\"0.5\" width=\"43\" height=\"215\" rx=\"10.5\" fill=\"white\" stroke=\"#D36CFF\"></rect>\n\n <!-- Plus button at the top -->\n <g (click)=\"\n onEditSwimlane($event, swimlane, swimlane.order);\n $event.stopPropagation()\n \">\n <rect x=\"12.5\" y=\"10\" width=\"18\" height=\"18\" fill=\"transparent\" style=\"cursor: pointer\"></rect>\n\n <path d=\"M22.52 12.174V17.08H27.47V18.51H22.52V23.394H21.2V18.51H16.25V17.08H21.2V12.174H22.52Z\"\n fill=\"#0000FF\" class=\"plus-icon\" style=\"cursor: pointer\"></path>\n </g>\n\n <!-- Swimlane label -->\n <text x=\"22\" y=\"106\" font-family=\"sans-serif\" font-size=\"14\" fill=\"#333333\" text-anchor=\"middle\"\n transform=\"rotate(-90, 22, 100)\">\n {{\n swimlane.label.length > 12\n ? swimlane.label.substring(0, 10) + \"...\"\n : swimlane.label\n }}\n </text>\n\n <!-- Tag count indicator at the bottom -->\n <rect x=\"11.5\" y=\"187\" width=\"21\" height=\"19\" rx=\"9.5\" fill=\"#9E9E9E\"></rect>\n <text x=\"24\" y=\"206\" font-family=\"sans-serif\" font-size=\"12\" fill=\"black\" text-anchor=\"middle\"\n transform=\"rotate(-90, 22, 200)\">\n {{ swimlane.tags.length }}\n </text>\n </g>\n </g>\n\n }\n\n <!-- Nodes -->\n @for (swimlane of state.swimlanes; track swimlane.order) {\n <!-- Render nodes in this swimlane -->\n @for (node of swimlane.nodes; track node.id) {\n <g class=\"node-element\" [attr.transform]=\"\n 'translate(' + node.x + ',' + (node.y + swimlane.order * 263) + ')'\"\n (mouseenter)=\"showConnectionPoints(node.id, swimlane.order)\"\n (mouseleave)=\"hideConnectionPoints(node.id)\">\n <!-- Start node indicator (circle and arrow) for the first node -->\n @if (node.isStartNode) {\n <!-- Circle -->\n <circle cx=\"-50\" cy=\"50\" r=\"30\" fill=\"none\" stroke=\"#D36CFF\" stroke-width=\"1\"></circle>\n <!-- Form icon for start node -->\n <svg:g (click)=\"\n toggleStartNodeFormPopup($event, node.x, node.y, swimlane.order)\n \" class=\"stage-icon\" transform=\"translate(-45, 42)\" style=\"cursor: pointer; pointer-events: all\">\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect x=\"-2\" y=\"-2\" width=\"24\" height=\"24\" fill=\"transparent\" style=\"cursor: pointer\" />\n <svg:path\n d=\"M16.5 20.475V17.475H13.5V16.475H16.5V13.475H17.5V16.475H20.5V17.475H17.5V20.475H16.5ZM3.5 17.5V16.5H4.5V17.5H3.5ZM6.5 17.5V16.5H11.517C11.5057 16.6767 11.5043 16.845 11.513 17.005C11.521 17.165 11.531 17.33 11.543 17.5H6.5ZM3.5 13.5V12.5H4.5V13.5H3.5ZM6.5 13.5V12.5H13.804C13.6127 12.6387 13.4333 12.7913 13.266 12.958C13.0993 13.1247 12.9377 13.3053 12.781 13.5H6.5ZM3.5 9.5V8.5H4.5V9.5H3.5ZM6.5 9.5V8.5H18.5V9.5H6.5ZM3.5 5.5V4.5H4.5V5.5H3.5ZM6.5 5.5V4.5H18.5V5.5H6.5Z\"\n [attr.fill]=\"state.workflowFormId ? '#D36CFF' : 'black'\" transform=\"scale(0.7)\" />\n </svg:g>\n <!-- Arrow from circle to node -->\n <path d=\"M -20 50 L 0 50\" stroke=\"#D36CFF\" stroke-width=\"1\" marker-end=\"url(#arrowhead)\"></path>\n }\n\n <!-- Exit point indicator (circle and arrow) -->\n @if (node.stageData?.IsExitPoint) {\n <!-- Arrow from node to circle -->\n <path [attr.d]=\"'M ' + node.width + ' 50 L ' + (node.width + 20) + ' 50'\" stroke=\"#D36CFF\" stroke-width=\"1\"\n marker-end=\"url(#arrowhead)\"></path>\n <!-- Circle -->\n <circle [attr.cx]=\"node.width + 50\" cy=\"50\" r=\"30\" fill=\"none\" stroke=\"#D36CFF\" stroke-width=\"2\"></circle>\n }\n\n <!-- Inner drag-handle: inset 12px so the node edge ring stays drag-free.\n Rendered BEFORE the node shapes/icons so the clickable icons paint on\n top of it and win pointer events; the node body still falls through to\n this handle because its shape/label use pointer-events:none.\n Connection point <use> elements are rendered after everything so they\n stay topmost and win pointer events at the node edges. -->\n <rect class=\"node-drag-handle\"\n [attr.x]=\"12\" [attr.y]=\"12\"\n [attr.width]=\"node.width - 24\" [attr.height]=\"node.height - 24\"\n fill=\"transparent\"\n [style.cursor]=\"getCursorForNode(node.id)\"\n (mousedown)=\"onNodeMouseDown($event, node, swimlane.order)\" />\n\n <!-- Stage node -->\n @if (node.type === 'stage') {\n <!-- <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></rect> -->\n <svg:g lib-stage-node [node]=\"node\" [isStartNode]=\"node.isStartNode\"\n (stagePropertiesUpdated)=\"onStagePropertiesUpdated($event)\" (showShieldDialog)=\"onShowShieldDialog($event)\"\n (showEscalationDialog)=\"onShowEscalationDialog($event)\" (showFormDialog)=\"\n toggleStartNodeFormPopup($event, node.x, node.y, swimlane.order, node)\n \"></svg:g>\n }\n\n <!-- Decision node -->\n @if (node.type === 'decision') {\n <path [attr.d]=\"\n 'M 0 ' +\n node.height / 2 +\n ' L ' +\n node.width / 2 +\n ' 0' +\n ' L ' +\n node.width +\n ' ' +\n node.height / 2 +\n ' L ' +\n node.width / 2 +\n ' ' +\n node.height +\n ' Z'\n \" fill=\"none\" stroke=\"#D36CFF\" stroke-width=\"1\" style=\"pointer-events: none\"></path>\n\n <!-- Decision node condition indicator icon -->\n <svg:g (click)=\"showDecisionConditionsPopup($event, node, swimlane.order)\" class=\"decision-condition-icon\"\n [attr.transform]=\"\n 'translate(' +\n (node.width / 2 - 10) +\n ',' +\n (node.height / 2 - 10) +\n ')'\n \">\n <svg:rect x=\"-5\" y=\"-5\" width=\"30\" height=\"30\" fill=\"transparent\" style=\"cursor: pointer\" />\n <svg:path d=\"M10 0H20V2H10V0ZM10 8H20V10H10V8ZM10 4H20V6H10V4ZM0 0H8V2H0V0ZM0 8H8V10H0V8ZM0 4H8V6H0V4Z\"\n fill=\"#D36CFF\" />\n </svg:g>\n }\n\n <!-- Form node -->\n @if (node.type === 'form') {\n <path\n d=\"M95.0625 50.5591V95.0625C95.0625 97.9716 93.9069 100.762 91.8498 102.819C89.7928 104.876 87.0028 106.031 84.0938 106.031H32.9062C29.9972 106.031 27.2072 104.876 25.1502 102.819C23.0931 100.762 21.9375 97.9716 21.9375 95.0625V21.9375C21.9375 19.0284 23.0931 16.2385 25.1502 14.1814C27.2072 12.1244 29.9972 10.9688 32.9062 10.9688H55.4722C57.4109 10.969 59.2701 11.7392 60.6412 13.1099L92.9213 45.3901C94.292 46.7611 95.0622 48.6204 95.0625 50.5591Z\"\n transform=\"scale(0.7)\" fill=\"none\" stroke=\"#D36CFF\" stroke-linejoin=\"round\" style=\"pointer-events: none\" />\n <path\n d=\"M58.5 12.7969V40.2188C58.5 42.1581 59.2704 44.0181 60.6418 45.3895C62.0131 46.7608 63.8731 47.5312 65.8125 47.5312H93.2344\"\n transform=\"scale(0.7)\" stroke=\"#D36CFF\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"pointer-events: none\" />\n <text x=\"50%\" y=\"50%\" text-anchor=\"middle\" dominant-baseline=\"middle\" font-family=\"sans-serif\" font-size=\"14\"\n fill=\"#D36CFF\" style=\"pointer-events: none\">\n Form\n </text>\n }\n\n <!-- Subflow node -->\n @if (node.type === 'subflow') {\n <svg:g lib-stage-node [node]=\"node\" [isStartNode]=\"node.isStartNode\" [isSubflow]=\"true\"\n (stagePropertiesUpdated)=\"onStagePropertiesUpdated($event)\" (showShieldDialog)=\"onShowShieldDialog($event)\"\n (showEscalationDialog)=\"onShowEscalationDialog($event)\" (showFormDialog)=\"\n toggleStartNodeFormPopup($event, node.x, node.y, swimlane.order, node)\n \"></svg:g>\n }\n\n <!-- Connection points for this node (rendered last = topmost) -->\n @for (point of node.connectionPoints || []; track point.id) {\n @if (isConnectionPointVisible(node.id)) {\n <use [attr.href]=\"'#connection-point-template'\" [attr.x]=\"point.x\" [attr.y]=\"point.y\" [attr.id]=\"point.id\"\n (mousedown)=\"startConnectionDrag($event, point, swimlane.order)\" />\n }\n }\n </g>\n <!-- A transparent hover area for improved hover detection -->\n <rect [attr.x]=\"node.x - 10\" [attr.y]=\"node.y - 10\" [attr.width]=\"node.width + 20\" [attr.height]=\"node.height + 20\"\n fill=\"transparent\" (mouseover)=\"showConnectionPoints(node.id, swimlane.order)\"\n (mouseout)=\"hideConnectionPoints(node.id)\" style=\"pointer-events: none\" />\n } }\n\n <!-- Connections -->\n @for (connection of state.connections; track connection.id) {\n <g class=\"connection-element\">\n <path [attr.d]=\"getConnectionPathForSavedConnection(connection)\" stroke=\"#D36CFF\" stroke-width=\"2\" fill=\"none\"\n marker-end=\"url(#arrowhead)\"></path>\n\n <!-- Connection Label -->\n @if (connection.sourceNodeId && connection.targetNodeId) {\n <foreignObject [attr.x]=\"getLabelPosition(connection).x\" [attr.y]=\"getLabelPosition(connection).y\" width=\"150\"\n height=\"40\" style=\"overflow: visible; pointer-events: all;\" [attr.transform]=\"'translate(-75, -10)'\">\n <wf-label-editor [label]=\"connection.label || 'New Action'\" [x]=\"0\" [y]=\"0\" [isConnection]=\"true\"\n (labelChange)=\"saveConnectionLabel(connection, $event)\" (openDialog)=\"onShowActionDialog(connection)\" />\n </foreignObject>\n }\n </g>\n }\n\n <!-- Connection preview line -->\n @if (state.isConnectionDragging()) {\n <g>\n <path [attr.d]=\"getConnectionPath()\" stroke=\"#D36CFF\" stroke-width=\"2\" stroke-dasharray=\"5,5\" fill=\"none\"></path>\n </g>\n }\n </svg>\n\n <div *ngIf=\"popupVisible\" [style.position]=\"'absolute'\" [style.left.px]=\"popupX\" [style.top.px]=\"popupY\">\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '99' }\" [enableMouseLeave]=\"false\"\n (dropdownOpenChange)=\"$event ? null : hideConnectionPopup()\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div class=\"p-3 bg-white border border-gray-200 rounded shadow-md\" dropdown-content>\n <h4 class=\"mb-2 font-medium\">Create Connection</h4>\n <div class=\"flex flex-col gap-2\">\n <ng-container *ngFor=\"let type of allowedNodeTypes\">\n <button class=\"px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50\"\n (click)=\"createNodeConnection(type)\">\n Create {{ type | titlecase }}\n </button>\n </ng-container>\n <button class=\"px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50\"\n (click)=\"hideConnectionPopup()\">\n Cancel\n </button>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <div *ngIf=\"showStartNodeFormPopup\" [style.position]=\"'absolute'\" [style.left.px]=\"startNodeFormPopupX\"\n [style.top.px]=\"startNodeFormPopupY\">\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\" dropdown-content\n style=\"background-color: white; z-index: 1000\">\n <h4 class=\"mb-2 font-medium\">Select Workflow Form</h4>\n <div *ngIf=\"isLoadingStartNodeForms\" class=\"text-center py-2\">\n Loading forms...\n </div>\n <div *ngIf=\"!isLoadingStartNodeForms\" class=\"max-h-48 overflow-y-auto\" style=\"max-height: 192px; overflow-y: auto;\">\n <div class=\"mb-3\">\n <button\n class=\"w-full px-3 py-2 bg-gray-50 border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectStartNodeForm(null)\">\n Clear form selection\n </button>\n </div>\n <div *ngFor=\"let form of startNodeFormsList\" class=\"mb-1\">\n <button class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n [ngClass]=\"{ 'bg-blue-50 !border-blue-500 !border-2 font-semibold': form?.Code === activeNodeForFormSelection?.stageData?.formId}\"\n (click)=\"selectStartNodeForm(form)\">\n {{ form.Name }}\n </button>\n </div>\n <div *ngIf=\"startNodeFormsList.length === 0\" class=\"text-center py-2 text-gray-500\">\n No forms available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <div *ngIf=\"showSubflowPopup\" [style.position]=\"'absolute'\" [style.left.px]=\"subflowPopupX\"\n [style.top.px]=\"subflowPopupY\">\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\" dropdown-content\n style=\"background-color: white; z-index: 1000\">\n <h4 class=\"mb-2 font-medium\">Select Workflow</h4>\n <div *ngIf=\"isLoadingWorkflows\" class=\"text-center py-2\">\n Loading workflows...\n </div>\n <div *ngIf=\"!isLoadingWorkflows\" class=\"max-h-48 overflow-y-auto\" style=\"max-height: 192px; overflow-y: auto;\">\n <div *ngFor=\"let workflow of workflowsList\" class=\"mb-1\">\n <button class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectSubflowWorkflow(workflow)\">\n {{ workflow.Name }}\n </button>\n </div>\n <div *ngIf=\"workflowsList.length === 0\" class=\"text-center py-2 text-gray-500\">\n No workflows available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <lib-conditions-popup [visible]=\"showDecisionConditionDialog\" [decisionNodeId]=\"activeDecisionNodeId\"\n [newConnectionId]=\"activeConnectionId\" [popupX]=\"decisionConditionPopupX\" [popupY]=\"decisionConditionPopupY\"\n (closed)=\"onDecisionConditionCancelled()\" (saved)=\"onDecisionConditionSaved($event)\"></lib-conditions-popup>\n\n <!-- <lib-decision-popup\n [visible]=\"showConditionsDialog\"\n [decisionNodeId]=\"activeDecisionNodeId\"\n [popupX]=\"decisionConditionPopupX\"\n [popupY]=\"decisionConditionPopupY\"\n (closed)=\"onDecisionConditionCancelled()\"\n ></lib-decision-popup> -->\n\n <lib-stage-dialog [visible]=\"showStageDialogLocal\" [stageData]=\"activeStageData || {}\"\n (closed)=\"onStageDialogClosed()\" (saved)=\"onStageDialogSaved($event)\"></lib-stage-dialog>\n\n <lib-action-dialog [visible]=\"showActionDialogLocal\" [actionData]=\"activeActionData\" (closed)=\"onActionDialogClosed()\"\n (saved)=\"onActionDialogSaved($event)\" (deleted)=\"onActionDialogDeleted($event)\"></lib-action-dialog>\n\n\n <lib-escalation-dialog [visible]=\"showEscalationDialogLocal\" [type]=\"activeEscalationType()!\"\n [stageCode]=\"activeStageCode() || ''\" [workflowCode]=\"state.workflow?.Code || ''\"\n (closed)=\"onEscalationDialogClosed()\"></lib-escalation-dialog>\n</div>\n", styles: [".canvas-container{flex:1;overflow:auto;background-color:#fff}.designer-canvas{min-width:100%;min-height:100%;cursor:default}.designer-canvas:focus{outline:none}.edit-swimlane-button{cursor:pointer}.edit-swimlane-button:hover rect{fill:#ddd6fe}.decision-condition-icon{cursor:pointer}.decision-condition-icon:hover path{fill:#c084fc}.swimlane-label{z-index:10;cursor:default}.plus-icon:hover{fill:#d36cff}.swimlane-container{pointer-events:none}.swimlane-container>*{pointer-events:auto}.node-element{z-index:10}.connection-element{z-index:5}.node-drag-handle{pointer-events:all;-webkit-user-select:none;user-select:none}\n"] }]
|
|
3636
3636
|
}], ctorParameters: () => [{ type: WorkflowDesignerState }, { type: WorkflowDataService }], propDecorators: { selectedTool: [{
|
|
3637
3637
|
type: Input
|
|
3638
3638
|
}], clickedPosition: [{
|