@libs-ui/services-diagram-draw 0.2.356-37
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/README.md +279 -0
- package/canvas.service.d.ts +42 -0
- package/diagram-draw.service.d.ts +91 -0
- package/direction.service.d.ts +43 -0
- package/esm2022/canvas.service.mjs +628 -0
- package/esm2022/diagram-draw.service.mjs +711 -0
- package/esm2022/direction.service.mjs +255 -0
- package/esm2022/index.mjs +6 -0
- package/esm2022/interfaces/canvas.interface.mjs +2 -0
- package/esm2022/interfaces/coordinates.interface.mjs +2 -0
- package/esm2022/interfaces/diagram.interface.mjs +2 -0
- package/esm2022/libs-ui-services-diagram-draw.mjs +5 -0
- package/esm2022/utils/calculator-branch.util.mjs +187 -0
- package/esm2022/utils/calculator-element.util.mjs +335 -0
- package/esm2022/utils/canvas.util.mjs +176 -0
- package/esm2022/utils/diagram.util.mjs +54 -0
- package/esm2022/utils/direction.util.mjs +103 -0
- package/esm2022/utils/horizontal/calculator-branch.util.mjs +243 -0
- package/esm2022/utils/horizontal/calculator-coordinates.util.mjs +315 -0
- package/fesm2022/libs-ui-services-diagram-draw.mjs +2982 -0
- package/fesm2022/libs-ui-services-diagram-draw.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/interfaces/canvas.interface.d.ts +21 -0
- package/interfaces/coordinates.interface.d.ts +40 -0
- package/interfaces/diagram.interface.d.ts +109 -0
- package/package.json +27 -0
- package/utils/calculator-branch.util.d.ts +16 -0
- package/utils/calculator-element.util.d.ts +24 -0
- package/utils/canvas.util.d.ts +21 -0
- package/utils/diagram.util.d.ts +6 -0
- package/utils/direction.util.d.ts +6 -0
- package/utils/horizontal/calculator-branch.util.d.ts +67 -0
- package/utils/horizontal/calculator-coordinates.util.d.ts +42 -0
|
@@ -0,0 +1,2982 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { signal, Injectable, inject } from '@angular/core';
|
|
3
|
+
import { setStylesElement, cloneDeep, uuid } from '@libs-ui/utils';
|
|
4
|
+
import { TranslateService } from '@ngx-translate/core';
|
|
5
|
+
import { Subject } from 'rxjs';
|
|
6
|
+
|
|
7
|
+
class MoLibDiagramCalculatorBranchUtil {
|
|
8
|
+
static drawStraightLine(left, lineFormElementToBranching, top, branch, maxHeightBranch, config) {
|
|
9
|
+
const startDraw = top + lineFormElementToBranching;
|
|
10
|
+
let Ly = startDraw;
|
|
11
|
+
Ly += canvasConfigReadonly().ELEMENT_HEIGHT_CURVE; // chiều dài đến element đầu tiên
|
|
12
|
+
Ly += getDistanceFromBranchToFirstBlock(branch.nodeOtherConfig); // chiều dài đến element đầu tiên
|
|
13
|
+
if (!branch.elements || !branch.elements.length) {
|
|
14
|
+
Ly += maxHeightBranch;
|
|
15
|
+
Ly += 20; // thêm đoạn nối từ điểm kết thúc nhánh đến hết
|
|
16
|
+
}
|
|
17
|
+
const coord1 = {
|
|
18
|
+
mx: left,
|
|
19
|
+
my: top,
|
|
20
|
+
ly: Ly,
|
|
21
|
+
lx: left,
|
|
22
|
+
};
|
|
23
|
+
branch.attributeSvgD = `M ${coord1.mx} ${coord1.my} L ${coord1.lx} ${coord1.ly}`;
|
|
24
|
+
if (branch.nodeOtherConfig) {
|
|
25
|
+
const endYToWait = startDraw + canvasConfigReadonly().ELEMENT_HEIGHT_CURVE + canvasConfigReadonly().DISTANCE_TO_WAIT;
|
|
26
|
+
branch.attributeSvgD = `M ${coord1.mx} ${coord1.my} L ${coord1.mx} ${endYToWait}`;
|
|
27
|
+
const waitHeight = getHeightWaitConfig(branch.nodeOtherConfig);
|
|
28
|
+
const topStartLine2 = endYToWait + waitHeight;
|
|
29
|
+
delete branch.nodeOtherConfig.attributeSvgD;
|
|
30
|
+
if (branch.elements && branch.elements.length) {
|
|
31
|
+
branch.nodeOtherConfig.attributeSvgD = `M ${coord1.mx} ${topStartLine2} L ${coord1.mx} ${coord1.ly}`;
|
|
32
|
+
}
|
|
33
|
+
branch.nodeOtherConfig.specific_y = endYToWait;
|
|
34
|
+
branch.nodeOtherConfig.specific_x = coord1.mx;
|
|
35
|
+
}
|
|
36
|
+
branch.specific_start_branch_y = top + lineFormElementToBranching + canvasConfigReadonly().ELEMENT_HEIGHT_CURVE;
|
|
37
|
+
branch.specific_start_branch_x = left;
|
|
38
|
+
if (!branch.elements || !branch.elements.length) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const heightToElement = canvasConfigReadonly().ELEMENT_HEIGHT_CURVE + getDistanceFromBranchToFirstBlock(branch.nodeOtherConfig); // chiều dài đến element đầu tiên;
|
|
42
|
+
branch.elements.forEach((elementBranch) => {
|
|
43
|
+
elementBranch.specific_y = top + lineFormElementToBranching + heightToElement;
|
|
44
|
+
elementBranch.specific_x = left - (elementBranch.specific_width ?? 0) / 2;
|
|
45
|
+
});
|
|
46
|
+
MoLibDiagramCalculatorCoordinatesUtil.setXYElements(branch.elements, config);
|
|
47
|
+
}
|
|
48
|
+
static leftLine(left, lineFormElementToBranching, top, lineBetweenBranch, branch, maxHeightBranch, element, config) {
|
|
49
|
+
const { coordQL2, isCheckAllElementConnectToExit } = this.drawAboveUnderLine(left, lineFormElementToBranching, top, lineBetweenBranch, branch, maxHeightBranch, 'left', config);
|
|
50
|
+
branch.specific_start_branch_x = coordQL2.x;
|
|
51
|
+
branch.specific_start_branch_y = coordQL2.y;
|
|
52
|
+
if (!branch.elements || !branch.elements.length) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
this.drawElementInBranchLine(branch, coordQL2, config);
|
|
56
|
+
if (isCheckAllElementConnectToExit) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
static rightLine(left, lineFormElementToBranching, top, lineBetweenBranch, branch, maxHeightBranch, element, config) {
|
|
61
|
+
const { coordQL1, coordQL2, isCheckAllElementConnectToExit } = this.drawAboveUnderLine(left, lineFormElementToBranching, top, lineBetweenBranch, branch, maxHeightBranch, 'right', config);
|
|
62
|
+
branch.specific_start_branch_x = coordQL2.x;
|
|
63
|
+
branch.specific_start_branch_y = coordQL2.y;
|
|
64
|
+
const maxY = (coordQL1.y ?? 0) + 200;
|
|
65
|
+
const configSvg = config.widthHeightSvgCanvas;
|
|
66
|
+
configSvg.heightSvg = maxY > configSvg.heightSvg ? maxY : configSvg.heightSvg;
|
|
67
|
+
if (!branch.elements || !branch.elements.length) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
this.drawElementInBranchLine(branch, coordQL2, config);
|
|
71
|
+
if (isCheckAllElementConnectToExit) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
static drawElementInBranchLine(branch, coordQL2, config) {
|
|
76
|
+
const topElementInBranch = coordQL2.ly; // bắt đầu của 1 emelent trong nhánh sẽ là điểm cuối cùng của nhánh trỏ tới
|
|
77
|
+
const elementFirst = branch.elements[0];
|
|
78
|
+
elementFirst.specific_y = topElementInBranch;
|
|
79
|
+
elementFirst.specific_x = (coordQL2.lx ?? 0) - (elementFirst.specific_width ?? 0) / 2;
|
|
80
|
+
MoLibDiagramCalculatorCoordinatesUtil.setXYElements(branch.elements, config);
|
|
81
|
+
}
|
|
82
|
+
static drawMissingLineFromLastElement(branch, coordQ3) {
|
|
83
|
+
const { mx, my } = coordQ3;
|
|
84
|
+
if (!my) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const lastElementInBranch = branch.elements[branch.elements.length - 1];
|
|
88
|
+
if (!lastElementInBranch.branches?.length) {
|
|
89
|
+
branch.attributeSvgD = `${branch.attributeSvgD} M ${mx} ${my} L ${mx} ${lastElementInBranch.position_end ?? 0}`;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const ly = (lastElementInBranch.position_end_branch ?? 0) - canvasConfigReadonly().ELEMENT_HEIGHT_CURVE;
|
|
93
|
+
if (ly === my) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
branch.attributeSvgD = `${branch.attributeSvgD} M ${mx} ${my} L ${mx} ${ly}`;
|
|
97
|
+
}
|
|
98
|
+
static drawAboveUnderLine(left, lineFormElementToBranching, top, lineBetweenBranch, branch, maxHeightBranch, direction, config) {
|
|
99
|
+
const M = `M ${left} ${top}`; // đặt bút vẽ từ điểm x, y
|
|
100
|
+
const Ly = top + lineFormElementToBranching - canvasConfigReadonly().ELEMENT_HEIGHT_CURVE;
|
|
101
|
+
const L = `L ${left} ${Ly}`; // Kéo dài 1 đường thẳng tới X, Y,
|
|
102
|
+
const straightLineBetweenBranch = lineBetweenBranch - canvasConfigReadonly().ELEMENT_HEIGHT_CURVE * 2;
|
|
103
|
+
const coordQL1 = {
|
|
104
|
+
// điểm cong số 1 của nhánh và đường thẳng kéo trên dưới
|
|
105
|
+
mx: left,
|
|
106
|
+
my: Ly,
|
|
107
|
+
x1: left,
|
|
108
|
+
y1: Ly + canvasConfigReadonly().ELEMENT_HEIGHT_CURVE,
|
|
109
|
+
x: this.mathOperatorsCalculation(direction, left, canvasConfigReadonly().ELEMENT_HEIGHT_CURVE, true),
|
|
110
|
+
y: Ly + canvasConfigReadonly().ELEMENT_HEIGHT_CURVE,
|
|
111
|
+
};
|
|
112
|
+
coordQL1.ly = coordQL1.y;
|
|
113
|
+
// đoạn thẳng kéo dài lineBetweenBranch phải trừ đi hai đầu cong
|
|
114
|
+
coordQL1.lx = this.mathOperatorsCalculation(direction, coordQL1.x, straightLineBetweenBranch, true);
|
|
115
|
+
const QL1 = `M ${coordQL1.mx},${coordQL1.my} Q ${coordQL1.x1},${coordQL1.y1} ${coordQL1.x},${coordQL1.y} L ${coordQL1.lx} ${coordQL1.ly}`;
|
|
116
|
+
const coordQL2 = {
|
|
117
|
+
// điểm cong số 2 của nhánh và đường thẳng đứng
|
|
118
|
+
y1: coordQL1.ly,
|
|
119
|
+
x1: this.mathOperatorsCalculation(direction, coordQL1.lx, canvasConfigReadonly().ELEMENT_HEIGHT_CURVE, true),
|
|
120
|
+
y: (coordQL1.ly ?? 0) + canvasConfigReadonly().ELEMENT_HEIGHT_CURVE,
|
|
121
|
+
x: this.mathOperatorsCalculation(direction, coordQL1.lx, canvasConfigReadonly().ELEMENT_HEIGHT_CURVE, true),
|
|
122
|
+
};
|
|
123
|
+
coordQL2.ly = (coordQL2.y ?? 0) + getDistanceFromBranchToFirstBlock(branch.nodeOtherConfig);
|
|
124
|
+
coordQL2.lx = coordQL2.x;
|
|
125
|
+
if (!branch.elements || !branch.elements.length) {
|
|
126
|
+
coordQL2.ly = maxHeightBranch + (coordQL2.ly ?? 0); // tổng chiều cao của nhánh + điểm xuất phát
|
|
127
|
+
}
|
|
128
|
+
let QL2 = `Q ${coordQL2.x1},${coordQL2.y1} ${coordQL2.x},${coordQL2.y} L ${coordQL2.lx} ${coordQL2.ly}`;
|
|
129
|
+
if (branch.nodeOtherConfig) {
|
|
130
|
+
const endYToWait = (coordQL2.y ?? 0) + canvasConfigReadonly().DISTANCE_TO_WAIT; // xử lý các line vào first element
|
|
131
|
+
const waitHeight = getHeightWaitConfig(branch.nodeOtherConfig);
|
|
132
|
+
const topStartLine2 = endYToWait + waitHeight;
|
|
133
|
+
delete branch.nodeOtherConfig.attributeSvgD;
|
|
134
|
+
QL2 = `Q ${coordQL2.x1},${coordQL2.y1} ${coordQL2.x},${coordQL2.y} M ${coordQL2.x},${coordQL2.y} L ${coordQL2.x},${endYToWait}`;
|
|
135
|
+
if (branch.elements && branch.elements.length) {
|
|
136
|
+
branch.nodeOtherConfig.attributeSvgD = `M ${coordQL2.x} ${topStartLine2} L ${coordQL2.x} ${coordQL2.ly}`;
|
|
137
|
+
}
|
|
138
|
+
branch.nodeOtherConfig.specific_y = endYToWait;
|
|
139
|
+
branch.nodeOtherConfig.specific_x = coordQL2.x;
|
|
140
|
+
}
|
|
141
|
+
branch.positionMaxLeft = coordQL2.lx;
|
|
142
|
+
branch.attributeSvgD = `${M} ${L} ${QL1} ${QL2}`;
|
|
143
|
+
const isCheckAllElementConnectToExit = this.checkChildrenConnectToExit(branch.elements);
|
|
144
|
+
checkMaxLeft(config, branch.nodeOtherConfig?.specific_x, branch.positionMaxLeft);
|
|
145
|
+
return { coordQL1, coordQL2, isCheckAllElementConnectToExit };
|
|
146
|
+
}
|
|
147
|
+
static checkChildrenConnectToExit(elements) {
|
|
148
|
+
let toExit = false;
|
|
149
|
+
if (elements.length && (elements[elements.length - 1].subCodeOfElement || elements[elements.length - 1].code) === canvasConfigReadonly().TYPE_ELEMENT_EXIT) {
|
|
150
|
+
toExit = true;
|
|
151
|
+
return toExit;
|
|
152
|
+
}
|
|
153
|
+
elements.forEach((element) => {
|
|
154
|
+
if (!element.branches || !element.branches.length) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
let branchConnectToExit = 0;
|
|
158
|
+
element.branches.forEach((branch) => {
|
|
159
|
+
if (this.checkChildrenConnectToExit(branch.elements)) {
|
|
160
|
+
branchConnectToExit++;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
if (branchConnectToExit === element.branches.length) {
|
|
164
|
+
toExit = true;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
return toExit;
|
|
168
|
+
}
|
|
169
|
+
/* above, left a + b; opposite = true a - b;
|
|
170
|
+
under, right a - b; opposite = true a + b
|
|
171
|
+
*/
|
|
172
|
+
static mathOperatorsCalculation(direction, a, b, opposite) {
|
|
173
|
+
switch (direction) {
|
|
174
|
+
case 'left':
|
|
175
|
+
case 'above':
|
|
176
|
+
if (opposite) {
|
|
177
|
+
return (a ?? 0) - (b ?? 0);
|
|
178
|
+
}
|
|
179
|
+
return (a ?? 0) + (b ?? 0);
|
|
180
|
+
case 'right':
|
|
181
|
+
case 'under':
|
|
182
|
+
if (opposite) {
|
|
183
|
+
return (a ?? 0) + (b ?? 0);
|
|
184
|
+
}
|
|
185
|
+
return (a ?? 0) - (b ?? 0);
|
|
186
|
+
default:
|
|
187
|
+
return (a ?? 0) - (b ?? 0);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const buildFlatElement = (elements, flatElementsContainer) => {
|
|
193
|
+
elements.forEach((element) => {
|
|
194
|
+
flatElementsContainer.push(element);
|
|
195
|
+
if (!element.branches || !element.branches.length) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
flatElementHasBranch(element, flatElementsContainer);
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
const flatElementHasBranch = (element, flatElementsContainer) => {
|
|
202
|
+
if (!element.branches || !element.branches.length) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
element.branches.forEach((branch) => {
|
|
206
|
+
if (!branch.elements || !branch.elements.length) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
buildFlatElement(branch.elements, flatElementsContainer);
|
|
210
|
+
});
|
|
211
|
+
};
|
|
212
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
213
|
+
const setAttributeSvgAndAppend = (svgElement, elementSvg, svgAttributePathD, color) => {
|
|
214
|
+
elementSvg?.setAttribute('d', svgAttributePathD);
|
|
215
|
+
elementSvg?.setAttribute('stroke', `#${color ?? canvasConfigReadonly().ELEMENT_SVG_STROKE_COLOR}`);
|
|
216
|
+
elementSvg?.setAttribute('fill', 'none');
|
|
217
|
+
elementSvg?.setAttribute('stroke-width', canvasConfigReadonly().ELEMENT_SVG_STROKE_WIDTH);
|
|
218
|
+
svgElement?.nativeElement.appendChild(elementSvg);
|
|
219
|
+
};
|
|
220
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
221
|
+
const canvasRemoveChildInSvg = (svgElement) => {
|
|
222
|
+
if (!svgElement) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const children = Array.from(svgElement?.nativeElement.children);
|
|
226
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
227
|
+
children.forEach((child) => {
|
|
228
|
+
if (child.tagName !== 'path') {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
child.remove();
|
|
232
|
+
});
|
|
233
|
+
};
|
|
234
|
+
const applyElementStyle = (el, element) => {
|
|
235
|
+
el.style.position = 'absolute';
|
|
236
|
+
el.style.top = '0';
|
|
237
|
+
el.style.left = '0';
|
|
238
|
+
el.style.transform = `translate(${element.translateX}px, ${element.translateY}px)`;
|
|
239
|
+
el.style.width = `${element.specific_width}px`;
|
|
240
|
+
el.style.height = `${element.specific_height}px`;
|
|
241
|
+
el.style.zIndex = '1';
|
|
242
|
+
el.style.willChange = 'transform';
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
class MoLibDiagramCalculatorCoordinatesUtil {
|
|
246
|
+
static setXYElements(elements, config, positionX) {
|
|
247
|
+
if (!elements || !elements.length) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
elements.forEach((currentElement, index) => {
|
|
251
|
+
currentElement.specific_x = (currentElement.specific_x ?? 0) + (positionX ?? 0);
|
|
252
|
+
const preElement = elements[index - 1];
|
|
253
|
+
if (preElement) {
|
|
254
|
+
currentElement.specific_x = (preElement.specific_x ?? 0) + (preElement.specific_width ?? 0) / 2 - (currentElement.specific_width ?? 0) / 2;
|
|
255
|
+
currentElement.specific_y = (preElement.specific_y ?? 0) + (preElement.specific_height ?? 0) + checkElementGetHeight(preElement.nodeOtherConfig);
|
|
256
|
+
if (preElement.position_end_branch) {
|
|
257
|
+
currentElement.specific_y = preElement.position_end_branch;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const top = currentElement.specific_y ?? 0;
|
|
261
|
+
const left = (currentElement.specific_x ?? 0) + (currentElement.specific_width ?? 0) / 2;
|
|
262
|
+
let lineBetweenElements = (currentElement.specific_height ?? 0) + checkElementGetHeight(currentElement.nodeOtherConfig);
|
|
263
|
+
if (currentElement.branches && currentElement.branches.length) {
|
|
264
|
+
lineBetweenElements = (currentElement.specific_height ?? 0) + canvasConfigReadonly().ELEMENT_MARGIN_DEFAULT_BRANCH;
|
|
265
|
+
}
|
|
266
|
+
const maxY = (currentElement.specific_y ?? 0) + (currentElement.specific_height ?? 0) + 200;
|
|
267
|
+
const maxXLeft = (currentElement.specific_x ?? 0) + (currentElement.specific_width ?? 0) + 100;
|
|
268
|
+
const configSvg = config.widthHeightSvgCanvas;
|
|
269
|
+
configSvg.heightSvg = maxY > configSvg.heightSvg ? maxY : configSvg.heightSvg;
|
|
270
|
+
configSvg.widthSvg = maxXLeft > configSvg.widthSvg ? maxXLeft : configSvg.widthSvg;
|
|
271
|
+
checkMaxLeft(config, currentElement.specific_x, currentElement.branches?.[0].positionMaxLeft);
|
|
272
|
+
if (!currentElement.branches || !currentElement.branches.length) {
|
|
273
|
+
// các element không có nhánh mới vẽ đường thẳng
|
|
274
|
+
const drawFormToHeightExit = (currentElement.subCodeOfElement || currentElement.code) === canvasConfigReadonly().TYPE_ELEMENT_EXIT ? (currentElement.specific_height ?? 0) / 2 : lineBetweenElements;
|
|
275
|
+
currentElement.attributeSvgD = `M ${left} ${top} l 0 ${drawFormToHeightExit}`;
|
|
276
|
+
currentElement.position_end = top + lineBetweenElements;
|
|
277
|
+
if (currentElement.nodeOtherConfig) {
|
|
278
|
+
const waitHeight = getHeightWaitConfig(currentElement.nodeOtherConfig);
|
|
279
|
+
const distanceElementToWait = (currentElement.specific_height ?? 0) + canvasConfigReadonly().DISTANCE_TO_WAIT;
|
|
280
|
+
const topStartLine2 = top + distanceElementToWait + waitHeight;
|
|
281
|
+
currentElement.attributeSvgD = `M ${left} ${top} l 0 ${distanceElementToWait}`;
|
|
282
|
+
currentElement.position_end = topStartLine2 + lineBetweenElements;
|
|
283
|
+
currentElement.nodeOtherConfig.specific_y = top + distanceElementToWait;
|
|
284
|
+
currentElement.nodeOtherConfig.specific_x = left;
|
|
285
|
+
delete currentElement.nodeOtherConfig.attributeSvgD;
|
|
286
|
+
if (currentElement.subCodeOfElement !== canvasConfigReadonly().TYPE_ELEMENT_EXIT) {
|
|
287
|
+
currentElement.nodeOtherConfig.attributeSvgD = `M ${left} ${topStartLine2} l 0 ${canvasConfigReadonly().WAIT_TO_ELEMENT}`;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
// start các element có nhánh
|
|
293
|
+
const half = Math.ceil(currentElement.branches.length / 2);
|
|
294
|
+
const aboveHalf = currentElement.branches.slice(0, half);
|
|
295
|
+
const underHalf = currentElement.branches.slice(half);
|
|
296
|
+
let brachMiddle;
|
|
297
|
+
if (aboveHalf.length !== underHalf.length) {
|
|
298
|
+
brachMiddle = aboveHalf.pop();
|
|
299
|
+
}
|
|
300
|
+
// TÍNH CHIỀU Cao TỐI ĐA CỦA NHÁNH
|
|
301
|
+
const maxHeightBranch = {
|
|
302
|
+
height: 0,
|
|
303
|
+
};
|
|
304
|
+
MoLibDiagramCalculatorCoordinatesUtil.calculatorMaxBranchHeight(currentElement, maxHeightBranch);
|
|
305
|
+
currentElement.maxHeightBranch = maxHeightBranch.height;
|
|
306
|
+
const maxWidthBrachMiddle = { width: 0, aboveWidth: 0, underWidth: 0 };
|
|
307
|
+
const lineFormElementToBranching = (currentElement.specific_height ?? 0) + canvasConfigReadonly().ELEMENT_MARGIN_DEFAULT_BRANCH;
|
|
308
|
+
if (brachMiddle) {
|
|
309
|
+
this.calculatorWidthBranch(brachMiddle.elements, maxWidthBrachMiddle);
|
|
310
|
+
brachMiddle.widthBranch = {
|
|
311
|
+
width: maxWidthBrachMiddle.width,
|
|
312
|
+
above: maxWidthBrachMiddle.aboveWidth,
|
|
313
|
+
under: maxWidthBrachMiddle.underWidth,
|
|
314
|
+
};
|
|
315
|
+
MoLibDiagramCalculatorBranchUtil.drawStraightLine(left, lineFormElementToBranching, top, brachMiddle, maxHeightBranch.height, config);
|
|
316
|
+
}
|
|
317
|
+
let hasNextSameParentElementNext = false;
|
|
318
|
+
const aboveHalfReverse = aboveHalf.reverse();
|
|
319
|
+
aboveHalfReverse.forEach((branch, indexBranch) => {
|
|
320
|
+
if (!hasNextSameParentElementNext) {
|
|
321
|
+
const flatElementOnBranch = [];
|
|
322
|
+
buildFlatElement(branch.elements, flatElementOnBranch);
|
|
323
|
+
hasNextSameParentElementNext = !flatElementOnBranch || !flatElementOnBranch.length ? true : flatElementOnBranch.find((item) => item.next_id === currentElement.next_id) ? true : false;
|
|
324
|
+
}
|
|
325
|
+
branch.onTheSide = 'above';
|
|
326
|
+
const maxWidthBranch = { width: 0, aboveWidth: 0, underWidth: 0 }; // tính chiều ngang để cách nhánh trên or dưới
|
|
327
|
+
this.calculatorWidthBranch(branch.elements, maxWidthBranch);
|
|
328
|
+
branch.widthBranch = {
|
|
329
|
+
width: maxWidthBranch.width,
|
|
330
|
+
above: maxWidthBranch.aboveWidth,
|
|
331
|
+
under: maxWidthBranch.underWidth,
|
|
332
|
+
};
|
|
333
|
+
const branchPre = aboveHalfReverse[indexBranch - 1];
|
|
334
|
+
let lineBetweenBranch = maxWidthBranch.underWidth + canvasConfigReadonly().ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT / 2;
|
|
335
|
+
if (branchPre) {
|
|
336
|
+
lineBetweenBranch += (branchPre.widthBranch?.width ?? 0) + canvasConfigReadonly().ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT;
|
|
337
|
+
}
|
|
338
|
+
if (brachMiddle) {
|
|
339
|
+
lineBetweenBranch += (brachMiddle.widthBranch?.above ?? 0) + canvasConfigReadonly().ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT / 2;
|
|
340
|
+
}
|
|
341
|
+
// nhánh trái
|
|
342
|
+
MoLibDiagramCalculatorBranchUtil.leftLine(left, lineFormElementToBranching, top, lineBetweenBranch, branch, maxHeightBranch.height, currentElement, config);
|
|
343
|
+
});
|
|
344
|
+
underHalf.forEach((branch, indexBranch) => {
|
|
345
|
+
if (!hasNextSameParentElementNext) {
|
|
346
|
+
const flatElementOnBranch = [];
|
|
347
|
+
buildFlatElement(branch.elements, flatElementOnBranch);
|
|
348
|
+
hasNextSameParentElementNext = !flatElementOnBranch || !flatElementOnBranch.length ? true : flatElementOnBranch.find((item) => item.next_id === currentElement.next_id) ? true : false;
|
|
349
|
+
}
|
|
350
|
+
branch.onTheSide = 'under';
|
|
351
|
+
const maxWidthBranch = { width: 0, aboveWidth: 0, underWidth: 0 }; // tính chiều cao để cách nhánh trên or dưới
|
|
352
|
+
this.calculatorWidthBranch(branch.elements, maxWidthBranch);
|
|
353
|
+
branch.widthBranch = {
|
|
354
|
+
width: maxWidthBranch.width,
|
|
355
|
+
above: maxWidthBranch.aboveWidth,
|
|
356
|
+
under: maxWidthBranch.underWidth,
|
|
357
|
+
};
|
|
358
|
+
const branchPre = underHalf[indexBranch - 1];
|
|
359
|
+
let lineBetweenBranch = maxWidthBranch.aboveWidth + canvasConfigReadonly().ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT / 2;
|
|
360
|
+
if (branchPre) {
|
|
361
|
+
lineBetweenBranch += (branchPre.widthBranch?.width ?? 0) + canvasConfigReadonly().ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT;
|
|
362
|
+
}
|
|
363
|
+
if (brachMiddle) {
|
|
364
|
+
lineBetweenBranch += (brachMiddle.widthBranch?.under ?? 0) + canvasConfigReadonly().ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT / 2;
|
|
365
|
+
}
|
|
366
|
+
// nhánh phải
|
|
367
|
+
MoLibDiagramCalculatorBranchUtil.rightLine(left, lineFormElementToBranching, top, lineBetweenBranch, branch, maxHeightBranch.height, currentElement, config);
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
// Set translateX và translateY để có thể render nodes
|
|
371
|
+
this.setTranslatePositions(elements);
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Set translateX và translateY từ specific_x và specific_y cho tất cả elements (bao gồm branches)
|
|
375
|
+
*/
|
|
376
|
+
static setTranslatePositions(elements) {
|
|
377
|
+
elements.forEach((element) => {
|
|
378
|
+
// Set position cho element chính
|
|
379
|
+
element.translateX = element.specific_x;
|
|
380
|
+
element.translateY = element.specific_y;
|
|
381
|
+
// Set position cho nodeOtherConfig nếu có
|
|
382
|
+
if (element.nodeOtherConfig) {
|
|
383
|
+
const offsetX = (element.nodeOtherConfig.specific_width ?? 0) / 2;
|
|
384
|
+
element.nodeOtherConfig.translateX = (element.nodeOtherConfig.specific_x ?? 0) - offsetX;
|
|
385
|
+
element.nodeOtherConfig.translateY = element.nodeOtherConfig.specific_y;
|
|
386
|
+
}
|
|
387
|
+
// Recursively set positions cho elements trong branches
|
|
388
|
+
if (element.branches && element.branches.length > 0) {
|
|
389
|
+
element.branches.forEach((branch) => {
|
|
390
|
+
// Set position cho branch nodeOtherConfig nếu có
|
|
391
|
+
if (branch.nodeOtherConfig) {
|
|
392
|
+
const offsetX = (branch.nodeOtherConfig.specific_width ?? 0) / 2;
|
|
393
|
+
branch.nodeOtherConfig.translateX = (branch.nodeOtherConfig.specific_x ?? 0) - offsetX;
|
|
394
|
+
branch.nodeOtherConfig.translateY = branch.nodeOtherConfig.specific_y;
|
|
395
|
+
}
|
|
396
|
+
// Recursively set positions cho elements trong branch
|
|
397
|
+
if (branch.elements && branch.elements.length > 0) {
|
|
398
|
+
this.setTranslatePositions(branch.elements);
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
static calculatorMaxBranchHeight(element, maxHeightBranch) {
|
|
405
|
+
if (!element.branches || !element.branches.length) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
element.branches.forEach((branch) => {
|
|
409
|
+
if (!branch.elements || !branch.elements.length) {
|
|
410
|
+
const lineBranchNoHasElement = canvasConfigReadonly().ELEMENT_WIDTH_DEFAULT_BRANCH_WHEN_NOT_ELEMENT;
|
|
411
|
+
maxHeightBranch.height = lineBranchNoHasElement > maxHeightBranch.height ? lineBranchNoHasElement : maxHeightBranch.height;
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
const total = {
|
|
415
|
+
height: 0,
|
|
416
|
+
};
|
|
417
|
+
this.plusHeightElementInBranch(branch.elements, total);
|
|
418
|
+
maxHeightBranch.height = total.height > maxHeightBranch.height ? total.height : maxHeightBranch.height;
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
static plusHeightElementInBranch(elements, total) {
|
|
422
|
+
elements.forEach((element) => {
|
|
423
|
+
const margin = !element.branches || !element.branches.length ? checkElementGetHeight(element.nodeOtherConfig) : canvasConfigReadonly().ELEMENT_MARGIN_DEFAULT_BRANCH;
|
|
424
|
+
total.height += (element.specific_height ?? 0) + margin;
|
|
425
|
+
if (!element.branches || !element.branches.length) {
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
total.height += 64; // từ chiều đường thẳng ngang xuống đến nhánh đầu tiên
|
|
429
|
+
const theMostElementsInBranch = {
|
|
430
|
+
height: 0,
|
|
431
|
+
};
|
|
432
|
+
element.branches.forEach((branch) => {
|
|
433
|
+
const heightInBranch = { height: 0 };
|
|
434
|
+
heightInBranch.height += canvasConfigReadonly().ELEMENT_MARGIN_DEFAULT_BRANCH_TO_ELEMENT_FIRST + 20; // đường cong kết thúc nhánh + 10px đường cong
|
|
435
|
+
if (!branch.elements || !branch.elements.length) {
|
|
436
|
+
heightInBranch.height += canvasConfigReadonly().ELEMENT_WIDTH_DEFAULT_BRANCH_WHEN_NOT_ELEMENT;
|
|
437
|
+
if (theMostElementsInBranch.height < heightInBranch.height) {
|
|
438
|
+
theMostElementsInBranch.height = heightInBranch.height;
|
|
439
|
+
}
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
this.plusHeightElementInBranch(branch.elements, heightInBranch);
|
|
443
|
+
if (theMostElementsInBranch.height < heightInBranch.height) {
|
|
444
|
+
theMostElementsInBranch.height = heightInBranch.height;
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
total.height += theMostElementsInBranch.height;
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
static calculatorWidthBranch(elements, maxWidth, log) {
|
|
451
|
+
if (!elements || !elements.length) {
|
|
452
|
+
const width = canvasConfigReadonly().DEFAULT_BRANCH_WHEN_NO_ELEMENT;
|
|
453
|
+
maxWidth.width = width;
|
|
454
|
+
maxWidth.aboveWidth = width / 2;
|
|
455
|
+
maxWidth.underWidth = width / 2;
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
const maxWidthInElements = { width: 0, aboveWidth: 0, underWidth: 0 };
|
|
459
|
+
elements.forEach((element) => {
|
|
460
|
+
if (!element.branches || !element.branches.length) {
|
|
461
|
+
// tính chiều cao của từng element 1, vì chiều cao của từng thằng có thể khác nhau
|
|
462
|
+
if ((element.specific_width ?? 0) < maxWidthInElements.width) {
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
const width = element.specific_width ?? 0;
|
|
466
|
+
maxWidthInElements.width = width;
|
|
467
|
+
maxWidthInElements.aboveWidth = width / 2;
|
|
468
|
+
maxWidthInElements.underWidth = width / 2;
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
const widthElementIncludeBranch = { width: 0, aboveWidth: 0, underWidth: 0 };
|
|
472
|
+
const width = element.specific_width ?? 0;
|
|
473
|
+
if (width > widthElementIncludeBranch.width) {
|
|
474
|
+
widthElementIncludeBranch.width = width;
|
|
475
|
+
widthElementIncludeBranch.aboveWidth = width / 2;
|
|
476
|
+
widthElementIncludeBranch.underWidth = width / 2;
|
|
477
|
+
}
|
|
478
|
+
const half = Math.ceil(element.branches.length / 2);
|
|
479
|
+
const aboveHalf = element.branches.slice(0, half);
|
|
480
|
+
const underHalf = element.branches.slice(half);
|
|
481
|
+
let brachMiddle;
|
|
482
|
+
if (aboveHalf.length !== underHalf.length) {
|
|
483
|
+
brachMiddle = aboveHalf.pop();
|
|
484
|
+
}
|
|
485
|
+
if (brachMiddle) {
|
|
486
|
+
brachMiddle.onTheSide = 'center';
|
|
487
|
+
}
|
|
488
|
+
aboveHalf.forEach((branch) => (branch.onTheSide = 'above'));
|
|
489
|
+
underHalf.forEach((branch) => (branch.onTheSide = 'under'));
|
|
490
|
+
// tính chiều cao của từng nhánh 1, rồi tính tổng => so sánh với chiều cao xem element có nhánh nào cao nhất thì lấy
|
|
491
|
+
const widthBranch = { width: 0, aboveWidth: 0, underWidth: 0 };
|
|
492
|
+
element.branches.forEach((branch) => {
|
|
493
|
+
const width = { width: 0, aboveWidth: 0, underWidth: 0 }; // tính chiều cao của từng nhánh 1
|
|
494
|
+
this.calculatorWidthBranch(branch.elements, width, log);
|
|
495
|
+
switch (branch.onTheSide) {
|
|
496
|
+
case 'above':
|
|
497
|
+
widthBranch.aboveWidth += width.width;
|
|
498
|
+
break;
|
|
499
|
+
case 'under':
|
|
500
|
+
widthBranch.underWidth += width.width;
|
|
501
|
+
break;
|
|
502
|
+
case 'center':
|
|
503
|
+
widthBranch.aboveWidth += width.aboveWidth;
|
|
504
|
+
widthBranch.underWidth += width.underWidth;
|
|
505
|
+
break;
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
const marginBetweenBranch = (element.branches.length - 1) * canvasConfigReadonly().ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT;
|
|
509
|
+
widthBranch.aboveWidth += marginBetweenBranch / 2;
|
|
510
|
+
widthBranch.underWidth += marginBetweenBranch / 2;
|
|
511
|
+
const maxAboveWidth = Math.max(widthElementIncludeBranch.aboveWidth, widthBranch.aboveWidth);
|
|
512
|
+
const maxUnderWidth = Math.max(widthElementIncludeBranch.underWidth, widthBranch.underWidth);
|
|
513
|
+
if (maxWidthInElements.aboveWidth < maxAboveWidth) {
|
|
514
|
+
maxWidthInElements.aboveWidth = maxAboveWidth;
|
|
515
|
+
}
|
|
516
|
+
if (maxWidthInElements.underWidth < maxUnderWidth) {
|
|
517
|
+
maxWidthInElements.underWidth = maxUnderWidth;
|
|
518
|
+
}
|
|
519
|
+
maxWidthInElements.width = maxWidthInElements.aboveWidth + maxWidthInElements.underWidth;
|
|
520
|
+
});
|
|
521
|
+
maxWidth.width += maxWidthInElements.aboveWidth + maxWidthInElements.underWidth;
|
|
522
|
+
maxWidth.aboveWidth += maxWidthInElements.aboveWidth;
|
|
523
|
+
maxWidth.underWidth += maxWidthInElements.underWidth;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
const checkElementGetHeight = (hasWaitConfig) => {
|
|
527
|
+
if (hasWaitConfig) {
|
|
528
|
+
return canvasConfigReadonly().DISTANCE_TO_WAIT + getHeightWaitConfig(hasWaitConfig) + canvasConfigReadonly().WAIT_TO_ELEMENT;
|
|
529
|
+
}
|
|
530
|
+
return canvasConfigReadonly().ELEMENT_MARGIN_DEFAULT;
|
|
531
|
+
};
|
|
532
|
+
const getDistanceFromBranchToFirstBlock = (hasWaitConfig) => {
|
|
533
|
+
if (hasWaitConfig) {
|
|
534
|
+
return canvasConfigReadonly().DISTANCE_TO_WAIT + getHeightWaitConfig(hasWaitConfig) + canvasConfigReadonly().WAIT_TO_ELEMENT;
|
|
535
|
+
}
|
|
536
|
+
return canvasConfigReadonly().ELEMENT_MARGIN_DEFAULT_BRANCH_TO_ELEMENT_FIRST;
|
|
537
|
+
};
|
|
538
|
+
const getHeightWaitConfig = (hasWaitConfig) => {
|
|
539
|
+
if (hasWaitConfig) {
|
|
540
|
+
return (hasWaitConfig.specific_height ?? canvasConfigReadonly().ELEMENT_WAIT_DEFAULT) + canvasConfigReadonly().DISTANCE_WAIT_TO_NEXT_LINE;
|
|
541
|
+
}
|
|
542
|
+
return canvasConfigReadonly().ELEMENT_WAIT_DEFAULT + canvasConfigReadonly().DISTANCE_WAIT_TO_NEXT_LINE;
|
|
543
|
+
};
|
|
544
|
+
const checkMaxLeft = (config, specific_x, positionMaxLeft) => {
|
|
545
|
+
let numberTranslatePosition = config.positionMaxLeft;
|
|
546
|
+
if (numberTranslatePosition === 0 && specific_x && specific_x > 0 && specific_x < 50) {
|
|
547
|
+
numberTranslatePosition = -50;
|
|
548
|
+
}
|
|
549
|
+
if (specific_x && specific_x < numberTranslatePosition) {
|
|
550
|
+
numberTranslatePosition = specific_x;
|
|
551
|
+
}
|
|
552
|
+
if (positionMaxLeft && positionMaxLeft < numberTranslatePosition) {
|
|
553
|
+
numberTranslatePosition = positionMaxLeft;
|
|
554
|
+
}
|
|
555
|
+
if (numberTranslatePosition === config.positionMaxLeft) {
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
config.positionMaxLeft = numberTranslatePosition;
|
|
559
|
+
};
|
|
560
|
+
const checkMaxTop = (config, specific_y, positionMaxTop) => {
|
|
561
|
+
let numberTranslatePosition = config.positionMaxTop;
|
|
562
|
+
if (numberTranslatePosition === 0 && specific_y && specific_y > 0 && specific_y < 100) {
|
|
563
|
+
numberTranslatePosition = specific_y - 120;
|
|
564
|
+
}
|
|
565
|
+
if (specific_y && specific_y < numberTranslatePosition) {
|
|
566
|
+
numberTranslatePosition = specific_y;
|
|
567
|
+
}
|
|
568
|
+
if (positionMaxTop && positionMaxTop < numberTranslatePosition) {
|
|
569
|
+
numberTranslatePosition = positionMaxTop;
|
|
570
|
+
}
|
|
571
|
+
if (numberTranslatePosition === config.positionMaxTop) {
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
config.positionMaxTop = numberTranslatePosition;
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Horizontal branch calculator.
|
|
579
|
+
* Flow direction: LEFT → RIGHT (trái sang phải).
|
|
580
|
+
* Branches spread: above = UP (y giảm), under = DOWN (y tăng).
|
|
581
|
+
*
|
|
582
|
+
* Mapping từ trục dọc sang ngang:
|
|
583
|
+
* vertical "left" branch (x giảm) ↔ horizontal "above" branch (y giảm)
|
|
584
|
+
* vertical "right" branch (x tăng) ↔ horizontal "under" branch (y tăng)
|
|
585
|
+
*
|
|
586
|
+
* Ký hiệu trong ICoordinatesDiagramSvg:
|
|
587
|
+
* (mx,my) = điểm bắt đầu Q curve
|
|
588
|
+
* (x1,y1) = control point
|
|
589
|
+
* (x,y) = end point của Q curve
|
|
590
|
+
* (lx,ly) = điểm cuối của L sau Q
|
|
591
|
+
*/
|
|
592
|
+
class MoLibDiagramHorizontalCalculatorBranchUtil {
|
|
593
|
+
static getHorizontalDistanceToFirstBlock(branch) {
|
|
594
|
+
if (branch.nodeOtherConfig) {
|
|
595
|
+
const distToWait = 110; // increase statically since branch label is always assigned later
|
|
596
|
+
return distToWait + getHeightWaitConfig(branch.nodeOtherConfig) + canvasConfigReadonly().WAIT_TO_ELEMENT;
|
|
597
|
+
}
|
|
598
|
+
return canvasConfigReadonly().ELEMENT_MARGIN_DEFAULT_BRANCH_TO_ELEMENT_FIRST;
|
|
599
|
+
}
|
|
600
|
+
// ─────────────────────────────────────────────────────────────────── public ──
|
|
601
|
+
/** Nhánh giữa (center): kéo thẳng sang phải */
|
|
602
|
+
static drawStraightLine(top, // Y tâm của element cha
|
|
603
|
+
lineFormElementToBranching, // width element + margin đến điểm rẽ
|
|
604
|
+
left, // X trái của element cha
|
|
605
|
+
branch, maxWidthBranch, config) {
|
|
606
|
+
branch.onTheSide = 'center';
|
|
607
|
+
const startDraw = left + lineFormElementToBranching;
|
|
608
|
+
let Lx = startDraw;
|
|
609
|
+
Lx += canvasConfigReadonly().ELEMENT_HEIGHT_CURVE;
|
|
610
|
+
Lx += this.getHorizontalDistanceToFirstBlock(branch);
|
|
611
|
+
if (!branch.elements || !branch.elements.length) {
|
|
612
|
+
Lx += maxWidthBranch + 20;
|
|
613
|
+
}
|
|
614
|
+
const coord1 = { mx: left, my: top, lx: Lx, ly: top };
|
|
615
|
+
branch.attributeSvgD = `M ${coord1.mx} ${coord1.my} L ${coord1.lx} ${coord1.ly}`;
|
|
616
|
+
if (branch.nodeOtherConfig) {
|
|
617
|
+
const distToWait = 110;
|
|
618
|
+
const endXToWait = startDraw + canvasConfigReadonly().ELEMENT_HEIGHT_CURVE + distToWait;
|
|
619
|
+
branch.attributeSvgD = `M ${coord1.mx} ${coord1.my} L ${endXToWait} ${coord1.my}`;
|
|
620
|
+
const waitWidth = getHeightWaitConfig(branch.nodeOtherConfig);
|
|
621
|
+
const leftLine2 = endXToWait + waitWidth;
|
|
622
|
+
delete branch.nodeOtherConfig.attributeSvgD;
|
|
623
|
+
if (branch.elements && branch.elements.length) {
|
|
624
|
+
branch.nodeOtherConfig.attributeSvgD = `M ${leftLine2} ${coord1.my} L ${coord1.lx} ${coord1.my}`;
|
|
625
|
+
}
|
|
626
|
+
branch.nodeOtherConfig.specific_x = endXToWait;
|
|
627
|
+
branch.nodeOtherConfig.specific_y = coord1.my;
|
|
628
|
+
// Đảm bảo có kích thước để setTranslatePositions tính offset đúng
|
|
629
|
+
if (!branch.nodeOtherConfig.specific_height) {
|
|
630
|
+
branch.nodeOtherConfig.specific_height = canvasConfigReadonly().ELEMENT_WAIT_DEFAULT;
|
|
631
|
+
}
|
|
632
|
+
if (!branch.nodeOtherConfig.specific_width) {
|
|
633
|
+
branch.nodeOtherConfig.specific_width = canvasConfigReadonly().ELEMENT_WAIT_DEFAULT;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
branch.specific_start_branch_x = left + lineFormElementToBranching + canvasConfigReadonly().ELEMENT_HEIGHT_CURVE;
|
|
637
|
+
branch.specific_start_branch_y = top;
|
|
638
|
+
if (!branch.elements || !branch.elements.length)
|
|
639
|
+
return;
|
|
640
|
+
const widthToElement = canvasConfigReadonly().ELEMENT_HEIGHT_CURVE + this.getHorizontalDistanceToFirstBlock(branch);
|
|
641
|
+
branch.elements.forEach((el) => {
|
|
642
|
+
el.specific_x = left + lineFormElementToBranching + widthToElement;
|
|
643
|
+
el.specific_y = top - (el.specific_height ?? 0) / 2;
|
|
644
|
+
});
|
|
645
|
+
MoLibDiagramHorizontalCalculatorCoordinatesUtil.setXYElements(branch.elements, config);
|
|
646
|
+
}
|
|
647
|
+
/** Nhánh phía trên (above): rẽ lên, rồi bẻ phải đến element */
|
|
648
|
+
static aboveLine(top, lineFormElementToBranching, left, lineBetweenBranch, branch, maxWidthBranch, element, config) {
|
|
649
|
+
branch.onTheSide = 'above';
|
|
650
|
+
const { coordQL2, isCheckAllElementConnectToExit } = this.drawAboveUnderLine(top, lineFormElementToBranching, left, lineBetweenBranch, branch, maxWidthBranch, 'above', config);
|
|
651
|
+
branch.specific_start_branch_x = coordQL2.x;
|
|
652
|
+
branch.specific_start_branch_y = coordQL2.y;
|
|
653
|
+
if (!branch.elements || !branch.elements.length)
|
|
654
|
+
return;
|
|
655
|
+
this.drawElementInBranchLine(branch, coordQL2, config);
|
|
656
|
+
if (isCheckAllElementConnectToExit)
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
/** Nhánh phía dưới (under): rẽ xuống, rồi bẻ phải đến element */
|
|
660
|
+
static underLine(top, lineFormElementToBranching, left, lineBetweenBranch, branch, maxWidthBranch, element, config) {
|
|
661
|
+
branch.onTheSide = 'under';
|
|
662
|
+
const { coordQL1, coordQL2, isCheckAllElementConnectToExit } = this.drawAboveUnderLine(top, lineFormElementToBranching, left, lineBetweenBranch, branch, maxWidthBranch, 'under', config);
|
|
663
|
+
branch.specific_start_branch_x = coordQL2.x;
|
|
664
|
+
branch.specific_start_branch_y = coordQL2.y;
|
|
665
|
+
// Mở rộng widthSvg nếu nhánh xuống vượt
|
|
666
|
+
const maxY = (coordQL1.y ?? 0) + 200;
|
|
667
|
+
const configSvg = config.widthHeightSvgCanvas;
|
|
668
|
+
configSvg.heightSvg = maxY > configSvg.heightSvg ? maxY : configSvg.heightSvg;
|
|
669
|
+
if (!branch.elements || !branch.elements.length)
|
|
670
|
+
return;
|
|
671
|
+
this.drawElementInBranchLine(branch, coordQL2, config);
|
|
672
|
+
if (isCheckAllElementConnectToExit)
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
// ────────────────────────────────────────────────────────────────── private ──
|
|
676
|
+
/** Đặt element đầu tiên trong nhánh dựa theo tọa độ cuối của QL2 */
|
|
677
|
+
static drawElementInBranchLine(branch, coordQL2, config) {
|
|
678
|
+
const elementFirst = branch.elements[0];
|
|
679
|
+
// lx = X bắt đầu của element; ly = Y tâm nhánh → specific_y = tâm - half height
|
|
680
|
+
elementFirst.specific_x = coordQL2.lx;
|
|
681
|
+
elementFirst.specific_y = (coordQL2.ly ?? 0) - (elementFirst.specific_height ?? 0) / 2;
|
|
682
|
+
MoLibDiagramHorizontalCalculatorCoordinatesUtil.setXYElements(branch.elements, config);
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Vẽ đường rẽ nhánh kiểu horizontal:
|
|
686
|
+
*
|
|
687
|
+
* M left top → bắt đầu tại tâm Y của element cha
|
|
688
|
+
* L Lx top → kéo NGANG sang phải đến điểm rẽ (Lx = left + lineFormElementToBranching - CURVE)
|
|
689
|
+
*
|
|
690
|
+
* QL1: bẻ từ hướng → sang hướng ↑ (above) hoặc ↓ (under)
|
|
691
|
+
* M (Lx, top)
|
|
692
|
+
* Q (Lx+C, top) → (Lx+C, top±C) control đi thẳng phải, điểm kết bắt đầu đi dọc
|
|
693
|
+
* L Lx+C, top±C±straight kéo thẳng dọc (lên/xuống)
|
|
694
|
+
*
|
|
695
|
+
* QL2: bẻ ngược lại → hướng ngang để tới element
|
|
696
|
+
* Q (Lx+C, top±C±straight±C) → (Lx+2C, top±C±straight±C)
|
|
697
|
+
* control đi tiếp dọc, điểm kết đi ngang (phải)
|
|
698
|
+
* L Lx+2C+distToFirst, same_Y kéo ngang đến vị trí element đầu tiên
|
|
699
|
+
*
|
|
700
|
+
* Sau QL2:
|
|
701
|
+
* coordQL2.lx = X bắt đầu của element đầu tiên
|
|
702
|
+
* coordQL2.ly = Y tâm của nhánh
|
|
703
|
+
*/
|
|
704
|
+
static drawAboveUnderLine(top, lineFormElementToBranching, left, lineBetweenBranch, branch, maxWidthBranch, direction, config) {
|
|
705
|
+
const CURVE = canvasConfigReadonly().ELEMENT_HEIGHT_CURVE;
|
|
706
|
+
const M = `M ${left} ${top}`;
|
|
707
|
+
const Lx = left + lineFormElementToBranching - CURVE;
|
|
708
|
+
const L = `L ${Lx} ${top}`;
|
|
709
|
+
const straight = lineBetweenBranch - CURVE * 2; // đoạn thẳng dọc (bỏ 2 cong đầu-cuối)
|
|
710
|
+
// ── QL1: curve từ hướng ngang → hướng dọc (lên/xuống), rồi thẳng dọc ────
|
|
711
|
+
const coordQL1 = {
|
|
712
|
+
mx: Lx,
|
|
713
|
+
my: top,
|
|
714
|
+
x1: Lx + CURVE, // control: đi thẳng phải thêm CURVE
|
|
715
|
+
y1: top, // control: cùng Y
|
|
716
|
+
x: Lx + CURVE, // end X: giữ nguyên (= Lx+CURVE)
|
|
717
|
+
y: this.mathOp(direction, top, CURVE, true),
|
|
718
|
+
// 'above': top - CURVE (bắt đầu đi lên)
|
|
719
|
+
// 'under': top + CURVE (bắt đầu đi xuống)
|
|
720
|
+
};
|
|
721
|
+
// Đoạn L sau QL1: giữ nguyên X, tiếp tục đi dọc
|
|
722
|
+
coordQL1.lx = coordQL1.x; // X không đổi
|
|
723
|
+
coordQL1.ly = this.mathOp(direction, coordQL1.y, straight, true); // tiếp tục lên/xuống
|
|
724
|
+
const QL1 = `M ${coordQL1.mx},${coordQL1.my} Q ${coordQL1.x1},${coordQL1.y1} ${coordQL1.x},${coordQL1.y} L ${coordQL1.lx} ${coordQL1.ly}`;
|
|
725
|
+
// ── QL2: curve từ hướng dọc → hướng ngang (phải), rồi thẳng ngang đến element ──
|
|
726
|
+
const coordQL2 = {
|
|
727
|
+
// control: tiếp tục đi dọc thêm CURVE từ điểm cuối QL1
|
|
728
|
+
x1: coordQL1.lx,
|
|
729
|
+
y1: this.mathOp(direction, coordQL1.ly, CURVE, true),
|
|
730
|
+
// end: rẽ ngang sang phải thêm CURVE, giữ nguyên Y tại y1
|
|
731
|
+
y: this.mathOp(direction, coordQL1.ly, CURVE, true),
|
|
732
|
+
x: (coordQL1.lx ?? 0) + CURVE,
|
|
733
|
+
};
|
|
734
|
+
// Đoạn L sau QL2: đi ngang (phải) đến vị trí element đầu tiên trong nhánh
|
|
735
|
+
const distToFirst = this.getHorizontalDistanceToFirstBlock(branch);
|
|
736
|
+
if (!branch.elements || !branch.elements.length) {
|
|
737
|
+
coordQL2.lx = (coordQL2.x ?? 0) + distToFirst + maxWidthBranch;
|
|
738
|
+
}
|
|
739
|
+
else {
|
|
740
|
+
coordQL2.lx = (coordQL2.x ?? 0) + distToFirst;
|
|
741
|
+
}
|
|
742
|
+
coordQL2.ly = coordQL2.y; // Y không đổi → tâm Y của nhánh
|
|
743
|
+
// Xử lý nodeOtherConfig (wait block) trên nhánh
|
|
744
|
+
let QL2 = `Q ${coordQL2.x1},${coordQL2.y1} ${coordQL2.x},${coordQL2.y} L ${coordQL2.lx} ${coordQL2.ly}`;
|
|
745
|
+
if (branch.nodeOtherConfig) {
|
|
746
|
+
const distToWait = 110;
|
|
747
|
+
const endXToWait = (coordQL2.x ?? 0) + distToWait;
|
|
748
|
+
const waitWidth = getHeightWaitConfig(branch.nodeOtherConfig);
|
|
749
|
+
const leftLine2 = endXToWait + waitWidth;
|
|
750
|
+
delete branch.nodeOtherConfig.attributeSvgD;
|
|
751
|
+
QL2 = `Q ${coordQL2.x1},${coordQL2.y1} ${coordQL2.x},${coordQL2.y} M ${coordQL2.x},${coordQL2.y} L ${endXToWait},${coordQL2.y}`;
|
|
752
|
+
if (branch.elements && branch.elements.length) {
|
|
753
|
+
branch.nodeOtherConfig.attributeSvgD = `M ${leftLine2} ${coordQL2.y} L ${coordQL2.lx} ${coordQL2.ly}`;
|
|
754
|
+
}
|
|
755
|
+
branch.nodeOtherConfig.specific_x = endXToWait;
|
|
756
|
+
// specific_y = tâm Y của nhánh — setTranslatePositions sẽ trừ height/2 khi set translateY
|
|
757
|
+
branch.nodeOtherConfig.specific_y = coordQL2.y ?? 0;
|
|
758
|
+
// Đảm bảo có kích thước để setTranslatePositions tính offset đúng
|
|
759
|
+
if (!branch.nodeOtherConfig.specific_height) {
|
|
760
|
+
branch.nodeOtherConfig.specific_height = canvasConfigReadonly().ELEMENT_WAIT_DEFAULT;
|
|
761
|
+
}
|
|
762
|
+
if (!branch.nodeOtherConfig.specific_width) {
|
|
763
|
+
branch.nodeOtherConfig.specific_width = canvasConfigReadonly().ELEMENT_WAIT_DEFAULT;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
branch.attributeSvgD = `${M} ${L} ${QL1} ${QL2}`;
|
|
767
|
+
const isCheckAllElementConnectToExit = this.checkChildrenConnectToExit(branch.elements);
|
|
768
|
+
branch.positionMaxTop = coordQL2.y;
|
|
769
|
+
checkMaxTop(config, branch.nodeOtherConfig?.specific_y, branch.positionMaxTop);
|
|
770
|
+
return { coordQL1, coordQL2, isCheckAllElementConnectToExit };
|
|
771
|
+
}
|
|
772
|
+
static checkChildrenConnectToExit(elements) {
|
|
773
|
+
let toExit = false;
|
|
774
|
+
if (elements.length &&
|
|
775
|
+
(elements[elements.length - 1].subCodeOfElement || elements[elements.length - 1].code) === canvasConfigReadonly().TYPE_ELEMENT_EXIT) {
|
|
776
|
+
return true;
|
|
777
|
+
}
|
|
778
|
+
elements.forEach((element) => {
|
|
779
|
+
if (!element.branches || !element.branches.length)
|
|
780
|
+
return;
|
|
781
|
+
let branchConnectToExit = 0;
|
|
782
|
+
element.branches.forEach((branch) => {
|
|
783
|
+
if (this.checkChildrenConnectToExit(branch.elements))
|
|
784
|
+
branchConnectToExit++;
|
|
785
|
+
});
|
|
786
|
+
if (branchConnectToExit === element.branches.length)
|
|
787
|
+
toExit = true;
|
|
788
|
+
});
|
|
789
|
+
return toExit;
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Quy ước:
|
|
793
|
+
* above + opposite=true → a - b (đi lên, y giảm)
|
|
794
|
+
* above + opposite=false → a + b
|
|
795
|
+
* under + opposite=true → a + b (đi xuống, y tăng)
|
|
796
|
+
* under + opposite=false → a - b
|
|
797
|
+
*/
|
|
798
|
+
static mathOp(direction, a, b, opposite) {
|
|
799
|
+
const av = a ?? 0;
|
|
800
|
+
const bv = b ?? 0;
|
|
801
|
+
switch (direction) {
|
|
802
|
+
case 'above':
|
|
803
|
+
return opposite ? av - bv : av + bv;
|
|
804
|
+
case 'under':
|
|
805
|
+
return opposite ? av + bv : av - bv;
|
|
806
|
+
default:
|
|
807
|
+
return av - bv;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
/** Alias để tương thích với code cũ gọi mathOperatorsCalculation */
|
|
811
|
+
static mathOperatorsCalculation(direction, a, b, opposite) {
|
|
812
|
+
return this.mathOp(direction, a, b, opposite);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Horizontal layout calculator.
|
|
818
|
+
* Mirrors MoLibDiagramCalculatorCoordinatesUtil (vertical) but rotated 90°:
|
|
819
|
+
* - Elements grow LEFT → RIGHT (specific_x advances along main axis)
|
|
820
|
+
* - Branches split UP / DOWN (specific_y for branch offset)
|
|
821
|
+
* - "above" branch = upper side (y decreases)
|
|
822
|
+
* - "under" branch = lower side (y increases)
|
|
823
|
+
*
|
|
824
|
+
* Key axis mapping vs. vertical:
|
|
825
|
+
* vertical: specific_y ↕ specific_x centered
|
|
826
|
+
* horizontal: specific_x → specific_y centered
|
|
827
|
+
*/
|
|
828
|
+
class MoLibDiagramHorizontalCalculatorCoordinatesUtil {
|
|
829
|
+
static setXYElements(elements, config, positionY) {
|
|
830
|
+
if (!elements || !elements.length) {
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
elements.forEach((currentElement, index) => {
|
|
834
|
+
currentElement.specific_y = (currentElement.specific_y ?? 0) + (positionY ?? 0);
|
|
835
|
+
const preElement = elements[index - 1];
|
|
836
|
+
if (preElement) {
|
|
837
|
+
// Trong horizontal: y (chiều cao) căn giữa theo preElement, x tiến sang phải
|
|
838
|
+
currentElement.specific_y = (preElement.specific_y ?? 0) + (preElement.specific_height ?? 0) / 2 - (currentElement.specific_height ?? 0) / 2;
|
|
839
|
+
currentElement.specific_x = (preElement.specific_x ?? 0) + (preElement.specific_width ?? 0) + this.checkElementGetMargin(preElement.nodeOtherConfig);
|
|
840
|
+
if (preElement.position_end_branch) {
|
|
841
|
+
currentElement.specific_x = preElement.position_end_branch;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
checkMaxTop(config, currentElement.specific_y, currentElement.branches?.[0]?.positionMaxTop);
|
|
845
|
+
const top = currentElement.specific_y ?? 0;
|
|
846
|
+
const left = (currentElement.specific_x ?? 0) + (currentElement.specific_width ?? 0) / 2; // điểm giữa theo X (điểm vẽ SVG)
|
|
847
|
+
let lineBetweenElements = (currentElement.specific_width ?? 0) + this.checkElementGetMargin(currentElement.nodeOtherConfig);
|
|
848
|
+
if (currentElement.branches && currentElement.branches.length) {
|
|
849
|
+
lineBetweenElements = (currentElement.specific_width ?? 0) + canvasConfigReadonly().ELEMENT_MARGIN_DEFAULT_BRANCH;
|
|
850
|
+
}
|
|
851
|
+
const maxY = (currentElement.specific_y ?? 0) + (currentElement.specific_height ?? 0) + 200;
|
|
852
|
+
const maxXRight = (currentElement.specific_x ?? 0) + (currentElement.specific_width ?? 0) + 200;
|
|
853
|
+
const configSvg = config.widthHeightSvgCanvas;
|
|
854
|
+
configSvg.heightSvg = maxY > configSvg.heightSvg ? maxY : configSvg.heightSvg;
|
|
855
|
+
configSvg.widthSvg = maxXRight > configSvg.widthSvg ? maxXRight : configSvg.widthSvg;
|
|
856
|
+
if (!currentElement.branches || !currentElement.branches.length) {
|
|
857
|
+
// Element không có nhánh: vẽ đường thẳng ngang
|
|
858
|
+
const drawFormToWidthExit = (currentElement.subCodeOfElement || currentElement.code) === canvasConfigReadonly().TYPE_ELEMENT_EXIT ? (currentElement.specific_width ?? 0) / 2 : lineBetweenElements;
|
|
859
|
+
currentElement.attributeSvgD = `M ${currentElement.specific_x ?? 0} ${top + (currentElement.specific_height ?? 0) / 2} l ${drawFormToWidthExit} 0`;
|
|
860
|
+
currentElement.position_end = (currentElement.specific_x ?? 0) + lineBetweenElements;
|
|
861
|
+
if (currentElement.nodeOtherConfig) {
|
|
862
|
+
const waitWidth = this.getWidthWaitConfig(currentElement.nodeOtherConfig);
|
|
863
|
+
const distanceElementToWait = (currentElement.specific_width ?? 0) + canvasConfigReadonly().DISTANCE_TO_WAIT;
|
|
864
|
+
const leftStartLine2 = (currentElement.specific_x ?? 0) + distanceElementToWait + waitWidth;
|
|
865
|
+
currentElement.attributeSvgD = `M ${currentElement.specific_x ?? 0} ${top + (currentElement.specific_height ?? 0) / 2} l ${distanceElementToWait} 0`;
|
|
866
|
+
currentElement.position_end = leftStartLine2 + lineBetweenElements;
|
|
867
|
+
currentElement.nodeOtherConfig.specific_x = (currentElement.specific_x ?? 0) + distanceElementToWait;
|
|
868
|
+
// specific_y = tâm Y của đường line — setTranslatePositions sẽ trừ height/2 để căn giữa dọc
|
|
869
|
+
currentElement.nodeOtherConfig.specific_y = top + (currentElement.specific_height ?? 0) / 2;
|
|
870
|
+
// Đảm bảo có kích thước để setTranslatePositions tính offset đúng
|
|
871
|
+
if (!currentElement.nodeOtherConfig.specific_height) {
|
|
872
|
+
currentElement.nodeOtherConfig.specific_height = canvasConfigReadonly().ELEMENT_WAIT_DEFAULT;
|
|
873
|
+
}
|
|
874
|
+
if (!currentElement.nodeOtherConfig.specific_width) {
|
|
875
|
+
currentElement.nodeOtherConfig.specific_width = canvasConfigReadonly().ELEMENT_WAIT_DEFAULT;
|
|
876
|
+
}
|
|
877
|
+
delete currentElement.nodeOtherConfig.attributeSvgD;
|
|
878
|
+
if (currentElement.subCodeOfElement !== canvasConfigReadonly().TYPE_ELEMENT_EXIT) {
|
|
879
|
+
currentElement.nodeOtherConfig.attributeSvgD = `M ${leftStartLine2} ${top + (currentElement.specific_height ?? 0) / 2} l ${canvasConfigReadonly().WAIT_TO_ELEMENT} 0`;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
// Elements có nhánh
|
|
885
|
+
const half = Math.ceil(currentElement.branches.length / 2);
|
|
886
|
+
const aboveHalf = currentElement.branches.slice(0, half);
|
|
887
|
+
const underHalf = currentElement.branches.slice(half);
|
|
888
|
+
let brachMiddle;
|
|
889
|
+
if (aboveHalf.length !== underHalf.length) {
|
|
890
|
+
brachMiddle = aboveHalf.pop();
|
|
891
|
+
}
|
|
892
|
+
// TÍNH CHIỀU CAO TỐI ĐA CỦA NHÁNH (đây là chiều rộng trong horizontal context nhưng
|
|
893
|
+
// dùng chung maxHeightBranch để đơn giản, ý nghĩa là "khoảng trống tối thiểu dành cho nhánh không có element")
|
|
894
|
+
const maxWidthBranch = { width: 0 };
|
|
895
|
+
this.calculatorMaxBranchWidth(currentElement, maxWidthBranch);
|
|
896
|
+
currentElement.maxHeightBranch = maxWidthBranch.width;
|
|
897
|
+
const maxWidthBrachMiddle = { width: 0, aboveWidth: 0, underWidth: 0 };
|
|
898
|
+
const lineFormElementToBranching = (currentElement.specific_width ?? 0) + canvasConfigReadonly().ELEMENT_MARGIN_DEFAULT_BRANCH;
|
|
899
|
+
// top = y + height/2: điểm giữa theo chiều Y → dùng làm tọa độ Y của đường ngang chính
|
|
900
|
+
const topMid = top + (currentElement.specific_height ?? 0) / 2;
|
|
901
|
+
if (brachMiddle) {
|
|
902
|
+
brachMiddle.onTheSide = 'center';
|
|
903
|
+
this.calculatorHeightBranch(brachMiddle.elements, maxWidthBrachMiddle);
|
|
904
|
+
brachMiddle.widthBranch = {
|
|
905
|
+
width: maxWidthBrachMiddle.width,
|
|
906
|
+
above: maxWidthBrachMiddle.aboveWidth,
|
|
907
|
+
under: maxWidthBrachMiddle.underWidth,
|
|
908
|
+
};
|
|
909
|
+
MoLibDiagramHorizontalCalculatorBranchUtil.drawStraightLine(topMid, lineFormElementToBranching, currentElement.specific_x ?? 0, brachMiddle, maxWidthBranch.width, config);
|
|
910
|
+
}
|
|
911
|
+
let hasNextSameParentElementNext = false;
|
|
912
|
+
const aboveHalfReverse = aboveHalf.reverse();
|
|
913
|
+
aboveHalfReverse.forEach((branch, indexBranch) => {
|
|
914
|
+
if (!hasNextSameParentElementNext) {
|
|
915
|
+
const flatElementOnBranch = [];
|
|
916
|
+
buildFlatElement(branch.elements, flatElementOnBranch);
|
|
917
|
+
hasNextSameParentElementNext = !flatElementOnBranch || !flatElementOnBranch.length ? true : flatElementOnBranch.find((item) => item.next_id === currentElement.next_id) ? true : false;
|
|
918
|
+
}
|
|
919
|
+
branch.onTheSide = 'above';
|
|
920
|
+
const maxWidthB = { width: 0, aboveWidth: 0, underWidth: 0 };
|
|
921
|
+
this.calculatorHeightBranch(branch.elements, maxWidthB);
|
|
922
|
+
branch.widthBranch = {
|
|
923
|
+
width: maxWidthB.width,
|
|
924
|
+
above: maxWidthB.aboveWidth,
|
|
925
|
+
under: maxWidthB.underWidth,
|
|
926
|
+
};
|
|
927
|
+
const branchPre = aboveHalfReverse[indexBranch - 1];
|
|
928
|
+
let lineBetweenBranch = maxWidthB.underWidth + canvasConfigReadonly().ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT / 2;
|
|
929
|
+
if (branchPre) {
|
|
930
|
+
lineBetweenBranch += (branchPre.widthBranch?.width ?? 0) + canvasConfigReadonly().ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT;
|
|
931
|
+
}
|
|
932
|
+
if (brachMiddle) {
|
|
933
|
+
lineBetweenBranch += (brachMiddle.widthBranch?.above ?? 0) + canvasConfigReadonly().ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT / 2;
|
|
934
|
+
}
|
|
935
|
+
MoLibDiagramHorizontalCalculatorBranchUtil.aboveLine(topMid, lineFormElementToBranching, currentElement.specific_x ?? 0, lineBetweenBranch, branch, maxWidthBranch.width, currentElement, config);
|
|
936
|
+
});
|
|
937
|
+
underHalf.forEach((branch, indexBranch) => {
|
|
938
|
+
if (!hasNextSameParentElementNext) {
|
|
939
|
+
const flatElementOnBranch = [];
|
|
940
|
+
buildFlatElement(branch.elements, flatElementOnBranch);
|
|
941
|
+
hasNextSameParentElementNext = !flatElementOnBranch || !flatElementOnBranch.length ? true : flatElementOnBranch.find((item) => item.next_id === currentElement.next_id) ? true : false;
|
|
942
|
+
}
|
|
943
|
+
branch.onTheSide = 'under';
|
|
944
|
+
const maxWidthB = { width: 0, aboveWidth: 0, underWidth: 0 };
|
|
945
|
+
this.calculatorHeightBranch(branch.elements, maxWidthB);
|
|
946
|
+
branch.widthBranch = {
|
|
947
|
+
width: maxWidthB.width,
|
|
948
|
+
above: maxWidthB.aboveWidth,
|
|
949
|
+
under: maxWidthB.underWidth,
|
|
950
|
+
};
|
|
951
|
+
const branchPre = underHalf[indexBranch - 1];
|
|
952
|
+
let lineBetweenBranch = maxWidthB.aboveWidth + canvasConfigReadonly().ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT / 2;
|
|
953
|
+
if (branchPre) {
|
|
954
|
+
lineBetweenBranch += (branchPre.widthBranch?.width ?? 0) + canvasConfigReadonly().ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT;
|
|
955
|
+
}
|
|
956
|
+
if (brachMiddle) {
|
|
957
|
+
lineBetweenBranch += (brachMiddle.widthBranch?.under ?? 0) + canvasConfigReadonly().ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT / 2;
|
|
958
|
+
}
|
|
959
|
+
MoLibDiagramHorizontalCalculatorBranchUtil.underLine(topMid, lineFormElementToBranching, currentElement.specific_x ?? 0, lineBetweenBranch, branch, maxWidthBranch.width, currentElement, config);
|
|
960
|
+
});
|
|
961
|
+
});
|
|
962
|
+
this.setTranslatePositions(elements);
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Set translateX/Y cho tất cả elements (bao gồm branches và nodeOtherConfig)
|
|
966
|
+
*/
|
|
967
|
+
static setTranslatePositions(elements) {
|
|
968
|
+
elements.forEach((element) => {
|
|
969
|
+
element.translateX = element.specific_x;
|
|
970
|
+
element.translateY = element.specific_y;
|
|
971
|
+
if (element.nodeOtherConfig) {
|
|
972
|
+
const offsetY = (element.nodeOtherConfig.specific_height ?? 0) / 2;
|
|
973
|
+
element.nodeOtherConfig.translateX = element.nodeOtherConfig.specific_x;
|
|
974
|
+
element.nodeOtherConfig.translateY = (element.nodeOtherConfig.specific_y ?? 0) - offsetY;
|
|
975
|
+
}
|
|
976
|
+
if (element.branches && element.branches.length > 0) {
|
|
977
|
+
element.branches.forEach((branch) => {
|
|
978
|
+
if (branch.nodeOtherConfig) {
|
|
979
|
+
const offsetY = (branch.nodeOtherConfig.specific_height ?? 0) / 2;
|
|
980
|
+
branch.nodeOtherConfig.translateX = branch.nodeOtherConfig.specific_x;
|
|
981
|
+
branch.nodeOtherConfig.translateY = (branch.nodeOtherConfig.specific_y ?? 0) - offsetY;
|
|
982
|
+
}
|
|
983
|
+
if (branch.elements && branch.elements.length > 0) {
|
|
984
|
+
this.setTranslatePositions(branch.elements);
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
/** Tính chiều rộng tối đa của nhánh (trong horizontal = chiều cao thực của nhánh không có element) */
|
|
991
|
+
static calculatorMaxBranchWidth(element, maxWidthBranch) {
|
|
992
|
+
if (!element.branches || !element.branches.length) {
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
element.branches.forEach((branch) => {
|
|
996
|
+
if (!branch.elements || !branch.elements.length) {
|
|
997
|
+
const lineBranchNoHasElement = canvasConfigReadonly().ELEMENT_WIDTH_DEFAULT_BRANCH_WHEN_NOT_ELEMENT;
|
|
998
|
+
maxWidthBranch.width = lineBranchNoHasElement > maxWidthBranch.width ? lineBranchNoHasElement : maxWidthBranch.width;
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
const total = { height: 0 };
|
|
1002
|
+
this.plusWidthElementInBranch(branch.elements, total);
|
|
1003
|
+
maxWidthBranch.width = total.height > maxWidthBranch.width ? total.height : maxWidthBranch.width;
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
/** Tính tổng chiều rộng các elements trong nhánh (dọc theo trục X) */
|
|
1007
|
+
static plusWidthElementInBranch(elements, total) {
|
|
1008
|
+
elements.forEach((element) => {
|
|
1009
|
+
const margin = !element.branches || !element.branches.length ? this.checkElementGetMargin(element.nodeOtherConfig) : canvasConfigReadonly().ELEMENT_MARGIN_DEFAULT_BRANCH;
|
|
1010
|
+
total.height += (element.specific_width ?? 0) + margin;
|
|
1011
|
+
if (!element.branches || !element.branches.length) {
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
total.height += 64;
|
|
1015
|
+
const theMostElementsInBranch = { height: 0 };
|
|
1016
|
+
element.branches.forEach((branch) => {
|
|
1017
|
+
const widthInBranch = { height: 0 };
|
|
1018
|
+
widthInBranch.height += canvasConfigReadonly().ELEMENT_MARGIN_DEFAULT_BRANCH_TO_ELEMENT_FIRST + 20;
|
|
1019
|
+
if (!branch.elements || !branch.elements.length) {
|
|
1020
|
+
widthInBranch.height += canvasConfigReadonly().ELEMENT_WIDTH_DEFAULT_BRANCH_WHEN_NOT_ELEMENT;
|
|
1021
|
+
if (theMostElementsInBranch.height < widthInBranch.height) {
|
|
1022
|
+
theMostElementsInBranch.height = widthInBranch.height;
|
|
1023
|
+
}
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
this.plusWidthElementInBranch(branch.elements, widthInBranch);
|
|
1027
|
+
if (theMostElementsInBranch.height < widthInBranch.height) {
|
|
1028
|
+
theMostElementsInBranch.height = widthInBranch.height;
|
|
1029
|
+
}
|
|
1030
|
+
});
|
|
1031
|
+
total.height += theMostElementsInBranch.height;
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Tính chiều cao (Y span) của một nhánh — dùng để offset các nhánh trên/dưới.
|
|
1036
|
+
* Trong horizontal context: mỗi nhánh trải dài theo Y (lên/xuống).
|
|
1037
|
+
*/
|
|
1038
|
+
static calculatorHeightBranch(elements, maxWidth) {
|
|
1039
|
+
if (!elements || !elements.length) {
|
|
1040
|
+
const w = canvasConfigReadonly().DEFAULT_BRANCH_WHEN_NO_ELEMENT;
|
|
1041
|
+
maxWidth.width = w;
|
|
1042
|
+
maxWidth.aboveWidth = w / 2;
|
|
1043
|
+
maxWidth.underWidth = w / 2;
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
const maxWidthInElements = { width: 0, aboveWidth: 0, underWidth: 0 };
|
|
1047
|
+
elements.forEach((element) => {
|
|
1048
|
+
if (!element.branches || !element.branches.length) {
|
|
1049
|
+
// element không nhánh: dùng specific_height làm Y span
|
|
1050
|
+
if ((element.specific_height ?? 0) < maxWidthInElements.width) {
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
const h = element.specific_height ?? 0;
|
|
1054
|
+
maxWidthInElements.width = h;
|
|
1055
|
+
maxWidthInElements.aboveWidth = h / 2;
|
|
1056
|
+
maxWidthInElements.underWidth = h / 2;
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
const widthElementIncludeBranch = { width: 0, aboveWidth: 0, underWidth: 0 };
|
|
1060
|
+
const h = element.specific_height ?? 0;
|
|
1061
|
+
if (h > widthElementIncludeBranch.width) {
|
|
1062
|
+
widthElementIncludeBranch.width = h;
|
|
1063
|
+
widthElementIncludeBranch.aboveWidth = h / 2;
|
|
1064
|
+
widthElementIncludeBranch.underWidth = h / 2;
|
|
1065
|
+
}
|
|
1066
|
+
const half = Math.ceil(element.branches.length / 2);
|
|
1067
|
+
const aboveHalf = element.branches.slice(0, half);
|
|
1068
|
+
const underHalf = element.branches.slice(half);
|
|
1069
|
+
let brachMiddle;
|
|
1070
|
+
if (aboveHalf.length !== underHalf.length) {
|
|
1071
|
+
brachMiddle = aboveHalf.pop();
|
|
1072
|
+
}
|
|
1073
|
+
if (brachMiddle)
|
|
1074
|
+
brachMiddle.onTheSide = 'center';
|
|
1075
|
+
aboveHalf.forEach((branch) => (branch.onTheSide = 'above'));
|
|
1076
|
+
underHalf.forEach((branch) => (branch.onTheSide = 'under'));
|
|
1077
|
+
const widthBranch = { width: 0, aboveWidth: 0, underWidth: 0 };
|
|
1078
|
+
element.branches.forEach((branch) => {
|
|
1079
|
+
const w = { width: 0, aboveWidth: 0, underWidth: 0 };
|
|
1080
|
+
this.calculatorHeightBranch(branch.elements, w);
|
|
1081
|
+
switch (branch.onTheSide) {
|
|
1082
|
+
case 'above':
|
|
1083
|
+
widthBranch.aboveWidth += w.width;
|
|
1084
|
+
break;
|
|
1085
|
+
case 'under':
|
|
1086
|
+
widthBranch.underWidth += w.width;
|
|
1087
|
+
break;
|
|
1088
|
+
case 'center':
|
|
1089
|
+
widthBranch.aboveWidth += w.aboveWidth;
|
|
1090
|
+
widthBranch.underWidth += w.underWidth;
|
|
1091
|
+
break;
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
const marginBetweenBranch = (element.branches.length - 1) * canvasConfigReadonly().ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT;
|
|
1095
|
+
widthBranch.aboveWidth += marginBetweenBranch / 2;
|
|
1096
|
+
widthBranch.underWidth += marginBetweenBranch / 2;
|
|
1097
|
+
const maxAboveWidth = Math.max(widthElementIncludeBranch.aboveWidth, widthBranch.aboveWidth);
|
|
1098
|
+
const maxUnderWidth = Math.max(widthElementIncludeBranch.underWidth, widthBranch.underWidth);
|
|
1099
|
+
if (maxWidthInElements.aboveWidth < maxAboveWidth) {
|
|
1100
|
+
maxWidthInElements.aboveWidth = maxAboveWidth;
|
|
1101
|
+
}
|
|
1102
|
+
if (maxWidthInElements.underWidth < maxUnderWidth) {
|
|
1103
|
+
maxWidthInElements.underWidth = maxUnderWidth;
|
|
1104
|
+
}
|
|
1105
|
+
maxWidthInElements.width = maxWidthInElements.aboveWidth + maxWidthInElements.underWidth;
|
|
1106
|
+
});
|
|
1107
|
+
maxWidth.width += maxWidthInElements.aboveWidth + maxWidthInElements.underWidth;
|
|
1108
|
+
maxWidth.aboveWidth += maxWidthInElements.aboveWidth;
|
|
1109
|
+
maxWidth.underWidth += maxWidthInElements.underWidth;
|
|
1110
|
+
}
|
|
1111
|
+
/** Tính khoảng cách thêm vào sau element (bao gồm wait config nếu có) */
|
|
1112
|
+
static checkElementGetMargin(hasWaitConfig) {
|
|
1113
|
+
if (hasWaitConfig) {
|
|
1114
|
+
return canvasConfigReadonly().DISTANCE_TO_WAIT + this.getWidthWaitConfig(hasWaitConfig) + canvasConfigReadonly().WAIT_TO_ELEMENT;
|
|
1115
|
+
}
|
|
1116
|
+
return canvasConfigReadonly().ELEMENT_MARGIN_DEFAULT;
|
|
1117
|
+
}
|
|
1118
|
+
/** Tính chiều rộng của wait config node */
|
|
1119
|
+
static getWidthWaitConfig(hasWaitConfig) {
|
|
1120
|
+
if (hasWaitConfig) {
|
|
1121
|
+
return (hasWaitConfig.specific_width ?? canvasConfigReadonly().ELEMENT_WAIT_DEFAULT) + canvasConfigReadonly().DISTANCE_WAIT_TO_NEXT_LINE;
|
|
1122
|
+
}
|
|
1123
|
+
return canvasConfigReadonly().ELEMENT_WAIT_DEFAULT + canvasConfigReadonly().DISTANCE_WAIT_TO_NEXT_LINE;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
const canvasConfig = signal({
|
|
1128
|
+
ELEMENT_WIDTH_DEFAULT_BRANCH_WHEN_NOT_ELEMENT: 60,
|
|
1129
|
+
ELEMENT_MARGIN_DEFAULT: 60,
|
|
1130
|
+
ELEMENT_MARGIN_DEFAULT_BRANCH: 16,
|
|
1131
|
+
ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT: 60,
|
|
1132
|
+
DEFAULT_BRANCH_WHEN_NO_ELEMENT: 100,
|
|
1133
|
+
ELEMENT_HEIGHT_CURVE: 10,
|
|
1134
|
+
ELEMENT_SVG_STROKE_COLOR: '9ca2ad',
|
|
1135
|
+
ELEMENT_SVG_STROKE_WIDTH: '1',
|
|
1136
|
+
ELEMENT_MARGIN_DEFAULT_BRANCH_TO_ELEMENT_FIRST: 78,
|
|
1137
|
+
DISTANCE_TO_WAIT: 30,
|
|
1138
|
+
WAIT_TO_ELEMENT: 50,
|
|
1139
|
+
ELEMENT_WAIT_DEFAULT: 28,
|
|
1140
|
+
DISTANCE_WAIT_TO_NEXT_LINE: 4,
|
|
1141
|
+
TYPE_ELEMENT_EXIT: 'EXIT',
|
|
1142
|
+
TYPE_ELEMENT_WORKFLOW: 'WORKFLOW',
|
|
1143
|
+
WIDTH_ELEMENT_DEFAULT: 165,
|
|
1144
|
+
HEIGHT_ELEMENT_DEFAULT: 48,
|
|
1145
|
+
ADD_ICON_DIAMETER: 24,
|
|
1146
|
+
});
|
|
1147
|
+
const canvasConfigReadonly = canvasConfig.asReadonly();
|
|
1148
|
+
const storeDataDefault = {
|
|
1149
|
+
positionMaxLeft: 0,
|
|
1150
|
+
positionMaxTop: 0,
|
|
1151
|
+
widthHeightSvgCanvas: { heightSvg: window.innerHeight, widthSvg: window.innerWidth },
|
|
1152
|
+
};
|
|
1153
|
+
class LibsUiDiagramDrawService {
|
|
1154
|
+
configCanvas = signal(undefined);
|
|
1155
|
+
orientation = signal('vertical');
|
|
1156
|
+
elementSvg = signal(undefined);
|
|
1157
|
+
attributeSvgD = signal('');
|
|
1158
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1159
|
+
componentRefs = signal([]);
|
|
1160
|
+
// Virtualization Tracking
|
|
1161
|
+
virtualViewport;
|
|
1162
|
+
virtualFlatElements = [];
|
|
1163
|
+
virtualFlatBranches = [];
|
|
1164
|
+
virtualNodesMap = new Map();
|
|
1165
|
+
updateViewport(vp) {
|
|
1166
|
+
this.virtualViewport = vp;
|
|
1167
|
+
if (this.configCanvas()?.useVirtualizationTemplate) {
|
|
1168
|
+
this.applyVirtualizationDiff();
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
set setConfig(config) {
|
|
1172
|
+
this.configCanvas.set(config);
|
|
1173
|
+
}
|
|
1174
|
+
set setConfigCanvas(config) {
|
|
1175
|
+
if (!config) {
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
canvasConfig.update((current) => ({ ...current, ...config }));
|
|
1179
|
+
}
|
|
1180
|
+
configXYElements(elements, updateSVG, positionX) {
|
|
1181
|
+
const storeData = this.configCanvas()?.storeDataDefine ?? storeDataDefault;
|
|
1182
|
+
const maxLeftOrigin = storeData.positionMaxLeft;
|
|
1183
|
+
const maxTopOrigin = storeData.positionMaxTop ?? 0;
|
|
1184
|
+
if (this.orientation() === 'horizontal') {
|
|
1185
|
+
MoLibDiagramHorizontalCalculatorCoordinatesUtil.setXYElements(elements, storeData, positionX);
|
|
1186
|
+
}
|
|
1187
|
+
else {
|
|
1188
|
+
MoLibDiagramCalculatorCoordinatesUtil.setXYElements(elements, storeData, positionX);
|
|
1189
|
+
}
|
|
1190
|
+
const maxLeftNew = storeData.positionMaxLeft;
|
|
1191
|
+
const maxTopNew = storeData.positionMaxTop ?? 0;
|
|
1192
|
+
if (!this.elementSvg()) {
|
|
1193
|
+
this.elementSvg.set(document.createElementNS('http://www.w3.org/2000/svg', 'path'));
|
|
1194
|
+
}
|
|
1195
|
+
if (!updateSVG && this.configCanvas()?.svgContainer && this.elementSvg()) {
|
|
1196
|
+
setAttributeSvgAndAppend(this.configCanvas()?.svgContainer, this.elementSvg(), '');
|
|
1197
|
+
}
|
|
1198
|
+
if (this.orientation() === 'horizontal' && maxTopOrigin !== maxTopNew) {
|
|
1199
|
+
this.updatePositionElements(maxTopNew, elements, storeData);
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
if (this.orientation() === 'vertical' && maxLeftOrigin !== maxLeftNew) {
|
|
1203
|
+
this.updatePositionElements(maxLeftNew, elements, storeData);
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
if (this.elementSvg()) {
|
|
1207
|
+
this.attributeSvgD.set('');
|
|
1208
|
+
this.buildSvgElement(elements);
|
|
1209
|
+
this.elementSvg()?.setAttribute('d', this.attributeSvgD());
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
updatePositionElements(positionOffset, elements, config) {
|
|
1213
|
+
if (positionOffset && positionOffset < 0) {
|
|
1214
|
+
positionOffset = positionOffset * -1 + 200; // 100 là khoảng cách cách biên
|
|
1215
|
+
}
|
|
1216
|
+
if (this.orientation() === 'horizontal') {
|
|
1217
|
+
MoLibDiagramHorizontalCalculatorCoordinatesUtil.setXYElements(elements, { ...config, updateSVG: true }, positionOffset);
|
|
1218
|
+
}
|
|
1219
|
+
else {
|
|
1220
|
+
MoLibDiagramCalculatorCoordinatesUtil.setXYElements(elements, { ...config, updateSVG: true }, positionOffset);
|
|
1221
|
+
}
|
|
1222
|
+
if (!this.elementSvg()) {
|
|
1223
|
+
this.elementSvg.set(document.createElementNS('http://www.w3.org/2000/svg', 'path'));
|
|
1224
|
+
}
|
|
1225
|
+
if (this.elementSvg()) {
|
|
1226
|
+
this.attributeSvgD.set('');
|
|
1227
|
+
this.buildSvgElement(elements);
|
|
1228
|
+
this.elementSvg()?.setAttribute('d', this.attributeSvgD());
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
buildSvgElement(elements) {
|
|
1232
|
+
elements.forEach((currentElement) => {
|
|
1233
|
+
if (currentElement.attributeSvgD) {
|
|
1234
|
+
this.attributeSvgD.update((prev) => `${prev ?? ''} ${currentElement.attributeSvgD}`);
|
|
1235
|
+
}
|
|
1236
|
+
if (currentElement.nodeOtherConfig?.attributeSvgD) {
|
|
1237
|
+
this.attributeSvgD.update((prev) => `${prev ?? ''} ${currentElement.nodeOtherConfig?.attributeSvgD}`);
|
|
1238
|
+
}
|
|
1239
|
+
if (!currentElement.branches || !currentElement.branches.length) {
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
currentElement.branches.forEach((branch) => {
|
|
1243
|
+
if (branch.attributeSvgD) {
|
|
1244
|
+
this.attributeSvgD.update((prev) => `${prev ?? ''} ${branch.attributeSvgD}`);
|
|
1245
|
+
}
|
|
1246
|
+
if (branch.nodeOtherConfig?.attributeSvgD) {
|
|
1247
|
+
this.attributeSvgD.update((prev) => `${prev ?? ''} ${branch.nodeOtherConfig?.attributeSvgD}`);
|
|
1248
|
+
}
|
|
1249
|
+
if (!branch.elements.length) {
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
this.buildSvgElement(branch.elements);
|
|
1253
|
+
});
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
/**
|
|
1257
|
+
* Render tất cả nodes từ elements array
|
|
1258
|
+
*/
|
|
1259
|
+
renderNodes(elements) {
|
|
1260
|
+
if (this.configCanvas()?.useVirtualizationTemplate) {
|
|
1261
|
+
this.extractVirtualElements(elements);
|
|
1262
|
+
this.applyVirtualizationDiff();
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
this.clearNodes();
|
|
1266
|
+
this.renderElementsRecursively(elements, true, undefined);
|
|
1267
|
+
this.renderBranchLabels(elements);
|
|
1268
|
+
}
|
|
1269
|
+
extractVirtualElements(elements) {
|
|
1270
|
+
this.virtualFlatElements = [];
|
|
1271
|
+
this.virtualFlatBranches = [];
|
|
1272
|
+
const scan = (list, branch, isFirstLevel = true) => {
|
|
1273
|
+
list.forEach((el, index) => {
|
|
1274
|
+
const shouldShowArrow = !(isFirstLevel && index === 0);
|
|
1275
|
+
this.virtualFlatElements.push({ element: el, branch, isOtherConfig: false, shouldShowArrow, parentId: el.id });
|
|
1276
|
+
if (el.nodeOtherConfig)
|
|
1277
|
+
this.virtualFlatElements.push({ element: el.nodeOtherConfig, branch, isOtherConfig: true, shouldShowArrow: false, parentId: el.id });
|
|
1278
|
+
if (el.branches) {
|
|
1279
|
+
el.branches.forEach((b, bIndex) => {
|
|
1280
|
+
if (!b.label)
|
|
1281
|
+
b.label = `Nhánh ${bIndex + 1}`;
|
|
1282
|
+
this.virtualFlatBranches.push(b);
|
|
1283
|
+
if (b.elements)
|
|
1284
|
+
scan(b.elements, b, false);
|
|
1285
|
+
if (b.nodeOtherConfig)
|
|
1286
|
+
this.virtualFlatElements.push({ element: b.nodeOtherConfig, branch: b, isOtherConfig: true, shouldShowArrow: false, parentId: 'branch_' + bIndex + '_' + el.id });
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
});
|
|
1290
|
+
};
|
|
1291
|
+
scan(elements, undefined, true);
|
|
1292
|
+
}
|
|
1293
|
+
applyVirtualizationDiff() {
|
|
1294
|
+
const vp = this.virtualViewport;
|
|
1295
|
+
if (!vp)
|
|
1296
|
+
return;
|
|
1297
|
+
const config = this.configCanvas();
|
|
1298
|
+
const container = config?.nodesContainer?.nativeElement;
|
|
1299
|
+
if (!container || !config)
|
|
1300
|
+
return;
|
|
1301
|
+
const buffer = 300;
|
|
1302
|
+
const visibleIds = new Set();
|
|
1303
|
+
this.virtualFlatElements.forEach((item) => {
|
|
1304
|
+
const el = item.element;
|
|
1305
|
+
if (el.translateX === undefined || el.translateY === undefined)
|
|
1306
|
+
return;
|
|
1307
|
+
if (el.translateX + (el.specific_width || 165) < vp.x - buffer || el.translateX > vp.x + vp.width + buffer || el.translateY + (el.specific_height || 48) < vp.y - buffer || el.translateY > vp.y + vp.height + buffer)
|
|
1308
|
+
return;
|
|
1309
|
+
const id = String(item.parentId) + (item.isOtherConfig ? '_other' : '');
|
|
1310
|
+
visibleIds.add(id);
|
|
1311
|
+
if (!this.virtualNodesMap.has(id)) {
|
|
1312
|
+
const componentType = item.isOtherConfig ? config.nodeOtherConfigComponentType : config.nodeComponentType;
|
|
1313
|
+
if (componentType && config.viewContainerRef) {
|
|
1314
|
+
const ref = config.viewContainerRef.createComponent(componentType);
|
|
1315
|
+
ref.setInput('element', el);
|
|
1316
|
+
if (item.branch)
|
|
1317
|
+
ref.setInput('branch', item.branch);
|
|
1318
|
+
ref.setInput('orientation', this.orientation());
|
|
1319
|
+
const domEl = ref.location.nativeElement;
|
|
1320
|
+
domEl.classList.add('diagram-node-dynamic');
|
|
1321
|
+
applyElementStyle(domEl, el);
|
|
1322
|
+
container.appendChild(domEl);
|
|
1323
|
+
let arrow;
|
|
1324
|
+
if (item.shouldShowArrow && !item.isOtherConfig) {
|
|
1325
|
+
arrow = this.createArrowIndicator(el, container);
|
|
1326
|
+
}
|
|
1327
|
+
this.virtualNodesMap.set(id, { component: ref, arrow });
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
else {
|
|
1331
|
+
const cache = this.virtualNodesMap.get(id);
|
|
1332
|
+
if (cache) {
|
|
1333
|
+
if (cache.component)
|
|
1334
|
+
applyElementStyle(cache.component.location.nativeElement, el);
|
|
1335
|
+
if (cache.arrow) {
|
|
1336
|
+
const arrowSize = 16;
|
|
1337
|
+
const nodeWidth = el.specific_width ?? 165;
|
|
1338
|
+
const nodeHeight = el.specific_height ?? 48;
|
|
1339
|
+
if (this.orientation() === 'horizontal') {
|
|
1340
|
+
cache.arrow.innerHTML = `
|
|
1341
|
+
<svg width="${arrowSize}" height="${arrowSize}" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
1342
|
+
<path d="M8 4L16 8L8 12Z" fill="#9ca3af"/>
|
|
1343
|
+
</svg>
|
|
1344
|
+
`;
|
|
1345
|
+
cache.arrow.style.left = `${el.translateX - arrowSize}px`;
|
|
1346
|
+
cache.arrow.style.top = `${el.translateY + nodeHeight / 2 - arrowSize / 2}px`;
|
|
1347
|
+
}
|
|
1348
|
+
else {
|
|
1349
|
+
cache.arrow.innerHTML = `
|
|
1350
|
+
<svg width="${arrowSize}" height="${arrowSize}" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
1351
|
+
<path d="M8 16L4 12H12L8 16Z" fill="#9ca3af"/>
|
|
1352
|
+
</svg>
|
|
1353
|
+
`;
|
|
1354
|
+
cache.arrow.style.left = `${el.translateX + nodeWidth / 2 - arrowSize / 2}px`;
|
|
1355
|
+
cache.arrow.style.top = `${el.translateY - arrowSize}px`;
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
});
|
|
1361
|
+
this.virtualFlatBranches.forEach((branch, index) => {
|
|
1362
|
+
const x = branch.specific_start_branch_x;
|
|
1363
|
+
const y = branch.specific_start_branch_y;
|
|
1364
|
+
if (x === undefined || y === undefined)
|
|
1365
|
+
return;
|
|
1366
|
+
if (x < vp.x - buffer || x > vp.x + vp.width + buffer || y < vp.y - buffer || y > vp.y + vp.height + buffer)
|
|
1367
|
+
return;
|
|
1368
|
+
const id = 'branch_' + index + '_' + Math.round(x) + '_' + Math.round(y);
|
|
1369
|
+
visibleIds.add(id);
|
|
1370
|
+
if (!this.virtualNodesMap.has(id)) {
|
|
1371
|
+
const labelDiv = this.createBranchLabel(branch);
|
|
1372
|
+
if (labelDiv)
|
|
1373
|
+
this.virtualNodesMap.set(id, { label: labelDiv });
|
|
1374
|
+
}
|
|
1375
|
+
else {
|
|
1376
|
+
const cache = this.virtualNodesMap.get(id);
|
|
1377
|
+
if (cache?.label) {
|
|
1378
|
+
const labelDiv = cache.label;
|
|
1379
|
+
const labelRect = labelDiv.getBoundingClientRect();
|
|
1380
|
+
const labelW = labelRect.width || labelDiv.offsetWidth;
|
|
1381
|
+
const labelH = labelRect.height || labelDiv.offsetHeight;
|
|
1382
|
+
if (this.orientation() === 'vertical') {
|
|
1383
|
+
labelDiv.style.left = `${x - labelW / 2 - 4}px`;
|
|
1384
|
+
labelDiv.style.top = `${y - labelH / 2 + 4}px`;
|
|
1385
|
+
}
|
|
1386
|
+
else {
|
|
1387
|
+
labelDiv.style.left = `${x + 4}px`;
|
|
1388
|
+
labelDiv.style.top = `${y - labelH / 2}px`; // center over wire
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
});
|
|
1393
|
+
Array.from(this.virtualNodesMap.entries()).forEach(([id, cache]) => {
|
|
1394
|
+
if (!visibleIds.has(id)) {
|
|
1395
|
+
cache.component?.destroy();
|
|
1396
|
+
cache.arrow?.remove();
|
|
1397
|
+
cache.label?.remove();
|
|
1398
|
+
this.virtualNodesMap.delete(id);
|
|
1399
|
+
}
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
/**
|
|
1403
|
+
* Render elements và branches một cách đệ quy
|
|
1404
|
+
*/
|
|
1405
|
+
renderElementsRecursively(elements, isFirstLevel = true, branch) {
|
|
1406
|
+
elements.forEach((element, index) => {
|
|
1407
|
+
if (element.translateX !== undefined && element.translateY !== undefined) {
|
|
1408
|
+
// Main node: có arrow nếu không phải element đầu tiên của level đầu
|
|
1409
|
+
const shouldShowArrow = !(isFirstLevel && index === 0);
|
|
1410
|
+
this.createNode(element, branch, shouldShowArrow, false);
|
|
1411
|
+
}
|
|
1412
|
+
// Render nodeOtherConfig nếu có (nodes phụ) - KHÔNG có arrow
|
|
1413
|
+
if (element.nodeOtherConfig && element.nodeOtherConfig.translateX !== undefined && element.nodeOtherConfig.translateY !== undefined) {
|
|
1414
|
+
this.createNode(element.nodeOtherConfig, branch, false, true);
|
|
1415
|
+
}
|
|
1416
|
+
// Render branches nếu có
|
|
1417
|
+
if (element.branches && element.branches.length > 0) {
|
|
1418
|
+
element.branches.forEach((branch) => {
|
|
1419
|
+
if (branch.elements && branch.elements.length > 0) {
|
|
1420
|
+
this.renderElementsRecursively(branch.elements, false, branch);
|
|
1421
|
+
}
|
|
1422
|
+
if (branch.nodeOtherConfig && branch.nodeOtherConfig.translateX !== undefined && branch.nodeOtherConfig.translateY !== undefined) {
|
|
1423
|
+
this.createNode(branch.nodeOtherConfig, branch, false, true);
|
|
1424
|
+
}
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
/**
|
|
1430
|
+
* Tạo một node component động
|
|
1431
|
+
*/
|
|
1432
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1433
|
+
createNode(element, branch, shouldShowArrow, isNodeOtherConfig) {
|
|
1434
|
+
const config = this.configCanvas();
|
|
1435
|
+
if (!config?.viewContainerRef) {
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
if (config.useVirtualizationTemplate) {
|
|
1439
|
+
if (config.nodesContainer?.nativeElement) {
|
|
1440
|
+
if (shouldShowArrow && !isNodeOtherConfig) {
|
|
1441
|
+
this.createArrowIndicator(element, config.nodesContainer.nativeElement);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
const componentType = isNodeOtherConfig ? config.nodeOtherConfigComponentType : config.nodeComponentType;
|
|
1447
|
+
if (!componentType) {
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
const componentRef = config.viewContainerRef.createComponent(componentType);
|
|
1451
|
+
const el = componentRef.location.nativeElement;
|
|
1452
|
+
componentRef.setInput('element', element);
|
|
1453
|
+
if (branch) {
|
|
1454
|
+
componentRef.setInput('branch', branch);
|
|
1455
|
+
}
|
|
1456
|
+
componentRef.setInput('orientation', this.orientation());
|
|
1457
|
+
el.classList.add('diagram-node-dynamic');
|
|
1458
|
+
applyElementStyle(el, element);
|
|
1459
|
+
if (config.nodesContainer?.nativeElement) {
|
|
1460
|
+
config.nodesContainer.nativeElement.appendChild(el);
|
|
1461
|
+
if (shouldShowArrow && !isNodeOtherConfig) {
|
|
1462
|
+
this.createArrowIndicator(element, config.nodesContainer.nativeElement);
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
this.componentRefs.update((refs) => [...refs, componentRef]); // Lưu reference để cleanup sau
|
|
1466
|
+
return componentRef;
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* Clear tất cả nodes đã tạo
|
|
1470
|
+
*/
|
|
1471
|
+
clearNodes() {
|
|
1472
|
+
this.componentRefs().forEach((ref) => {
|
|
1473
|
+
ref.destroy();
|
|
1474
|
+
});
|
|
1475
|
+
this.componentRefs.set([]);
|
|
1476
|
+
Array.from(this.virtualNodesMap.values()).forEach((cache) => {
|
|
1477
|
+
cache.component?.destroy();
|
|
1478
|
+
cache.arrow?.remove();
|
|
1479
|
+
cache.label?.remove();
|
|
1480
|
+
});
|
|
1481
|
+
this.virtualNodesMap.clear();
|
|
1482
|
+
const config = this.configCanvas();
|
|
1483
|
+
const nodesContainer = config?.nodesContainer?.nativeElement;
|
|
1484
|
+
if (nodesContainer) {
|
|
1485
|
+
if (!config?.useVirtualizationTemplate) {
|
|
1486
|
+
const dynamicNodes = nodesContainer.querySelectorAll('.diagram-node-dynamic');
|
|
1487
|
+
dynamicNodes.forEach((node) => node.remove());
|
|
1488
|
+
}
|
|
1489
|
+
const labels = nodesContainer.querySelectorAll('.branch-label');
|
|
1490
|
+
labels.forEach((label) => label.remove());
|
|
1491
|
+
const arrows = nodesContainer.querySelectorAll('.node-arrow-indicator');
|
|
1492
|
+
arrows.forEach((arrow) => arrow.remove());
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
/**
|
|
1496
|
+
* Tạo mũi tên indicator chỉ hướng vào node.
|
|
1497
|
+
* - vertical: mũi tên ▼ phía TRÊN node (luồng trên → xuống)
|
|
1498
|
+
* - horizontal: mũi tên ► phía TRÁI node (luồng trái → phải)
|
|
1499
|
+
*/
|
|
1500
|
+
createArrowIndicator(element, container) {
|
|
1501
|
+
if (element.translateX === undefined || element.translateY === undefined) {
|
|
1502
|
+
return undefined;
|
|
1503
|
+
}
|
|
1504
|
+
const arrow = document.createElement('div');
|
|
1505
|
+
arrow.className = 'node-arrow-indicator';
|
|
1506
|
+
arrow.style.position = 'absolute';
|
|
1507
|
+
arrow.style.zIndex = '1';
|
|
1508
|
+
arrow.style.pointerEvents = 'none';
|
|
1509
|
+
const arrowSize = 16;
|
|
1510
|
+
const nodeWidth = element.specific_width ?? 0;
|
|
1511
|
+
const nodeHeight = element.specific_height ?? 0;
|
|
1512
|
+
if (this.orientation() === 'horizontal') {
|
|
1513
|
+
// Mũi tên ► ở bên TRÁI node, căn giữa theo chiều cao
|
|
1514
|
+
arrow.innerHTML = `
|
|
1515
|
+
<svg width="${arrowSize}" height="${arrowSize}" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
1516
|
+
<path d="M8 4L16 8L8 12Z" fill="#9ca3af"/>
|
|
1517
|
+
</svg>
|
|
1518
|
+
`;
|
|
1519
|
+
arrow.style.left = `${element.translateX - arrowSize}px`;
|
|
1520
|
+
arrow.style.top = `${element.translateY + nodeHeight / 2 - arrowSize / 2}px`;
|
|
1521
|
+
}
|
|
1522
|
+
else {
|
|
1523
|
+
// Mũi tên ▼ ở phía TRÊN node, căn giữa theo chiều rộng
|
|
1524
|
+
arrow.innerHTML = `
|
|
1525
|
+
<svg width="${arrowSize}" height="${arrowSize}" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
1526
|
+
<path d="M8 16L4 12H12L8 16Z" fill="#9ca3af"/>
|
|
1527
|
+
</svg>
|
|
1528
|
+
`;
|
|
1529
|
+
arrow.style.left = `${element.translateX + nodeWidth / 2 - arrowSize / 2}px`;
|
|
1530
|
+
arrow.style.top = `${element.translateY - arrowSize}px`;
|
|
1531
|
+
}
|
|
1532
|
+
container.appendChild(arrow);
|
|
1533
|
+
return arrow;
|
|
1534
|
+
}
|
|
1535
|
+
/**
|
|
1536
|
+
* Render branch labels (text annotations on branches)
|
|
1537
|
+
*/
|
|
1538
|
+
renderBranchLabels(elements) {
|
|
1539
|
+
const svgContainer = this.configCanvas()?.svgContainer?.nativeElement;
|
|
1540
|
+
if (!svgContainer) {
|
|
1541
|
+
return;
|
|
1542
|
+
}
|
|
1543
|
+
elements.forEach((element) => {
|
|
1544
|
+
if (element.branches && element.branches.length > 0) {
|
|
1545
|
+
element.branches.forEach((branch, index) => {
|
|
1546
|
+
if (branch.specific_start_branch_x !== undefined && branch.specific_start_branch_y !== undefined) {
|
|
1547
|
+
if (!branch.label) {
|
|
1548
|
+
branch.label = `Nhánh ${index + 1}`;
|
|
1549
|
+
}
|
|
1550
|
+
this.createBranchLabel(branch);
|
|
1551
|
+
}
|
|
1552
|
+
if (branch.elements && branch.elements.length > 0) {
|
|
1553
|
+
this.renderBranchLabels(branch.elements);
|
|
1554
|
+
}
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
createBranchLabel(branch) {
|
|
1560
|
+
const config = this.configCanvas();
|
|
1561
|
+
const container = config?.nodesContainer?.nativeElement;
|
|
1562
|
+
if (!container) {
|
|
1563
|
+
return undefined;
|
|
1564
|
+
}
|
|
1565
|
+
const labelDiv = document.createElement('div');
|
|
1566
|
+
labelDiv.className = 'branch-label';
|
|
1567
|
+
labelDiv.textContent = branch.label || '';
|
|
1568
|
+
labelDiv.style.position = 'absolute';
|
|
1569
|
+
labelDiv.style.padding = '2px 7px';
|
|
1570
|
+
labelDiv.style.borderRadius = '10px';
|
|
1571
|
+
labelDiv.style.backgroundColor = branch.labelBgColor || '#f3f4f6';
|
|
1572
|
+
labelDiv.style.fontSize = '11px';
|
|
1573
|
+
labelDiv.style.fontFamily = 'system-ui, -apple-system, sans-serif';
|
|
1574
|
+
labelDiv.style.color = '#374151';
|
|
1575
|
+
labelDiv.style.whiteSpace = 'nowrap';
|
|
1576
|
+
labelDiv.style.zIndex = '2';
|
|
1577
|
+
labelDiv.style.pointerEvents = 'none';
|
|
1578
|
+
container.appendChild(labelDiv);
|
|
1579
|
+
const startX = branch.specific_start_branch_x ?? 0;
|
|
1580
|
+
const startY = branch.specific_start_branch_y ?? 0;
|
|
1581
|
+
const labelW = labelDiv.offsetWidth;
|
|
1582
|
+
const labelH = labelDiv.offsetHeight || 18;
|
|
1583
|
+
if (this.orientation() === 'horizontal') {
|
|
1584
|
+
/**
|
|
1585
|
+
* Horizontal: label nằm TRÊN đường line nằm ngang.
|
|
1586
|
+
* specific_start_branch = điểm bắt đầu đoạn thẳng ngang đến element.
|
|
1587
|
+
* Đặt label tịnh tiến sang phải 8px từ ngoặt nhánh, và căn giữa dây.
|
|
1588
|
+
*/
|
|
1589
|
+
labelDiv.style.left = `${startX + 4}px`;
|
|
1590
|
+
labelDiv.style.top = `${startY - labelH / 2}px`;
|
|
1591
|
+
}
|
|
1592
|
+
else {
|
|
1593
|
+
/**
|
|
1594
|
+
* Vertical: label nằm BÊN TRÁI đường ngang, căn giữa theo Y.
|
|
1595
|
+
* specific_start_branch = điểm đầu đoạn thẳng đến element (sau curve).
|
|
1596
|
+
*/
|
|
1597
|
+
labelDiv.style.left = `${startX - labelW / 2 - 4}px`;
|
|
1598
|
+
labelDiv.style.top = `${startY - labelH / 2 + 4}px`;
|
|
1599
|
+
}
|
|
1600
|
+
return labelDiv;
|
|
1601
|
+
}
|
|
1602
|
+
/**
|
|
1603
|
+
* Render dấu "+" tại mỗi vị trí có thể thả element từ sidebar.
|
|
1604
|
+
* Service tự tạo DOM elements và inject vào nodesContainer.
|
|
1605
|
+
* @param flatElements Danh sách flat elements đã tính toán vị trí
|
|
1606
|
+
* @param onDrop Callback khi người dùng thả element vào một "+"
|
|
1607
|
+
* @param canDropFn (tùy chọn) Function kiểm tra điều kiện hiển thị drop zone.
|
|
1608
|
+
* ```
|
|
1609
|
+
*/
|
|
1610
|
+
renderDropZones(flatElements, onDrop, canDropFn, ruleConnectableElements) {
|
|
1611
|
+
this.clearDropZones();
|
|
1612
|
+
const container = this.configCanvas()?.nodesContainer?.nativeElement;
|
|
1613
|
+
if (!container)
|
|
1614
|
+
return;
|
|
1615
|
+
flatElements.forEach((el) => {
|
|
1616
|
+
if (el.code === canvasConfigReadonly().TYPE_ELEMENT_EXIT)
|
|
1617
|
+
return;
|
|
1618
|
+
if (el.element_type === canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW) {
|
|
1619
|
+
el.branches?.forEach((branch) => {
|
|
1620
|
+
if (canDropFn(undefined, el, branch, ruleConnectableElements))
|
|
1621
|
+
return;
|
|
1622
|
+
const top = this.orientation() === 'horizontal' ? this.calcDropZoneTopBranchH(el, branch) : this.calcDropZoneTopBranch(el, branch);
|
|
1623
|
+
const left = this.orientation() === 'horizontal' ? this.calcDropZoneLeftBranchH(el, branch) : this.calcDropZoneLeftBranch(el, branch);
|
|
1624
|
+
this.createDropZoneEl(container, top, left, el, branch, onDrop);
|
|
1625
|
+
});
|
|
1626
|
+
}
|
|
1627
|
+
else {
|
|
1628
|
+
if (!canDropFn(undefined, el, undefined, ruleConnectableElements))
|
|
1629
|
+
return;
|
|
1630
|
+
const top = this.orientation() === 'horizontal' ? this.calcDropZoneTopNodeH(el) : this.calcDropZoneTopNode(el);
|
|
1631
|
+
const left = this.orientation() === 'horizontal' ? this.calcDropZoneLeftNodeH(el) : this.calcDropZoneLeftNode(el);
|
|
1632
|
+
this.createDropZoneEl(container, top, left, el, undefined, onDrop);
|
|
1633
|
+
}
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
/** Xóa tất cả drop zone "+" khỏi DOM */
|
|
1637
|
+
clearDropZones() {
|
|
1638
|
+
const container = this.configCanvas()?.nodesContainer?.nativeElement;
|
|
1639
|
+
if (!container)
|
|
1640
|
+
return;
|
|
1641
|
+
container.querySelectorAll('.diagram-drop-zone').forEach((el) => el.remove());
|
|
1642
|
+
}
|
|
1643
|
+
createDropZoneEl(container, top, left, element, branch, onDrop) {
|
|
1644
|
+
const indicatorStyle = this.configCanvas()?.dropZoneStyle;
|
|
1645
|
+
const RECT_W = element.specific_width ?? canvasConfigReadonly().WIDTH_ELEMENT_DEFAULT;
|
|
1646
|
+
const RECT_H = element.specific_height ?? canvasConfigReadonly().HEIGHT_ELEMENT_DEFAULT;
|
|
1647
|
+
// Tâm của nút tròn
|
|
1648
|
+
const centerX = left + canvasConfigReadonly().ADD_ICON_DIAMETER / 2;
|
|
1649
|
+
const centerY = top + canvasConfigReadonly().ADD_ICON_DIAMETER / 2;
|
|
1650
|
+
// Hit zone vô hình: rộng hơn rectangle một khoản padding để bắt drag sớm hơn
|
|
1651
|
+
const HIT_PAD = 20;
|
|
1652
|
+
const HIT_W = RECT_W + HIT_PAD * 2;
|
|
1653
|
+
const HIT_H = RECT_H + HIT_PAD * 2;
|
|
1654
|
+
const hitLeft = centerX - HIT_W / 2;
|
|
1655
|
+
const hitTop = centerY - HIT_H / 2;
|
|
1656
|
+
// ── Hit zone (wrapper vô hình, vùng nhận drag rộng) ─────────────────
|
|
1657
|
+
const hitZone = document.createElement('div');
|
|
1658
|
+
hitZone.className = 'diagram-drop-zone';
|
|
1659
|
+
hitZone.style.cssText = `
|
|
1660
|
+
position: absolute;
|
|
1661
|
+
top: ${hitTop}px;
|
|
1662
|
+
left: ${hitLeft}px;
|
|
1663
|
+
z-index: 9;
|
|
1664
|
+
width: ${HIT_W}px;
|
|
1665
|
+
height: ${HIT_H}px;
|
|
1666
|
+
cursor: pointer;
|
|
1667
|
+
`;
|
|
1668
|
+
// ── Indicator (con bên trong, phần hiển thị) ─────────────────────────
|
|
1669
|
+
const indicator = document.createElement('div');
|
|
1670
|
+
indicator.style.pointerEvents = 'none'; // tránh tranh chấp event với hitZone
|
|
1671
|
+
// Vị trí dot trong hitZone (căn giữa hitZone)
|
|
1672
|
+
const dotOffsetLeft = HIT_W / 2 - canvasConfigReadonly().ADD_ICON_DIAMETER / 2;
|
|
1673
|
+
const dotOffsetTop = HIT_H / 2 - canvasConfigReadonly().ADD_ICON_DIAMETER / 2;
|
|
1674
|
+
// Vị trí rectangle trong hitZone (căn giữa hitZone)
|
|
1675
|
+
const rectOffsetLeft = HIT_PAD;
|
|
1676
|
+
const rectOffsetTop = HIT_H / 2 - RECT_H / 2;
|
|
1677
|
+
const applyDotStyle = () => {
|
|
1678
|
+
const s = indicatorStyle;
|
|
1679
|
+
const size = canvasConfigReadonly().ADD_ICON_DIAMETER;
|
|
1680
|
+
const bg = s?.backgroundColor ?? '#3b82f6';
|
|
1681
|
+
const iconColor = s?.iconColor ?? '#ffffff';
|
|
1682
|
+
const opacity = s?.opacity ?? 0.95;
|
|
1683
|
+
const borderRadius = s?.borderRadius ?? '50%';
|
|
1684
|
+
setStylesElement(indicator, {
|
|
1685
|
+
position: 'absolute',
|
|
1686
|
+
top: `${dotOffsetTop}px`,
|
|
1687
|
+
left: `${dotOffsetLeft}px`,
|
|
1688
|
+
width: `${size}px`,
|
|
1689
|
+
height: `${size}px`,
|
|
1690
|
+
pointerEvents: 'none',
|
|
1691
|
+
display: 'flex',
|
|
1692
|
+
alignItems: 'center',
|
|
1693
|
+
justifyContent: 'center',
|
|
1694
|
+
transition: 'opacity 0.15s ease',
|
|
1695
|
+
border: 'none',
|
|
1696
|
+
});
|
|
1697
|
+
if (s?.renderIndicator) {
|
|
1698
|
+
indicator.innerHTML = s.renderIndicator(size);
|
|
1699
|
+
}
|
|
1700
|
+
else {
|
|
1701
|
+
indicator.innerHTML = `
|
|
1702
|
+
<div style="
|
|
1703
|
+
width: ${size}px;
|
|
1704
|
+
height: ${size}px;
|
|
1705
|
+
background-color: ${bg};
|
|
1706
|
+
border-radius: ${borderRadius};
|
|
1707
|
+
opacity: ${opacity};
|
|
1708
|
+
display: flex;
|
|
1709
|
+
align-items: center;
|
|
1710
|
+
justify-content: center;
|
|
1711
|
+
pointer-events: none;
|
|
1712
|
+
"><i class="libs-ui-icon-add before:text-[${iconColor}]"></i></div>
|
|
1713
|
+
`;
|
|
1714
|
+
}
|
|
1715
|
+
};
|
|
1716
|
+
const applyRectStyle = () => {
|
|
1717
|
+
const s = indicatorStyle;
|
|
1718
|
+
const hoverBg = s?.hoverBackgroundColor ?? '#eef4ff';
|
|
1719
|
+
const hoverBorder = s?.hoverBorderColor ?? 'rgba(59, 130, 246, 0.7)';
|
|
1720
|
+
const hoverBorderStyle = s?.hoverBorderStyle ?? 'dashed';
|
|
1721
|
+
const hoverRadius = s?.hoverBorderRadius ?? 10;
|
|
1722
|
+
setStylesElement(indicator, {
|
|
1723
|
+
top: `${rectOffsetTop}px`,
|
|
1724
|
+
left: `${rectOffsetLeft}px`,
|
|
1725
|
+
width: `${RECT_W}px`,
|
|
1726
|
+
height: `${RECT_H}px`,
|
|
1727
|
+
borderRadius: `${hoverRadius}px`,
|
|
1728
|
+
backgroundColor: hoverBg,
|
|
1729
|
+
border: `2px ${hoverBorderStyle} ${hoverBorder}`,
|
|
1730
|
+
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
|
1731
|
+
});
|
|
1732
|
+
// Nếu có hàm render động cho trạng thái hover — gọi nó
|
|
1733
|
+
indicator.innerHTML = s?.renderHoverIndicator ? s.renderHoverIndicator() : '';
|
|
1734
|
+
};
|
|
1735
|
+
applyDotStyle();
|
|
1736
|
+
hitZone.appendChild(indicator);
|
|
1737
|
+
hitZone.addEventListener('dragenter', (e) => {
|
|
1738
|
+
e.preventDefault();
|
|
1739
|
+
applyRectStyle();
|
|
1740
|
+
});
|
|
1741
|
+
hitZone.addEventListener('dragover', (e) => {
|
|
1742
|
+
e.preventDefault();
|
|
1743
|
+
if (e.dataTransfer)
|
|
1744
|
+
e.dataTransfer.dropEffect = 'copy';
|
|
1745
|
+
});
|
|
1746
|
+
hitZone.addEventListener('dragleave', (e) => {
|
|
1747
|
+
if (!hitZone.contains(e.relatedTarget)) {
|
|
1748
|
+
applyDotStyle();
|
|
1749
|
+
}
|
|
1750
|
+
});
|
|
1751
|
+
hitZone.addEventListener('drop', (e) => {
|
|
1752
|
+
e.preventDefault();
|
|
1753
|
+
e.stopPropagation();
|
|
1754
|
+
const raw = e.dataTransfer?.getData('application/json') ?? '';
|
|
1755
|
+
onDrop(element, branch, raw);
|
|
1756
|
+
});
|
|
1757
|
+
container.appendChild(hitZone);
|
|
1758
|
+
}
|
|
1759
|
+
// ── Vertical drop zone helpers (existing) ───────────────────────────────────
|
|
1760
|
+
calcDropZoneTopBranch(el, branch) {
|
|
1761
|
+
const oc = branch.nodeOtherConfig;
|
|
1762
|
+
if (oc)
|
|
1763
|
+
return (oc.specific_y ?? 0) + (oc.specific_height ?? canvasConfigReadonly().ELEMENT_WAIT_DEFAULT) + 10;
|
|
1764
|
+
return (branch.specific_start_branch_y ?? 0) + 30;
|
|
1765
|
+
}
|
|
1766
|
+
calcDropZoneLeftBranch(el, branch) {
|
|
1767
|
+
const firstEl = branch.elements?.[0];
|
|
1768
|
+
if (firstEl?.translateX !== undefined)
|
|
1769
|
+
return firstEl.translateX + (firstEl.specific_width ?? canvasConfigReadonly().WIDTH_ELEMENT_DEFAULT) / 2 - canvasConfigReadonly().ADD_ICON_DIAMETER / 2;
|
|
1770
|
+
return (el.translateX ?? 0) + (el.specific_width ?? canvasConfigReadonly().WIDTH_ELEMENT_DEFAULT) / 2 - canvasConfigReadonly().ADD_ICON_DIAMETER / 2;
|
|
1771
|
+
}
|
|
1772
|
+
calcDropZoneTopNode(el) {
|
|
1773
|
+
const oc = el.nodeOtherConfig;
|
|
1774
|
+
if (oc)
|
|
1775
|
+
return (oc.specific_y ?? 0) + (oc.specific_height ?? canvasConfigReadonly().ELEMENT_WAIT_DEFAULT) + 10;
|
|
1776
|
+
return (el.translateY ?? 0) + (el.specific_height ?? canvasConfigReadonly().HEIGHT_ELEMENT_DEFAULT) + 10;
|
|
1777
|
+
}
|
|
1778
|
+
calcDropZoneLeftNode(el) {
|
|
1779
|
+
return (el.translateX ?? 0) + (el.specific_width ?? canvasConfigReadonly().WIDTH_ELEMENT_DEFAULT) / 2 - canvasConfigReadonly().ADD_ICON_DIAMETER / 2;
|
|
1780
|
+
}
|
|
1781
|
+
// ── Horizontal drop zone helpers ─────────────────────────────────────────────
|
|
1782
|
+
/**
|
|
1783
|
+
* Horizontal node: "+" nằm giữa đường line nối 2 node — căn giữa Y của element.
|
|
1784
|
+
*/
|
|
1785
|
+
calcDropZoneTopNodeH(el) {
|
|
1786
|
+
const D = canvasConfigReadonly().ADD_ICON_DIAMETER;
|
|
1787
|
+
const centerY = (el.translateY ?? 0) + (el.specific_height ?? canvasConfigReadonly().HEIGHT_ELEMENT_DEFAULT) / 2;
|
|
1788
|
+
return centerY - D / 2;
|
|
1789
|
+
}
|
|
1790
|
+
calcDropZoneLeftNodeH(el) {
|
|
1791
|
+
const D = canvasConfigReadonly().ADD_ICON_DIAMETER;
|
|
1792
|
+
const M = canvasConfigReadonly().ELEMENT_MARGIN_DEFAULT;
|
|
1793
|
+
const oc = el.nodeOtherConfig;
|
|
1794
|
+
if (oc) {
|
|
1795
|
+
const right = (oc.specific_x ?? 0) + (oc.specific_width ?? canvasConfigReadonly().ELEMENT_WAIT_DEFAULT);
|
|
1796
|
+
return right + M / 2 - D / 2;
|
|
1797
|
+
}
|
|
1798
|
+
const right = (el.translateX ?? 0) + (el.specific_width ?? canvasConfigReadonly().WIDTH_ELEMENT_DEFAULT);
|
|
1799
|
+
// Midpoint của khoảng trống giữa node và node tiếp theo (gap = ELEMENT_MARGIN_DEFAULT)
|
|
1800
|
+
return right + M / 2 - D / 2;
|
|
1801
|
+
}
|
|
1802
|
+
/**
|
|
1803
|
+
* Horizontal branch: "+" nằm căn giữa Y của branch line, sau phần tử cuối trong nhánh.
|
|
1804
|
+
*/
|
|
1805
|
+
calcDropZoneTopBranchH(el, branch) {
|
|
1806
|
+
const D = canvasConfigReadonly().ADD_ICON_DIAMETER;
|
|
1807
|
+
// specific_start_branch_y = tâm Y của đường nhánh
|
|
1808
|
+
const centerY = branch.specific_start_branch_y ?? 0;
|
|
1809
|
+
return centerY - D / 2;
|
|
1810
|
+
}
|
|
1811
|
+
calcDropZoneLeftBranchH(el, branch) {
|
|
1812
|
+
const D = canvasConfigReadonly().ADD_ICON_DIAMETER;
|
|
1813
|
+
const M = canvasConfigReadonly().ELEMENT_MARGIN_DEFAULT;
|
|
1814
|
+
// Sau nodeOtherConfig của nhánh (cổ nhánh)
|
|
1815
|
+
const oc = branch.nodeOtherConfig;
|
|
1816
|
+
if (oc?.specific_x !== undefined) {
|
|
1817
|
+
return (oc.specific_x ?? 0) + (oc.specific_width ?? canvasConfigReadonly().ELEMENT_WAIT_DEFAULT) + M / 2 - D / 2;
|
|
1818
|
+
}
|
|
1819
|
+
// Nếu nhánh không có nodeOtherConfig: đặt sau điểm rẽ nhánh
|
|
1820
|
+
return (branch.specific_start_branch_x ?? 0) + M / 2 - D / 2;
|
|
1821
|
+
}
|
|
1822
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiDiagramDrawService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1823
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiDiagramDrawService, providedIn: 'root' });
|
|
1824
|
+
}
|
|
1825
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiDiagramDrawService, decorators: [{
|
|
1826
|
+
type: Injectable,
|
|
1827
|
+
args: [{
|
|
1828
|
+
providedIn: 'root',
|
|
1829
|
+
}]
|
|
1830
|
+
}] });
|
|
1831
|
+
|
|
1832
|
+
class LibsUiDiagramDrawCanvasUtil {
|
|
1833
|
+
static buildFlatElement(elements, flatElementsContainer) {
|
|
1834
|
+
elements.forEach((element) => {
|
|
1835
|
+
flatElementsContainer.push(element);
|
|
1836
|
+
if (!element.branches || !element.branches.length) {
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1839
|
+
LibsUiDiagramDrawCanvasUtil.flatElementHasBranch(element, flatElementsContainer);
|
|
1840
|
+
});
|
|
1841
|
+
}
|
|
1842
|
+
static flatElementHasBranch(element, flatElementsContainer) {
|
|
1843
|
+
if (!element.branches || !element.branches.length) {
|
|
1844
|
+
return;
|
|
1845
|
+
}
|
|
1846
|
+
element.branches.forEach((branch) => {
|
|
1847
|
+
if (!branch.elements || !branch.elements.length) {
|
|
1848
|
+
return;
|
|
1849
|
+
}
|
|
1850
|
+
LibsUiDiagramDrawCanvasUtil.buildFlatElement(branch.elements, flatElementsContainer);
|
|
1851
|
+
});
|
|
1852
|
+
}
|
|
1853
|
+
static getDefaultStorageBranchEndCount() {
|
|
1854
|
+
return { branchContainer: undefined, count: 0, branchEmpty: 0 };
|
|
1855
|
+
}
|
|
1856
|
+
/**
|
|
1857
|
+
* data: sử dụng reference Object type nên count sẽ đệ quy cho đến node cuối cùng
|
|
1858
|
+
* data.branchContainer: branch cuối cùng của element không next đến idElement
|
|
1859
|
+
* data.count: số lượng branch có next_id đến idElement
|
|
1860
|
+
*/
|
|
1861
|
+
static getBranchContainerAndCountElementHasNextIdToElement(element, idElement, data, flowFlatElements) {
|
|
1862
|
+
if (!element || !element.branches) {
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
element.branches.forEach((branch) => {
|
|
1866
|
+
if (!branch.elements || !branch.elements.length) {
|
|
1867
|
+
data.count++;
|
|
1868
|
+
data.branchContainer = branch;
|
|
1869
|
+
data.branchEmpty++;
|
|
1870
|
+
return;
|
|
1871
|
+
}
|
|
1872
|
+
const lastElement = branch.elements[branch.elements.length - 1];
|
|
1873
|
+
if (!lastElement.next_id) {
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
if (lastElement.branches && lastElement.branches.length) {
|
|
1877
|
+
const dataOfLastElement = this.getDefaultStorageBranchEndCount();
|
|
1878
|
+
LibsUiDiagramDrawCanvasUtil.getBranchContainerAndCountElementHasNextIdToElement(lastElement, idElement, dataOfLastElement, flowFlatElements);
|
|
1879
|
+
if (!dataOfLastElement.count) {
|
|
1880
|
+
lastElement.next_id = '';
|
|
1881
|
+
return;
|
|
1882
|
+
}
|
|
1883
|
+
data.count++;
|
|
1884
|
+
data.branchContainer = branch;
|
|
1885
|
+
if (dataOfLastElement.count === 1) {
|
|
1886
|
+
return (data.branchContainer = dataOfLastElement.branchContainer);
|
|
1887
|
+
}
|
|
1888
|
+
return;
|
|
1889
|
+
}
|
|
1890
|
+
const nextOfLastElement = flowFlatElements.find((el) => el.id === lastElement.next_id);
|
|
1891
|
+
if (nextOfLastElement && nextOfLastElement.id !== idElement) {
|
|
1892
|
+
return;
|
|
1893
|
+
}
|
|
1894
|
+
data.count++;
|
|
1895
|
+
data.branchContainer = branch;
|
|
1896
|
+
return;
|
|
1897
|
+
});
|
|
1898
|
+
}
|
|
1899
|
+
static getElementHasCurrentBranchOrCurrentElement(currentBranch, currentElement, flatElements) {
|
|
1900
|
+
const data = {
|
|
1901
|
+
currentBranch: undefined,
|
|
1902
|
+
element: undefined,
|
|
1903
|
+
};
|
|
1904
|
+
if (!flatElements || !flatElements.length || (!currentBranch && !currentElement)) {
|
|
1905
|
+
return data;
|
|
1906
|
+
}
|
|
1907
|
+
if (!currentElement) {
|
|
1908
|
+
data.currentBranch = currentBranch;
|
|
1909
|
+
data.element = flatElements.find((el) => el.branches && !!el.branches.find((branch) => branch === currentBranch));
|
|
1910
|
+
return data;
|
|
1911
|
+
}
|
|
1912
|
+
const elementsHasBranches = flatElements.filter((item) => item.branches && item.branches.length);
|
|
1913
|
+
if (!elementsHasBranches) {
|
|
1914
|
+
return data;
|
|
1915
|
+
}
|
|
1916
|
+
for (const element of elementsHasBranches) {
|
|
1917
|
+
const branches = element.branches;
|
|
1918
|
+
if (!branches) {
|
|
1919
|
+
return data;
|
|
1920
|
+
}
|
|
1921
|
+
for (const branch of branches) {
|
|
1922
|
+
if (!branch.elements || !branch.elements.length) {
|
|
1923
|
+
continue;
|
|
1924
|
+
}
|
|
1925
|
+
if (branch.elements.find((item) => item.id === currentElement.id)) {
|
|
1926
|
+
data.currentBranch = branch;
|
|
1927
|
+
data.element = element;
|
|
1928
|
+
return data;
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
return data;
|
|
1933
|
+
}
|
|
1934
|
+
static findAndDeleteElementFromContainer(containerElement, elementDelete) {
|
|
1935
|
+
for (const index in containerElement) {
|
|
1936
|
+
const elementOfIndex = containerElement[index];
|
|
1937
|
+
if (elementOfIndex.id === elementDelete.id) {
|
|
1938
|
+
containerElement.splice(+index, 1);
|
|
1939
|
+
return true;
|
|
1940
|
+
}
|
|
1941
|
+
if (!elementOfIndex.branches || !elementOfIndex.branches.length) {
|
|
1942
|
+
continue;
|
|
1943
|
+
}
|
|
1944
|
+
for (const branch of elementOfIndex.branches) {
|
|
1945
|
+
if (!branch.elements || !branch.elements.length) {
|
|
1946
|
+
continue;
|
|
1947
|
+
}
|
|
1948
|
+
if (this.findAndDeleteElementFromContainer(branch.elements, elementDelete)) {
|
|
1949
|
+
return true;
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
return false;
|
|
1954
|
+
}
|
|
1955
|
+
static getBranchOfElement(elementId, flatElements, elementTypeWorkflow) {
|
|
1956
|
+
const flatElementHasBranch = flatElements.filter((item) => item.element_type === elementTypeWorkflow);
|
|
1957
|
+
if (!flatElementHasBranch) {
|
|
1958
|
+
return;
|
|
1959
|
+
}
|
|
1960
|
+
for (const item of flatElementHasBranch) {
|
|
1961
|
+
if (!item.branches || !item.branches.length) {
|
|
1962
|
+
continue;
|
|
1963
|
+
}
|
|
1964
|
+
for (const branch of item.branches) {
|
|
1965
|
+
const index = branch.elements.findIndex((el) => el.id === elementId);
|
|
1966
|
+
if (index === -1) {
|
|
1967
|
+
continue;
|
|
1968
|
+
}
|
|
1969
|
+
return branch;
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
const getDefaultConfigConstituentBranches = (code, dataDefault) => {
|
|
1976
|
+
dataDefault.branches = [
|
|
1977
|
+
{
|
|
1978
|
+
elements: [],
|
|
1979
|
+
},
|
|
1980
|
+
{
|
|
1981
|
+
elements: [],
|
|
1982
|
+
},
|
|
1983
|
+
];
|
|
1984
|
+
return dataDefault;
|
|
1985
|
+
};
|
|
1986
|
+
const findAndDeleteElementFromContainer = (containerElement, elementDelete) => {
|
|
1987
|
+
for (const index in containerElement) {
|
|
1988
|
+
const elementOfIndex = containerElement[index];
|
|
1989
|
+
if (elementOfIndex.id === elementDelete.id) {
|
|
1990
|
+
containerElement.splice(+index, 1);
|
|
1991
|
+
return true;
|
|
1992
|
+
}
|
|
1993
|
+
if (!elementOfIndex.branches || !elementOfIndex.branches.length) {
|
|
1994
|
+
continue;
|
|
1995
|
+
}
|
|
1996
|
+
for (const branch of elementOfIndex.branches) {
|
|
1997
|
+
if (!branch.elements || !branch.elements.length) {
|
|
1998
|
+
continue;
|
|
1999
|
+
}
|
|
2000
|
+
if (findAndDeleteElementFromContainer(branch.elements, elementDelete)) {
|
|
2001
|
+
return true;
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
return false;
|
|
2006
|
+
};
|
|
2007
|
+
|
|
2008
|
+
class LibsUiDiagramDrawCanvasService {
|
|
2009
|
+
elements;
|
|
2010
|
+
flatElements;
|
|
2011
|
+
handlerFunction;
|
|
2012
|
+
elementRuleConnectable = [];
|
|
2013
|
+
translateService = inject(TranslateService);
|
|
2014
|
+
set HandlerFunction(handlerFunction) {
|
|
2015
|
+
this.handlerFunction = handlerFunction;
|
|
2016
|
+
}
|
|
2017
|
+
get HandlerFunction() {
|
|
2018
|
+
return this.handlerFunction;
|
|
2019
|
+
}
|
|
2020
|
+
loadElements(elements, positionFirstNode, ruleConnectableElements) {
|
|
2021
|
+
if (!elements || !elements.length) {
|
|
2022
|
+
return;
|
|
2023
|
+
}
|
|
2024
|
+
this.flatElements = [];
|
|
2025
|
+
const firstElement = elements[0];
|
|
2026
|
+
firstElement.specific_x = positionFirstNode.x;
|
|
2027
|
+
firstElement.specific_y = positionFirstNode.y;
|
|
2028
|
+
this.Elements = elements;
|
|
2029
|
+
this.RuleConnectableElements = ruleConnectableElements;
|
|
2030
|
+
}
|
|
2031
|
+
set Elements(elements) {
|
|
2032
|
+
this.elements = elements;
|
|
2033
|
+
}
|
|
2034
|
+
get Elements() {
|
|
2035
|
+
return this.elements;
|
|
2036
|
+
}
|
|
2037
|
+
set RuleConnectableElements(elements) {
|
|
2038
|
+
this.elementRuleConnectable = elements;
|
|
2039
|
+
}
|
|
2040
|
+
get RuleConnectableElements() {
|
|
2041
|
+
return this.elementRuleConnectable;
|
|
2042
|
+
}
|
|
2043
|
+
checkRuleDragDrop(beforeElementDrag, currentElementDrag, afterElementDrag, ruleConnectableElements) {
|
|
2044
|
+
const ruleItemDrag = ruleConnectableElements.find((item) => item.elementCode === currentElementDrag?.code);
|
|
2045
|
+
const ruleAfterItemDrag = ruleConnectableElements.find((item) => afterElementDrag && item.elementCode === afterElementDrag.code);
|
|
2046
|
+
if (!!(ruleItemDrag && ruleItemDrag.elementCodeConnectableBefore.find((code) => code === beforeElementDrag?.code)) &&
|
|
2047
|
+
(!ruleAfterItemDrag || !!(ruleAfterItemDrag && ruleAfterItemDrag.elementCodeConnectableBefore.find((code) => code === currentElementDrag?.code)))) {
|
|
2048
|
+
return true;
|
|
2049
|
+
}
|
|
2050
|
+
return false;
|
|
2051
|
+
}
|
|
2052
|
+
get FlatElements() {
|
|
2053
|
+
this.flatElements = [];
|
|
2054
|
+
LibsUiDiagramDrawCanvasUtil.buildFlatElement(this.Elements, this.flatElements);
|
|
2055
|
+
return this.flatElements;
|
|
2056
|
+
}
|
|
2057
|
+
checkExistNameElement(name, flatElements) {
|
|
2058
|
+
if (!name || !name.trim()) {
|
|
2059
|
+
return false;
|
|
2060
|
+
}
|
|
2061
|
+
return flatElements?.find((element) => element.name && element.name.trim() && element.name.trim().toLocaleLowerCase() === name.trim().toLocaleLowerCase()) ? true : false;
|
|
2062
|
+
}
|
|
2063
|
+
addElement(itemDrop, branchDrop, dataDrop, branchChoice) {
|
|
2064
|
+
if (!itemDrop || !dataDrop) {
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2067
|
+
const elementsFlat = this.FlatElements;
|
|
2068
|
+
if (dataDrop.code !== canvasConfigReadonly().TYPE_ELEMENT_EXIT) {
|
|
2069
|
+
let counter = 0;
|
|
2070
|
+
const elementsOfCodeDataDrop = this.FlatElements?.filter((element) => element.code === dataDrop.code);
|
|
2071
|
+
if (elementsOfCodeDataDrop && elementsOfCodeDataDrop.length) {
|
|
2072
|
+
counter = elementsOfCodeDataDrop.reduce((preEl, currentEl) => (Number(currentEl.specific_counter_current_code) > Number(preEl.specific_counter_current_code) ? currentEl : preEl))
|
|
2073
|
+
.specific_counter_current_code;
|
|
2074
|
+
}
|
|
2075
|
+
counter++;
|
|
2076
|
+
let name = `${dataDrop.name} ${counter}`;
|
|
2077
|
+
while (this.checkExistNameElement(name, elementsFlat)) {
|
|
2078
|
+
counter++;
|
|
2079
|
+
name = `${dataDrop.name} ${counter}`;
|
|
2080
|
+
}
|
|
2081
|
+
dataDrop.name = name;
|
|
2082
|
+
dataDrop.specific_counter_current_code = counter;
|
|
2083
|
+
}
|
|
2084
|
+
if (branchDrop) {
|
|
2085
|
+
return this.addElementToBranch(itemDrop, branchDrop, cloneDeep(dataDrop), branchChoice);
|
|
2086
|
+
}
|
|
2087
|
+
const elements = this.Elements;
|
|
2088
|
+
const nextCurrentItemDrop = elements?.find((item) => item.id === itemDrop.next_id);
|
|
2089
|
+
if (!nextCurrentItemDrop && dataDrop.code !== canvasConfigReadonly().TYPE_ELEMENT_EXIT) {
|
|
2090
|
+
return;
|
|
2091
|
+
}
|
|
2092
|
+
const indexItemDrop = elements?.findIndex((item) => item.id === itemDrop.id);
|
|
2093
|
+
const newElement = { ...cloneDeep(dataDrop), pre_id: itemDrop.id, next_id: dataDrop.code !== canvasConfigReadonly().TYPE_ELEMENT_EXIT ? nextCurrentItemDrop.id : '' };
|
|
2094
|
+
if (!dataDrop.id) {
|
|
2095
|
+
newElement.id = `${uuid()}`;
|
|
2096
|
+
}
|
|
2097
|
+
itemDrop.next_id = newElement.id;
|
|
2098
|
+
if (nextCurrentItemDrop) {
|
|
2099
|
+
nextCurrentItemDrop.pre_id = newElement.id;
|
|
2100
|
+
}
|
|
2101
|
+
elements?.splice(indexItemDrop + 1, 0, newElement);
|
|
2102
|
+
const flatElements = this.FlatElements;
|
|
2103
|
+
const afterElement = flatElements?.find((el) => el.pre_id === newElement.id);
|
|
2104
|
+
if (!afterElement) {
|
|
2105
|
+
return;
|
|
2106
|
+
}
|
|
2107
|
+
const beforeElement = flatElements?.filter((el) => el.id !== newElement.id && el.next_id === afterElement.id);
|
|
2108
|
+
beforeElement?.forEach((element) => {
|
|
2109
|
+
element.next_id = newElement.id;
|
|
2110
|
+
});
|
|
2111
|
+
if (dataDrop.element_type !== canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW) {
|
|
2112
|
+
return;
|
|
2113
|
+
}
|
|
2114
|
+
this.addElementExitWorkFlow(newElement, branchChoice);
|
|
2115
|
+
}
|
|
2116
|
+
addElementToBranch(itemDrop, branchDrop, dataDrop, numberBranch) {
|
|
2117
|
+
const elements = branchDrop.elements;
|
|
2118
|
+
let nextCurrentItemDrop = undefined;
|
|
2119
|
+
let indexItemDrop = -1;
|
|
2120
|
+
if (elements && elements.length) {
|
|
2121
|
+
indexItemDrop = elements.findIndex((item) => item.id === itemDrop.id);
|
|
2122
|
+
nextCurrentItemDrop = elements.find((item) => item.id === itemDrop.next_id);
|
|
2123
|
+
if (!nextCurrentItemDrop && indexItemDrop === -1) {
|
|
2124
|
+
nextCurrentItemDrop = elements[indexItemDrop + 1];
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
let nextId = (nextCurrentItemDrop && nextCurrentItemDrop.id) || itemDrop.next_id;
|
|
2128
|
+
if (dataDrop.code === canvasConfigReadonly().TYPE_ELEMENT_EXIT) {
|
|
2129
|
+
nextId = '';
|
|
2130
|
+
}
|
|
2131
|
+
const newElement = { ...dataDrop, pre_id: itemDrop.id, next_id: nextId };
|
|
2132
|
+
if (!dataDrop.id) {
|
|
2133
|
+
newElement.id = `${uuid()}`;
|
|
2134
|
+
}
|
|
2135
|
+
if (!itemDrop.branches || !itemDrop.branches.find((branch) => branch === branchDrop)) {
|
|
2136
|
+
itemDrop.next_id = newElement.id;
|
|
2137
|
+
}
|
|
2138
|
+
if (nextCurrentItemDrop) {
|
|
2139
|
+
nextCurrentItemDrop.pre_id = newElement.id;
|
|
2140
|
+
}
|
|
2141
|
+
elements?.splice(indexItemDrop + 1, 0, newElement);
|
|
2142
|
+
if (itemDrop && itemDrop.element_type === canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW) {
|
|
2143
|
+
this.updateNextIdOfChildElementInElementWorkflow(itemDrop, this.FlatElements?.find((item) => item.id === itemDrop.next_id));
|
|
2144
|
+
}
|
|
2145
|
+
if (dataDrop.code === canvasConfigReadonly().TYPE_ELEMENT_EXIT) {
|
|
2146
|
+
this.checkEndMoveElementWhenDropElementCodeExit(branchDrop);
|
|
2147
|
+
}
|
|
2148
|
+
if (dataDrop.element_type !== canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW) {
|
|
2149
|
+
return;
|
|
2150
|
+
}
|
|
2151
|
+
this.addElementExitWorkFlow(newElement, numberBranch);
|
|
2152
|
+
}
|
|
2153
|
+
updateNextIdOfChildElementInElementWorkflow(elementWorkflow, nextElement) {
|
|
2154
|
+
if (!elementWorkflow || elementWorkflow.element_type !== canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW || !nextElement) {
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
const flatElement = [];
|
|
2158
|
+
LibsUiDiagramDrawCanvasUtil.flatElementHasBranch(nextElement, flatElement);
|
|
2159
|
+
elementWorkflow.branches?.forEach((branch) => {
|
|
2160
|
+
if (!branch.elements || !branch.elements.length) {
|
|
2161
|
+
return;
|
|
2162
|
+
}
|
|
2163
|
+
const elements = branch.elements;
|
|
2164
|
+
elements.forEach((element, index) => {
|
|
2165
|
+
if (index !== elements.length - 1) {
|
|
2166
|
+
if (element.element_type === canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW && element.next_id) {
|
|
2167
|
+
return this.updateNextIdOfChildElementInElementWorkflow(element, elements[index + 1]);
|
|
2168
|
+
}
|
|
2169
|
+
return;
|
|
2170
|
+
}
|
|
2171
|
+
if (element.code === canvasConfigReadonly().TYPE_ELEMENT_EXIT || element.id === nextElement.id) {
|
|
2172
|
+
return;
|
|
2173
|
+
}
|
|
2174
|
+
if (element.pre_id === nextElement.id || flatElement.find((item) => item.id === element.id)) {
|
|
2175
|
+
// trường hợp gọi đệ quy NextElement là khối trước hoặc cha của các element hiện tại
|
|
2176
|
+
return;
|
|
2177
|
+
}
|
|
2178
|
+
if (element.element_type === canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW) {
|
|
2179
|
+
if (element.next_id) {
|
|
2180
|
+
element.next_id = nextElement.id;
|
|
2181
|
+
}
|
|
2182
|
+
return this.updateNextIdOfChildElementInElementWorkflow(element, nextElement);
|
|
2183
|
+
}
|
|
2184
|
+
if (elementWorkflow.next_id !== nextElement.id) {
|
|
2185
|
+
return;
|
|
2186
|
+
}
|
|
2187
|
+
element.next_id = nextElement.id;
|
|
2188
|
+
});
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
2191
|
+
checkEndMoveElementWhenDropElementCodeExit(currentBranch) {
|
|
2192
|
+
const flatElements = this.FlatElements;
|
|
2193
|
+
const elementHasCurrentBranch = flatElements?.find((el) => el.branches && !!el.branches.find((branch) => branch === currentBranch));
|
|
2194
|
+
if (!elementHasCurrentBranch) {
|
|
2195
|
+
return;
|
|
2196
|
+
}
|
|
2197
|
+
const nextElement = flatElements?.find((el) => el.id === elementHasCurrentBranch.next_id);
|
|
2198
|
+
if (!nextElement) {
|
|
2199
|
+
return;
|
|
2200
|
+
}
|
|
2201
|
+
const dataElementHasCurrentBranch = LibsUiDiagramDrawCanvasUtil.getDefaultStorageBranchEndCount();
|
|
2202
|
+
LibsUiDiagramDrawCanvasUtil.getBranchContainerAndCountElementHasNextIdToElement(elementHasCurrentBranch, nextElement.id, dataElementHasCurrentBranch, this.FlatElements);
|
|
2203
|
+
if (!dataElementHasCurrentBranch.branchEmpty && dataElementHasCurrentBranch.count === 0) {
|
|
2204
|
+
elementHasCurrentBranch.next_id = '';
|
|
2205
|
+
}
|
|
2206
|
+
const elementNextToId = flatElements?.find((el) => el.next_id === nextElement.id); // Khối cha
|
|
2207
|
+
if (!elementNextToId || elementNextToId.element_type !== canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW || !elementNextToId.next_id) {
|
|
2208
|
+
return;
|
|
2209
|
+
}
|
|
2210
|
+
let dataFistElement = dataElementHasCurrentBranch;
|
|
2211
|
+
if (elementHasCurrentBranch.id !== elementNextToId.id) {
|
|
2212
|
+
dataFistElement = LibsUiDiagramDrawCanvasUtil.getDefaultStorageBranchEndCount();
|
|
2213
|
+
LibsUiDiagramDrawCanvasUtil.getBranchContainerAndCountElementHasNextIdToElement(elementNextToId, nextElement.id, dataFistElement, this.FlatElements);
|
|
2214
|
+
}
|
|
2215
|
+
if (dataFistElement.count > 1 || !dataFistElement.branchContainer) {
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2218
|
+
const branchAddItem = dataFistElement.branchContainer;
|
|
2219
|
+
if (elementHasCurrentBranch.id === elementNextToId.id) {
|
|
2220
|
+
if (nextElement.code !== canvasConfigReadonly().TYPE_ELEMENT_EXIT) {
|
|
2221
|
+
this.moveElementsToBranch(nextElement, branchAddItem);
|
|
2222
|
+
const { element } = LibsUiDiagramDrawCanvasUtil.getElementHasCurrentBranchOrCurrentElement(undefined, elementHasCurrentBranch, this.FlatElements);
|
|
2223
|
+
const data = LibsUiDiagramDrawCanvasUtil.getDefaultStorageBranchEndCount();
|
|
2224
|
+
const elementCount = element && element.next_id ? element : elementHasCurrentBranch;
|
|
2225
|
+
// Fixbug: 15771
|
|
2226
|
+
LibsUiDiagramDrawCanvasUtil.getBranchContainerAndCountElementHasNextIdToElement(elementCount, elementCount.next_id, data, this.FlatElements);
|
|
2227
|
+
if (!data.count) {
|
|
2228
|
+
return (elementCount.next_id = '');
|
|
2229
|
+
}
|
|
2230
|
+
return;
|
|
2231
|
+
}
|
|
2232
|
+
elementHasCurrentBranch.next_id = '';
|
|
2233
|
+
const elementHasBranchesNotResetId = this.deleteElement(nextElement);
|
|
2234
|
+
if (!elementHasBranchesNotResetId.length) {
|
|
2235
|
+
return this.cloneElementAndAddElementEXitToBranch(elementHasCurrentBranch, branchAddItem, nextElement);
|
|
2236
|
+
}
|
|
2237
|
+
const { data, element } = elementHasBranchesNotResetId[0];
|
|
2238
|
+
const infoOfElement = LibsUiDiagramDrawCanvasUtil.getElementHasCurrentBranchOrCurrentElement(undefined, element, this.FlatElements);
|
|
2239
|
+
const parentOfElement = infoOfElement.element;
|
|
2240
|
+
let dataOfElementInfo = LibsUiDiagramDrawCanvasUtil.getDefaultStorageBranchEndCount();
|
|
2241
|
+
LibsUiDiagramDrawCanvasUtil.getBranchContainerAndCountElementHasNextIdToElement(parentOfElement, parentOfElement?.next_id, dataOfElementInfo, this.FlatElements);
|
|
2242
|
+
if (elementHasBranchesNotResetId.length === 1) {
|
|
2243
|
+
if (data.count > 1) {
|
|
2244
|
+
return this.cloneElementAndAddElementEXitToBranch(element, branchAddItem, nextElement);
|
|
2245
|
+
}
|
|
2246
|
+
if (dataFistElement.branchContainer !== data.branchContainer && dataFistElement.count === 1) {
|
|
2247
|
+
this.cloneElementAndAddElementEXitToBranch(elementHasCurrentBranch, branchAddItem, nextElement);
|
|
2248
|
+
dataOfElementInfo = LibsUiDiagramDrawCanvasUtil.getDefaultStorageBranchEndCount();
|
|
2249
|
+
LibsUiDiagramDrawCanvasUtil.getBranchContainerAndCountElementHasNextIdToElement(parentOfElement, parentOfElement?.next_id, dataOfElementInfo, this.FlatElements);
|
|
2250
|
+
}
|
|
2251
|
+
if (dataOfElementInfo.count > 1) {
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
// Fixbug: 15518
|
|
2255
|
+
if (data.branchContainer) {
|
|
2256
|
+
this.cloneElementAndAddElementEXitToBranch(element, data.branchContainer, nextElement);
|
|
2257
|
+
}
|
|
2258
|
+
LibsUiDiagramDrawCanvasUtil.getBranchContainerAndCountElementHasNextIdToElement(parentOfElement, parentOfElement?.next_id, LibsUiDiagramDrawCanvasUtil.getDefaultStorageBranchEndCount(), this.FlatElements);
|
|
2259
|
+
return;
|
|
2260
|
+
}
|
|
2261
|
+
this.cloneElementAndAddElementEXitToBranch(element, branchAddItem, nextElement);
|
|
2262
|
+
const flatElement = this.FlatElements;
|
|
2263
|
+
elementHasBranchesNotResetId.forEach((item) => {
|
|
2264
|
+
const { element } = item;
|
|
2265
|
+
if (element.next_id && flatElement?.find((item) => item.id === element.next_id)) {
|
|
2266
|
+
return;
|
|
2267
|
+
}
|
|
2268
|
+
const infoFromElement = LibsUiDiagramDrawCanvasUtil.getElementHasCurrentBranchOrCurrentElement(undefined, element, flatElement);
|
|
2269
|
+
if (infoFromElement.element) {
|
|
2270
|
+
element.next_id = infoFromElement.element.next_id;
|
|
2271
|
+
this.updateNextIdOfChildElementInElementWorkflow(element, flatElements?.find((item) => item.id === element.next_id)); // FixBug: 15729
|
|
2272
|
+
}
|
|
2273
|
+
});
|
|
2274
|
+
LibsUiDiagramDrawCanvasUtil.getBranchContainerAndCountElementHasNextIdToElement(parentOfElement, parentOfElement?.next_id, LibsUiDiagramDrawCanvasUtil.getDefaultStorageBranchEndCount(), flatElement);
|
|
2275
|
+
return;
|
|
2276
|
+
}
|
|
2277
|
+
if (nextElement.code !== canvasConfigReadonly().TYPE_ELEMENT_EXIT) {
|
|
2278
|
+
this.moveElementsToBranch(nextElement, branchAddItem);
|
|
2279
|
+
// kiểm tra khối cha chứa các element có branch xem có element nào count = 0 để reset next_id = '' của khối đó
|
|
2280
|
+
const dataOfElementNextToId = LibsUiDiagramDrawCanvasUtil.getDefaultStorageBranchEndCount();
|
|
2281
|
+
LibsUiDiagramDrawCanvasUtil.getBranchContainerAndCountElementHasNextIdToElement(elementNextToId, elementNextToId.next_id, dataOfElementNextToId, this.FlatElements);
|
|
2282
|
+
if (!dataOfElementNextToId.count) {
|
|
2283
|
+
elementNextToId.next_id = '';
|
|
2284
|
+
}
|
|
2285
|
+
return;
|
|
2286
|
+
}
|
|
2287
|
+
elementNextToId.next_id = '';
|
|
2288
|
+
elementHasCurrentBranch.next_id = '';
|
|
2289
|
+
const elementHasBranchesNotResetId = this.deleteElement(nextElement);
|
|
2290
|
+
if (dataElementHasCurrentBranch.branchContainer) {
|
|
2291
|
+
this.cloneElementAndAddElementEXitToBranch(elementHasCurrentBranch, dataElementHasCurrentBranch.branchContainer, nextElement);
|
|
2292
|
+
}
|
|
2293
|
+
if (branchAddItem && branchAddItem !== dataElementHasCurrentBranch.branchContainer) {
|
|
2294
|
+
this.cloneElementAndAddElementEXitToBranch(elementNextToId, branchAddItem, nextElement); // dòng này sẽ khởi tạo lại next_id của khối cha
|
|
2295
|
+
}
|
|
2296
|
+
if (!elementHasBranchesNotResetId.length) {
|
|
2297
|
+
return;
|
|
2298
|
+
}
|
|
2299
|
+
// kiểm tra khối cha chứa các element có branch xem có element nào count = 0 để reset next_id = '' của khối đó
|
|
2300
|
+
dataFistElement = LibsUiDiagramDrawCanvasUtil.getDefaultStorageBranchEndCount();
|
|
2301
|
+
LibsUiDiagramDrawCanvasUtil.getBranchContainerAndCountElementHasNextIdToElement(elementNextToId, nextElement.id, dataFistElement, this.FlatElements);
|
|
2302
|
+
if (!dataFistElement.count) {
|
|
2303
|
+
// nếu khối cha kiểm tra khối cha chứa các element có branch có count = 0 thì reset next_id = ''
|
|
2304
|
+
elementNextToId.next_id = '';
|
|
2305
|
+
}
|
|
2306
|
+
const newId = elementHasBranchesNotResetId[0].element.next_id;
|
|
2307
|
+
elementHasBranchesNotResetId.forEach((item) => (item.element.next_id = newId));
|
|
2308
|
+
}
|
|
2309
|
+
moveElementsToBranch(elementStart, branchContainer) {
|
|
2310
|
+
if (!elementStart || elementStart.code === canvasConfigReadonly().TYPE_ELEMENT_EXIT || !branchContainer || !branchContainer.elements) {
|
|
2311
|
+
return;
|
|
2312
|
+
}
|
|
2313
|
+
const result = this.findContainerHasElementStart(elementStart, this.Elements);
|
|
2314
|
+
if (!result) {
|
|
2315
|
+
return;
|
|
2316
|
+
}
|
|
2317
|
+
const { container, index } = result;
|
|
2318
|
+
const elementsHasMove = container.slice(Number(index));
|
|
2319
|
+
const elementsOfBranchContainer = branchContainer.elements;
|
|
2320
|
+
const lastElementOfBranchContainer = elementsOfBranchContainer[elementsOfBranchContainer.length - 1];
|
|
2321
|
+
elementsHasMove.forEach((element) => this.deleteElement(element, true));
|
|
2322
|
+
if (lastElementOfBranchContainer && lastElementOfBranchContainer.code !== canvasConfigReadonly().TYPE_ELEMENT_EXIT) {
|
|
2323
|
+
lastElementOfBranchContainer.next_id = elementStart.id;
|
|
2324
|
+
elementStart.pre_id = lastElementOfBranchContainer.id;
|
|
2325
|
+
}
|
|
2326
|
+
const lastElementMove = elementsHasMove[elementsHasMove.length - 1];
|
|
2327
|
+
let nextElement = elementStart;
|
|
2328
|
+
if (lastElementMove && lastElementMove.next_id) {
|
|
2329
|
+
nextElement = this.FlatElements?.find((item) => item.id === lastElementMove.next_id);
|
|
2330
|
+
}
|
|
2331
|
+
const preElementStart = container[Number(index) - 1];
|
|
2332
|
+
branchContainer.elements.push(...elementsHasMove);
|
|
2333
|
+
if (!preElementStart || preElementStart.element_type !== canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW) {
|
|
2334
|
+
return;
|
|
2335
|
+
}
|
|
2336
|
+
if (nextElement) {
|
|
2337
|
+
this.updateNextIdOfChildElementInElementWorkflow(preElementStart, nextElement);
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
findContainerHasElementStart(elementStart, containerCapacity) {
|
|
2341
|
+
if (!containerCapacity || !containerCapacity.length || !elementStart) {
|
|
2342
|
+
return undefined;
|
|
2343
|
+
}
|
|
2344
|
+
for (const index in containerCapacity) {
|
|
2345
|
+
const element = containerCapacity[index];
|
|
2346
|
+
if (element.id === elementStart.id) {
|
|
2347
|
+
return {
|
|
2348
|
+
elementStart: elementStart,
|
|
2349
|
+
index: index,
|
|
2350
|
+
container: containerCapacity,
|
|
2351
|
+
};
|
|
2352
|
+
}
|
|
2353
|
+
if (!element.branches || !element.branches.length) {
|
|
2354
|
+
continue;
|
|
2355
|
+
}
|
|
2356
|
+
const branches = element.branches;
|
|
2357
|
+
for (const branch of branches) {
|
|
2358
|
+
const result = this.findContainerHasElementStart(elementStart, branch.elements);
|
|
2359
|
+
if (result) {
|
|
2360
|
+
return result;
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
return undefined;
|
|
2365
|
+
}
|
|
2366
|
+
deleteElement(elementDelete, ignoreUpdatePreId) {
|
|
2367
|
+
if (!elementDelete || !elementDelete.id) {
|
|
2368
|
+
return [];
|
|
2369
|
+
}
|
|
2370
|
+
const elementHasBranchesNotResetId = new Array();
|
|
2371
|
+
const flatElements = this.FlatElements;
|
|
2372
|
+
const elementsHasNextIdToElementDelete = flatElements?.filter((el) => el.next_id === elementDelete.id);
|
|
2373
|
+
const elementHasPreIdToElementDelete = flatElements?.find((el) => el.pre_id === elementDelete.id && el.id === elementDelete.next_id);
|
|
2374
|
+
if (!ignoreUpdatePreId && elementHasPreIdToElementDelete) {
|
|
2375
|
+
elementHasPreIdToElementDelete.pre_id = elementDelete.pre_id;
|
|
2376
|
+
}
|
|
2377
|
+
elementsHasNextIdToElementDelete?.forEach((elementHasNextIdToElementDelete) => {
|
|
2378
|
+
if (elementHasPreIdToElementDelete) {
|
|
2379
|
+
elementHasNextIdToElementDelete.next_id = elementHasPreIdToElementDelete.id;
|
|
2380
|
+
return;
|
|
2381
|
+
}
|
|
2382
|
+
if (elementDelete.code !== canvasConfigReadonly().TYPE_ELEMENT_EXIT) {
|
|
2383
|
+
if (!elementHasPreIdToElementDelete && elementDelete.next_id) {
|
|
2384
|
+
// Fixbug: 15623
|
|
2385
|
+
elementHasNextIdToElementDelete.next_id = elementDelete.next_id;
|
|
2386
|
+
}
|
|
2387
|
+
return;
|
|
2388
|
+
}
|
|
2389
|
+
if (elementHasNextIdToElementDelete.branches && elementHasNextIdToElementDelete.branches.length) {
|
|
2390
|
+
const data = LibsUiDiagramDrawCanvasUtil.getDefaultStorageBranchEndCount();
|
|
2391
|
+
LibsUiDiagramDrawCanvasUtil.getBranchContainerAndCountElementHasNextIdToElement(elementHasNextIdToElementDelete, elementDelete.id, data, this.FlatElements);
|
|
2392
|
+
if (!data.count) {
|
|
2393
|
+
elementHasNextIdToElementDelete.next_id = '';
|
|
2394
|
+
return;
|
|
2395
|
+
}
|
|
2396
|
+
elementHasBranchesNotResetId.push({ element: elementHasNextIdToElementDelete, data });
|
|
2397
|
+
return;
|
|
2398
|
+
}
|
|
2399
|
+
elementHasNextIdToElementDelete.next_id = elementDelete.next_id;
|
|
2400
|
+
});
|
|
2401
|
+
LibsUiDiagramDrawCanvasUtil.findAndDeleteElementFromContainer(this.Elements, elementDelete);
|
|
2402
|
+
return elementHasBranchesNotResetId;
|
|
2403
|
+
}
|
|
2404
|
+
cloneElementAndAddElementEXitToBranch(elementHasCurrentBranch, branch, dataDrop) {
|
|
2405
|
+
if (!dataDrop || !elementHasCurrentBranch || !branch) {
|
|
2406
|
+
return;
|
|
2407
|
+
}
|
|
2408
|
+
let elementDrop = elementHasCurrentBranch;
|
|
2409
|
+
if (branch.elements && branch.elements.length) {
|
|
2410
|
+
elementDrop = branch.elements[branch.elements.length - 1];
|
|
2411
|
+
}
|
|
2412
|
+
return this.addElementToBranch(elementDrop, branch, cloneDeep(dataDrop));
|
|
2413
|
+
}
|
|
2414
|
+
addElementExitWorkFlow(newElement, numberBranch) {
|
|
2415
|
+
const exit = {
|
|
2416
|
+
element_type: canvasConfigReadonly().TYPE_ELEMENT_EXIT,
|
|
2417
|
+
code: canvasConfigReadonly().TYPE_ELEMENT_EXIT,
|
|
2418
|
+
name: this.translateService.instant('i18n_end_journey_new'),
|
|
2419
|
+
specific_width: canvasConfigReadonly().WIDTH_ELEMENT_DEFAULT,
|
|
2420
|
+
specific_height: canvasConfigReadonly().HEIGHT_ELEMENT_DEFAULT,
|
|
2421
|
+
specific_x: 0,
|
|
2422
|
+
specific_y: 0,
|
|
2423
|
+
};
|
|
2424
|
+
if (newElement.branches && newElement.branches.length) {
|
|
2425
|
+
newElement.branches.forEach((item, index) => {
|
|
2426
|
+
if (index === numberBranch) {
|
|
2427
|
+
return;
|
|
2428
|
+
}
|
|
2429
|
+
if (!newElement.branches) {
|
|
2430
|
+
return;
|
|
2431
|
+
}
|
|
2432
|
+
this.addElement(newElement, item, exit);
|
|
2433
|
+
});
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
addElementExitWhenDeleteDirectionLine(element, branch) {
|
|
2437
|
+
const exit = {
|
|
2438
|
+
element_type: canvasConfigReadonly().TYPE_ELEMENT_EXIT,
|
|
2439
|
+
code: canvasConfigReadonly().TYPE_ELEMENT_EXIT,
|
|
2440
|
+
name: this.translateService.instant('i18n_end_journey_new'),
|
|
2441
|
+
specific_width: canvasConfigReadonly().WIDTH_ELEMENT_DEFAULT,
|
|
2442
|
+
specific_height: canvasConfigReadonly().HEIGHT_ELEMENT_DEFAULT,
|
|
2443
|
+
specific_x: 0,
|
|
2444
|
+
specific_y: 0,
|
|
2445
|
+
};
|
|
2446
|
+
delete element.nodeOtherConfig;
|
|
2447
|
+
this.addElement(element, branch, exit);
|
|
2448
|
+
}
|
|
2449
|
+
/**
|
|
2450
|
+
* Khi một element bị xoá, hàm này sẽ tự động thêm exit cho tất cả các element phía trước đang trỏ tới element đó.
|
|
2451
|
+
*/
|
|
2452
|
+
addElementExitForAllElementPreviousWhenElementDelete(elementsDelete) {
|
|
2453
|
+
const flatElement = this.FlatElements;
|
|
2454
|
+
const flatElementsInElementDelete = [];
|
|
2455
|
+
LibsUiDiagramDrawCanvasUtil.buildFlatElement(elementsDelete, flatElementsInElementDelete);
|
|
2456
|
+
flatElementsInElementDelete.forEach((elementDel) => {
|
|
2457
|
+
if (elementDel.pre_other_id && elementDel.pre_other_id.length) {
|
|
2458
|
+
elementDel.pre_other_id.forEach((item) => {
|
|
2459
|
+
const element = flatElement?.find((el) => el.id === item);
|
|
2460
|
+
if (element?.element_type === canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW && element.branches?.length) {
|
|
2461
|
+
element.branches.forEach((branch) => {
|
|
2462
|
+
if (!branch.next_other_id || !branch.next_other_id.length) {
|
|
2463
|
+
return;
|
|
2464
|
+
}
|
|
2465
|
+
const indexEl = branch.next_other_id.findIndex((el) => el === elementDel.id);
|
|
2466
|
+
if (indexEl !== -1) {
|
|
2467
|
+
branch.next_other_id.splice(indexEl, 1);
|
|
2468
|
+
}
|
|
2469
|
+
if (!branch.next_other_id.length) {
|
|
2470
|
+
this.addElementExitWhenDeleteDirectionLine(element, branch);
|
|
2471
|
+
element.subCodeOfElement = undefined;
|
|
2472
|
+
element.attributeSvgD = undefined;
|
|
2473
|
+
element.position_end = undefined;
|
|
2474
|
+
branch.nodeOtherConfig = undefined;
|
|
2475
|
+
}
|
|
2476
|
+
});
|
|
2477
|
+
return;
|
|
2478
|
+
}
|
|
2479
|
+
if (!element || !element.next_other_id) {
|
|
2480
|
+
return;
|
|
2481
|
+
}
|
|
2482
|
+
const indexEl = element.next_other_id.findIndex((el) => el === elementDel.id);
|
|
2483
|
+
if (indexEl !== -1) {
|
|
2484
|
+
element.next_other_id.splice(indexEl, 1);
|
|
2485
|
+
}
|
|
2486
|
+
if (!element.next_other_id.length) {
|
|
2487
|
+
if (element.element_type === canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW) {
|
|
2488
|
+
const getConfigDefaultBranches = this.handlerFunction?.getConfigDefaultBranches ?? getDefaultConfigConstituentBranches;
|
|
2489
|
+
getConfigDefaultBranches(element.code ?? '', element);
|
|
2490
|
+
}
|
|
2491
|
+
const branch = LibsUiDiagramDrawCanvasUtil.getBranchOfElement(element.id ?? '', this.FlatElements, canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW);
|
|
2492
|
+
this.addElementExitWhenDeleteDirectionLine(element, branch);
|
|
2493
|
+
element.subCodeOfElement = undefined;
|
|
2494
|
+
element.attributeSvgD = undefined;
|
|
2495
|
+
element.position_end = undefined;
|
|
2496
|
+
}
|
|
2497
|
+
});
|
|
2498
|
+
return;
|
|
2499
|
+
}
|
|
2500
|
+
if (elementDel.next_other_id && elementDel.next_other_id.length) {
|
|
2501
|
+
elementDel.next_other_id.forEach((item) => {
|
|
2502
|
+
const element = flatElement?.find((el) => el.id === item);
|
|
2503
|
+
if (!element || !element.pre_other_id) {
|
|
2504
|
+
return;
|
|
2505
|
+
}
|
|
2506
|
+
const indexEl = element.pre_other_id.findIndex((el) => el === elementDel.id);
|
|
2507
|
+
if (indexEl !== -1) {
|
|
2508
|
+
element.pre_other_id.splice(indexEl, 1);
|
|
2509
|
+
}
|
|
2510
|
+
});
|
|
2511
|
+
}
|
|
2512
|
+
if (elementDel.element_type === canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW && elementDel.branches?.length) {
|
|
2513
|
+
elementDel.branches.forEach((branch) => {
|
|
2514
|
+
if (!branch.next_other_id || !branch.next_other_id.length) {
|
|
2515
|
+
return;
|
|
2516
|
+
}
|
|
2517
|
+
branch.next_other_id.forEach((item) => {
|
|
2518
|
+
const element = flatElement?.find((el) => el.id === item);
|
|
2519
|
+
if (!element || !element.pre_other_id) {
|
|
2520
|
+
return;
|
|
2521
|
+
}
|
|
2522
|
+
const indexEl = element.pre_other_id.findIndex((el) => el === elementDel.id);
|
|
2523
|
+
if (indexEl !== -1) {
|
|
2524
|
+
element.pre_other_id.splice(indexEl, 1);
|
|
2525
|
+
}
|
|
2526
|
+
});
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
});
|
|
2530
|
+
}
|
|
2531
|
+
async removeOrChangeElement(elementRemove, elementChange, branchChoose) {
|
|
2532
|
+
if (!elementRemove || elementRemove.code === canvasConfigReadonly().TYPE_ELEMENT_EXIT) {
|
|
2533
|
+
return;
|
|
2534
|
+
}
|
|
2535
|
+
const flatElements = this.FlatElements;
|
|
2536
|
+
const preElement = flatElements?.find((element) => element.id === elementRemove.pre_id);
|
|
2537
|
+
const infoOfElementRemove = LibsUiDiagramDrawCanvasUtil.getElementHasCurrentBranchOrCurrentElement(undefined, elementRemove, this.FlatElements);
|
|
2538
|
+
const exit = {
|
|
2539
|
+
element_type: canvasConfigReadonly().TYPE_ELEMENT_EXIT,
|
|
2540
|
+
code: canvasConfigReadonly().TYPE_ELEMENT_EXIT,
|
|
2541
|
+
name: this.translateService.instant('i18n_out_journey'),
|
|
2542
|
+
id: `${uuid()}`,
|
|
2543
|
+
specific_width: canvasConfigReadonly().WIDTH_ELEMENT_DEFAULT,
|
|
2544
|
+
specific_height: canvasConfigReadonly().HEIGHT_ELEMENT_DEFAULT,
|
|
2545
|
+
specific_x: 0,
|
|
2546
|
+
specific_y: 0,
|
|
2547
|
+
};
|
|
2548
|
+
const elementNextBeforeDelete = flatElements?.find((el) => el.pre_id === elementRemove.id && el.id === elementRemove.next_id);
|
|
2549
|
+
if (elementNextBeforeDelete && elementNextBeforeDelete.code === canvasConfigReadonly().TYPE_ELEMENT_EXIT) {
|
|
2550
|
+
if (preElement && preElement.element_type === canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW && infoOfElementRemove.currentBranch) {
|
|
2551
|
+
infoOfElementRemove.currentBranch.nodeOtherConfig = undefined;
|
|
2552
|
+
}
|
|
2553
|
+
if (preElement && preElement.element_type !== canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW) {
|
|
2554
|
+
preElement.nodeOtherConfig = undefined;
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
if (preElement && !elementNextBeforeDelete) {
|
|
2558
|
+
preElement.nodeOtherConfig = undefined;
|
|
2559
|
+
if (preElement && preElement.element_type === canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW && infoOfElementRemove.currentBranch) {
|
|
2560
|
+
infoOfElementRemove.currentBranch.nodeOtherConfig = undefined;
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
this.deleteElement(elementRemove);
|
|
2564
|
+
// xóa thẳng
|
|
2565
|
+
if (!elementChange) {
|
|
2566
|
+
if (!elementNextBeforeDelete) {
|
|
2567
|
+
this.addElement(preElement, infoOfElementRemove.currentBranch, exit);
|
|
2568
|
+
}
|
|
2569
|
+
return;
|
|
2570
|
+
}
|
|
2571
|
+
// thay đơn == đơn, thay đơn = nhánh
|
|
2572
|
+
if (elementRemove.element_type !== canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW) {
|
|
2573
|
+
if (!elementRemove.next_id && elementChange.element_type !== canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW) {
|
|
2574
|
+
// 48229
|
|
2575
|
+
this.addElement(preElement, infoOfElementRemove.currentBranch, exit);
|
|
2576
|
+
}
|
|
2577
|
+
this.addElement(preElement, infoOfElementRemove.currentBranch, elementChange, branchChoose);
|
|
2578
|
+
if (elementChange.element_type === canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW && elementNextBeforeDelete && elementNextBeforeDelete.code === canvasConfigReadonly().TYPE_ELEMENT_EXIT) {
|
|
2579
|
+
this.deleteElement(elementNextBeforeDelete);
|
|
2580
|
+
}
|
|
2581
|
+
return;
|
|
2582
|
+
}
|
|
2583
|
+
// thay nhánh = đơn
|
|
2584
|
+
if (elementChange.element_type !== canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW) {
|
|
2585
|
+
this.addElement(preElement, infoOfElementRemove.currentBranch, exit);
|
|
2586
|
+
this.addElement(preElement, infoOfElementRemove.currentBranch, elementChange, branchChoose);
|
|
2587
|
+
return;
|
|
2588
|
+
}
|
|
2589
|
+
// thay nhánh = nhánh
|
|
2590
|
+
this.addElement(preElement, infoOfElementRemove.currentBranch, exit);
|
|
2591
|
+
this.addElement(preElement, infoOfElementRemove.currentBranch, elementChange, branchChoose);
|
|
2592
|
+
this.deleteElement(exit);
|
|
2593
|
+
}
|
|
2594
|
+
async deleteChangeElementAndInsertBranchKeep(elementRemove, elementChange, branchKeepElement, branchChoice) {
|
|
2595
|
+
const flatElements = this.FlatElements;
|
|
2596
|
+
const preElement = flatElements?.find((element) => element.id === elementRemove.pre_id);
|
|
2597
|
+
const infoOfElementRemove = LibsUiDiagramDrawCanvasUtil.getElementHasCurrentBranchOrCurrentElement(undefined, elementRemove, this.FlatElements);
|
|
2598
|
+
this.addBranchKeepToFlow(branchKeepElement, preElement, infoOfElementRemove.currentBranch);
|
|
2599
|
+
this.deleteElement(elementRemove);
|
|
2600
|
+
if (elementChange) {
|
|
2601
|
+
this.addElement(preElement, infoOfElementRemove.currentBranch, elementChange, branchChoice);
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
addBranchKeepToFlow(branchKeepElement, itemDrop, branchDrop) {
|
|
2605
|
+
if (!branchKeepElement) {
|
|
2606
|
+
return;
|
|
2607
|
+
}
|
|
2608
|
+
const elements = branchKeepElement.elements;
|
|
2609
|
+
const nextCurrentItemDrop = elements[0];
|
|
2610
|
+
if (nextCurrentItemDrop) {
|
|
2611
|
+
if (itemDrop.element_type !== canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW) {
|
|
2612
|
+
itemDrop.next_id = nextCurrentItemDrop && nextCurrentItemDrop.id;
|
|
2613
|
+
}
|
|
2614
|
+
nextCurrentItemDrop.pre_id = itemDrop.id;
|
|
2615
|
+
}
|
|
2616
|
+
if (branchDrop) {
|
|
2617
|
+
branchDrop.elements.push(...elements);
|
|
2618
|
+
return;
|
|
2619
|
+
}
|
|
2620
|
+
this.Elements.push(...elements);
|
|
2621
|
+
}
|
|
2622
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiDiagramDrawCanvasService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2623
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiDiagramDrawCanvasService, providedIn: 'root' });
|
|
2624
|
+
}
|
|
2625
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiDiagramDrawCanvasService, decorators: [{
|
|
2626
|
+
type: Injectable,
|
|
2627
|
+
args: [{ providedIn: 'root' }]
|
|
2628
|
+
}] });
|
|
2629
|
+
|
|
2630
|
+
const buildElementDirectionTo = (element, flatElements, checkRule, branch) => {
|
|
2631
|
+
let elementsFlat = cloneDeep(flatElements) ?? [];
|
|
2632
|
+
if (element.element_type === canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW) {
|
|
2633
|
+
if (!branch) {
|
|
2634
|
+
return undefined;
|
|
2635
|
+
}
|
|
2636
|
+
getAllItemPreOfElement(element, elementsFlat);
|
|
2637
|
+
getAllItemNextOfBranch(branch, elementsFlat);
|
|
2638
|
+
elementsFlat = elementsFlat.filter((item) => item.id !== element.id && item.code !== canvasConfigReadonly().TYPE_ELEMENT_EXIT);
|
|
2639
|
+
if (element.pre_other_id?.length) {
|
|
2640
|
+
elementsFlat = elementsFlat.filter((item) => !element.pre_other_id?.includes(item.id ?? ''));
|
|
2641
|
+
}
|
|
2642
|
+
elementsFlat = elementsFlat.filter((item) => {
|
|
2643
|
+
return checkRule(element, item);
|
|
2644
|
+
});
|
|
2645
|
+
return elementsFlat;
|
|
2646
|
+
}
|
|
2647
|
+
getAllItemPreOfElement(element, elementsFlat);
|
|
2648
|
+
getAllItemNextOfElement(element, elementsFlat);
|
|
2649
|
+
elementsFlat = elementsFlat.filter((item) => item.id !== element.id && item.code !== canvasConfigReadonly().TYPE_ELEMENT_EXIT);
|
|
2650
|
+
if (element.pre_other_id?.length) {
|
|
2651
|
+
elementsFlat = elementsFlat.filter((item) => !element.pre_other_id?.includes(item.id ?? ''));
|
|
2652
|
+
}
|
|
2653
|
+
elementsFlat = elementsFlat.filter((item) => {
|
|
2654
|
+
return checkRule(element, item);
|
|
2655
|
+
});
|
|
2656
|
+
return elementsFlat;
|
|
2657
|
+
};
|
|
2658
|
+
const getAllItemPreOfElement = (element, flatElements) => {
|
|
2659
|
+
const pre_id = [];
|
|
2660
|
+
if (element.pre_id) {
|
|
2661
|
+
pre_id.push(element.pre_id);
|
|
2662
|
+
}
|
|
2663
|
+
if (element.pre_other_id) {
|
|
2664
|
+
pre_id.push(...element.pre_other_id);
|
|
2665
|
+
}
|
|
2666
|
+
if (!pre_id.length) {
|
|
2667
|
+
return;
|
|
2668
|
+
}
|
|
2669
|
+
pre_id.forEach((preId) => {
|
|
2670
|
+
const elementPre = flatElements.find((item) => item.id === preId);
|
|
2671
|
+
if (!elementPre) {
|
|
2672
|
+
return;
|
|
2673
|
+
}
|
|
2674
|
+
const itemIndex = flatElements.findIndex((item) => item.id === preId);
|
|
2675
|
+
flatElements.splice(itemIndex, 1);
|
|
2676
|
+
getAllItemPreOfElement(elementPre, flatElements);
|
|
2677
|
+
});
|
|
2678
|
+
};
|
|
2679
|
+
const getAllItemNextOfElement = (element, flatElements) => {
|
|
2680
|
+
if (!element.next_id) {
|
|
2681
|
+
if (!element.branches || !element.branches.length) {
|
|
2682
|
+
return;
|
|
2683
|
+
}
|
|
2684
|
+
const flatElementInElementCurrent = [];
|
|
2685
|
+
buildFlatElement([element], flatElementInElementCurrent);
|
|
2686
|
+
flatElementInElementCurrent.forEach((el) => {
|
|
2687
|
+
const itemIndex = flatElements.findIndex((item) => item.id === el.id);
|
|
2688
|
+
if (itemIndex === -1) {
|
|
2689
|
+
return;
|
|
2690
|
+
}
|
|
2691
|
+
flatElements.splice(itemIndex, 1);
|
|
2692
|
+
});
|
|
2693
|
+
return;
|
|
2694
|
+
}
|
|
2695
|
+
const elementNextIndex = flatElements.findIndex((item) => item.id === element.next_id);
|
|
2696
|
+
if (elementNextIndex === -1) {
|
|
2697
|
+
return;
|
|
2698
|
+
}
|
|
2699
|
+
const nextElement = flatElements[elementNextIndex];
|
|
2700
|
+
flatElements.splice(elementNextIndex, 1);
|
|
2701
|
+
getAllItemNextOfElement(nextElement, flatElements);
|
|
2702
|
+
};
|
|
2703
|
+
const getAllItemNextOfBranch = (branch, flatElements) => {
|
|
2704
|
+
const flatElementInElementCurrent = [];
|
|
2705
|
+
buildFlatElement(branch.elements, flatElementInElementCurrent);
|
|
2706
|
+
flatElementInElementCurrent.forEach((el) => {
|
|
2707
|
+
const itemIndex = flatElements.findIndex((item) => item.id === el.id);
|
|
2708
|
+
if (itemIndex === -1) {
|
|
2709
|
+
return;
|
|
2710
|
+
}
|
|
2711
|
+
flatElements.splice(itemIndex, 1);
|
|
2712
|
+
});
|
|
2713
|
+
};
|
|
2714
|
+
const collectNextChainElements = (element, flatContainerElement, listElementDelete) => {
|
|
2715
|
+
const elementNext = flatContainerElement.find((item) => item.id === element.next_id);
|
|
2716
|
+
if (!elementNext) {
|
|
2717
|
+
return;
|
|
2718
|
+
}
|
|
2719
|
+
const flatElementInElementCurrent = [];
|
|
2720
|
+
buildFlatElement([elementNext], flatElementInElementCurrent);
|
|
2721
|
+
flatElementInElementCurrent.forEach((el) => {
|
|
2722
|
+
const itemInNext = flatContainerElement.find((item) => item.id === el.id);
|
|
2723
|
+
if (itemInNext) {
|
|
2724
|
+
listElementDelete.push(itemInNext);
|
|
2725
|
+
}
|
|
2726
|
+
});
|
|
2727
|
+
collectNextChainElements(elementNext, flatContainerElement, listElementDelete);
|
|
2728
|
+
};
|
|
2729
|
+
|
|
2730
|
+
class LibsUiDiagramDrawDirectionService {
|
|
2731
|
+
elementDirection;
|
|
2732
|
+
branchDirection;
|
|
2733
|
+
elementViewDirectionId;
|
|
2734
|
+
onViewDirection = new Subject();
|
|
2735
|
+
canvasService = inject(LibsUiDiagramDrawCanvasService);
|
|
2736
|
+
set ElementDirection(element) {
|
|
2737
|
+
this.elementDirection = element;
|
|
2738
|
+
}
|
|
2739
|
+
get ElementDirection() {
|
|
2740
|
+
return this.elementDirection;
|
|
2741
|
+
}
|
|
2742
|
+
set BranchDirection(branch) {
|
|
2743
|
+
this.branchDirection = branch;
|
|
2744
|
+
}
|
|
2745
|
+
get BranchDirection() {
|
|
2746
|
+
return this.branchDirection;
|
|
2747
|
+
}
|
|
2748
|
+
set ElementViewDirectionId(id) {
|
|
2749
|
+
this.elementViewDirectionId = id;
|
|
2750
|
+
}
|
|
2751
|
+
get ElementViewDirectionId() {
|
|
2752
|
+
return this.elementViewDirectionId;
|
|
2753
|
+
}
|
|
2754
|
+
get OnViewDirection() {
|
|
2755
|
+
return this.onViewDirection;
|
|
2756
|
+
}
|
|
2757
|
+
checkRuleDirectionToElement(element, elementConnectTo) {
|
|
2758
|
+
const ruleAfterItemDrag = this.canvasService.RuleConnectableElements.find((item) => elementConnectTo && item.elementCode === elementConnectTo.code);
|
|
2759
|
+
if (!ruleAfterItemDrag) {
|
|
2760
|
+
return true;
|
|
2761
|
+
}
|
|
2762
|
+
if (ruleAfterItemDrag.elementCodeConnectableBefore.find((code) => code === element?.code)) {
|
|
2763
|
+
return true;
|
|
2764
|
+
}
|
|
2765
|
+
return false;
|
|
2766
|
+
}
|
|
2767
|
+
buildElementConnectTo(element, flatElements, branch, checkRule) {
|
|
2768
|
+
const checkRuleDirectionToElement = checkRule ?? this.checkRuleDirectionToElement.bind(this);
|
|
2769
|
+
return buildElementDirectionTo(element, flatElements, checkRuleDirectionToElement, branch);
|
|
2770
|
+
}
|
|
2771
|
+
/**Xóa các element nằm phía sau khi nhánh được điều hướng */
|
|
2772
|
+
deleteElementsBehindBranchConnectTo(branch) {
|
|
2773
|
+
const flatElements = this.canvasService.FlatElements ?? [];
|
|
2774
|
+
const flatElementInElementCurrent = [];
|
|
2775
|
+
const listElementDelete = [];
|
|
2776
|
+
buildFlatElement(branch.elements, flatElementInElementCurrent);
|
|
2777
|
+
listElementDelete.push(...flatElementInElementCurrent);
|
|
2778
|
+
this.deleteLineConnectFromElement(listElementDelete, flatElements);
|
|
2779
|
+
listElementDelete.forEach((elementDelete) => {
|
|
2780
|
+
findAndDeleteElementFromContainer(this.canvasService.Elements, elementDelete);
|
|
2781
|
+
});
|
|
2782
|
+
}
|
|
2783
|
+
/**xóa line đến và xóa line đi ở toàn bộ các khối và nhánh bị xóa */
|
|
2784
|
+
deleteLineConnectFromElement(elements, flatElements) {
|
|
2785
|
+
elements.forEach((item) => {
|
|
2786
|
+
if (item.branches && item.branches.length) {
|
|
2787
|
+
item.branches.forEach((branch) => {
|
|
2788
|
+
if (branch.next_other_id && branch.next_other_id.length) {
|
|
2789
|
+
this.deletePreOtherId(item.id ?? '', branch.next_other_id, flatElements);
|
|
2790
|
+
}
|
|
2791
|
+
});
|
|
2792
|
+
}
|
|
2793
|
+
if (item.next_other_id && item.next_other_id.length) {
|
|
2794
|
+
this.deletePreOtherId(item.id ?? '', item.next_other_id, flatElements);
|
|
2795
|
+
}
|
|
2796
|
+
if (item.pre_other_id && item.pre_other_id.length) {
|
|
2797
|
+
// nhánh chỉ lưu id next đi thôi chứ không lưu được pre => chỉ cần xóa pre trong khối.
|
|
2798
|
+
this.deleteNextOtherId(item.id ?? '', item.pre_other_id, flatElements);
|
|
2799
|
+
}
|
|
2800
|
+
});
|
|
2801
|
+
}
|
|
2802
|
+
/** Xóa pre_other_id từ khác khối mà khối bị xóa nối đến BlockDelete => Other*/
|
|
2803
|
+
deletePreOtherId(itemId, nextOtherId, flatElements) {
|
|
2804
|
+
nextOtherId.forEach((nextToId) => {
|
|
2805
|
+
const nextElement = flatElements.find((item) => item.id === nextToId);
|
|
2806
|
+
if (!nextElement || !nextElement.pre_other_id || !nextElement.pre_other_id.length) {
|
|
2807
|
+
return;
|
|
2808
|
+
}
|
|
2809
|
+
const indexIdPre = nextElement.pre_other_id.findIndex((id) => id === itemId);
|
|
2810
|
+
if (indexIdPre !== -1) {
|
|
2811
|
+
nextElement.pre_other_id.splice(indexIdPre, 1);
|
|
2812
|
+
}
|
|
2813
|
+
});
|
|
2814
|
+
}
|
|
2815
|
+
/** Xóa next_other_id từ khác khối nối đến khối bị xóa Other => BlockDelete và kiểm tra + thêm khối thoát cho những khối nối tới BlockDelete*/
|
|
2816
|
+
deleteNextOtherId(itemId, preOtherId, flatElements) {
|
|
2817
|
+
preOtherId.forEach((nextToId) => {
|
|
2818
|
+
const preElement = flatElements.find((item) => item.id === nextToId);
|
|
2819
|
+
if (!preElement) {
|
|
2820
|
+
return;
|
|
2821
|
+
}
|
|
2822
|
+
if (preElement.element_type === canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW) {
|
|
2823
|
+
preElement.branches?.forEach((branch) => {
|
|
2824
|
+
if (!branch.next_other_id || !branch.next_other_id.length) {
|
|
2825
|
+
return;
|
|
2826
|
+
}
|
|
2827
|
+
const indexIdNextInBranch = branch.next_other_id.findIndex((id) => id === itemId);
|
|
2828
|
+
if (indexIdNextInBranch !== -1) {
|
|
2829
|
+
branch.next_other_id.splice(indexIdNextInBranch, 1);
|
|
2830
|
+
}
|
|
2831
|
+
if (!branch.next_other_id || !branch.next_other_id.length) {
|
|
2832
|
+
branch.nodeOtherConfig = undefined;
|
|
2833
|
+
this.canvasService.addElementExitWhenDeleteDirectionLine(preElement, branch);
|
|
2834
|
+
}
|
|
2835
|
+
});
|
|
2836
|
+
return;
|
|
2837
|
+
}
|
|
2838
|
+
if (!preElement.next_other_id || !preElement.next_other_id.length) {
|
|
2839
|
+
return;
|
|
2840
|
+
}
|
|
2841
|
+
const indexIdNext = preElement.next_other_id.findIndex((id) => id === itemId);
|
|
2842
|
+
if (indexIdNext !== -1) {
|
|
2843
|
+
preElement.next_other_id.splice(indexIdNext, 1);
|
|
2844
|
+
}
|
|
2845
|
+
if (!preElement.next_other_id || !preElement.next_other_id.length) {
|
|
2846
|
+
// thêm khối thoát vì không còn khối nào connect tới nó
|
|
2847
|
+
if (preElement.element_type === canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW) {
|
|
2848
|
+
const getConfigDefaultBranches = this.canvasService.HandlerFunction?.getConfigDefaultBranches ?? getDefaultConfigConstituentBranches;
|
|
2849
|
+
getConfigDefaultBranches(preElement.code ?? '', preElement);
|
|
2850
|
+
}
|
|
2851
|
+
const branch = LibsUiDiagramDrawCanvasUtil.getBranchOfElement(preElement.id ?? '', flatElements, canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW);
|
|
2852
|
+
this.canvasService.addElementExitWhenDeleteDirectionLine(preElement, branch);
|
|
2853
|
+
preElement.subCodeOfElement = undefined;
|
|
2854
|
+
preElement.attributeSvgD = undefined;
|
|
2855
|
+
preElement.position_end = undefined;
|
|
2856
|
+
}
|
|
2857
|
+
});
|
|
2858
|
+
}
|
|
2859
|
+
addBranchDirection(targetId, element, branch, flatElements) {
|
|
2860
|
+
if (!branch.next_other_id) {
|
|
2861
|
+
branch.next_other_id = [];
|
|
2862
|
+
}
|
|
2863
|
+
branch.next_other_id.push(targetId);
|
|
2864
|
+
const elementTo = flatElements.find((item) => item.id === targetId);
|
|
2865
|
+
if (elementTo) {
|
|
2866
|
+
if (!elementTo.pre_other_id) {
|
|
2867
|
+
elementTo.pre_other_id = [];
|
|
2868
|
+
}
|
|
2869
|
+
elementTo.pre_other_id.push(element.id ?? '');
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
addElementDirection(targetId, element, flatElements) {
|
|
2873
|
+
if (!element.next_other_id) {
|
|
2874
|
+
element.next_other_id = [];
|
|
2875
|
+
}
|
|
2876
|
+
element.next_other_id.push(targetId);
|
|
2877
|
+
const elementTo = flatElements.find((item) => item.id === targetId);
|
|
2878
|
+
if (elementTo) {
|
|
2879
|
+
if (!elementTo.pre_other_id) {
|
|
2880
|
+
elementTo.pre_other_id = [];
|
|
2881
|
+
}
|
|
2882
|
+
elementTo.pre_other_id.push(element.id ?? '');
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
deleteElementsBehindElementConnectTo(element, flatElements) {
|
|
2886
|
+
const listElementDelete = [];
|
|
2887
|
+
let flatElementInElementCurrent = [];
|
|
2888
|
+
if (element.branches && element.branches.length) {
|
|
2889
|
+
buildFlatElement([element], flatElementInElementCurrent);
|
|
2890
|
+
flatElementInElementCurrent = flatElementInElementCurrent.filter((item) => item.id !== element.id);
|
|
2891
|
+
}
|
|
2892
|
+
delete element.branches;
|
|
2893
|
+
listElementDelete.push(...flatElementInElementCurrent);
|
|
2894
|
+
collectNextChainElements(element, flatElements, listElementDelete);
|
|
2895
|
+
this.deleteLineConnectFromElement(listElementDelete, flatElements);
|
|
2896
|
+
listElementDelete.forEach((elementDelete) => {
|
|
2897
|
+
this.findAndDeleteElementFromContainer(this.canvasService.Elements, elementDelete);
|
|
2898
|
+
});
|
|
2899
|
+
}
|
|
2900
|
+
findAndDeleteElementFromContainer(containerElement, elementDelete) {
|
|
2901
|
+
for (const index in containerElement) {
|
|
2902
|
+
const elementOfIndex = containerElement[index];
|
|
2903
|
+
if (elementOfIndex.id === elementDelete.id) {
|
|
2904
|
+
containerElement.splice(+index, 1);
|
|
2905
|
+
return true;
|
|
2906
|
+
}
|
|
2907
|
+
if (!elementOfIndex.branches || !elementOfIndex.branches.length) {
|
|
2908
|
+
continue;
|
|
2909
|
+
}
|
|
2910
|
+
for (const branch of elementOfIndex.branches) {
|
|
2911
|
+
if (!branch.elements || !branch.elements.length) {
|
|
2912
|
+
continue;
|
|
2913
|
+
}
|
|
2914
|
+
if (this.findAndDeleteElementFromContainer(branch.elements, elementDelete)) {
|
|
2915
|
+
return true;
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
return false;
|
|
2920
|
+
}
|
|
2921
|
+
editDirection(flatElements, targetId, element, branch) {
|
|
2922
|
+
if (element.element_type === canvasConfigReadonly().TYPE_ELEMENT_WORKFLOW && branch) {
|
|
2923
|
+
this.editDirectionWithBranch(flatElements, branch, targetId, element);
|
|
2924
|
+
return;
|
|
2925
|
+
}
|
|
2926
|
+
const idOldNextTo = element.next_other_id?.[0];
|
|
2927
|
+
const elementOldTo = flatElements?.find((item) => item.id === idOldNextTo);
|
|
2928
|
+
// xóa line cũ
|
|
2929
|
+
if (elementOldTo && elementOldTo.pre_other_id) {
|
|
2930
|
+
const itemIndex = elementOldTo.pre_other_id?.findIndex((id) => id === element.id);
|
|
2931
|
+
if (itemIndex !== -1) {
|
|
2932
|
+
elementOldTo.pre_other_id?.splice(itemIndex, 1);
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
element.attributeSvgD = undefined;
|
|
2936
|
+
element.position_end = undefined;
|
|
2937
|
+
// update line mới
|
|
2938
|
+
const elementNewTo = flatElements?.find((item) => item.id === targetId);
|
|
2939
|
+
if (elementNewTo) {
|
|
2940
|
+
if (!elementNewTo.pre_other_id) {
|
|
2941
|
+
elementNewTo.pre_other_id = [];
|
|
2942
|
+
}
|
|
2943
|
+
elementNewTo.pre_other_id.push(element.id ?? '');
|
|
2944
|
+
}
|
|
2945
|
+
element.next_other_id = [];
|
|
2946
|
+
element.next_other_id.push(targetId);
|
|
2947
|
+
}
|
|
2948
|
+
editDirectionWithBranch(flatElements, branch, targetId, element) {
|
|
2949
|
+
const idOldNextTo = branch.next_other_id?.[0];
|
|
2950
|
+
const elementOldTo = flatElements?.find((item) => item.id === idOldNextTo);
|
|
2951
|
+
// xóa line cũ
|
|
2952
|
+
if (elementOldTo && elementOldTo.pre_other_id) {
|
|
2953
|
+
const itemIndex = elementOldTo.pre_other_id?.findIndex((id) => id === element.id);
|
|
2954
|
+
if (itemIndex !== -1) {
|
|
2955
|
+
elementOldTo.pre_other_id?.splice(itemIndex, 1);
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
// update line mới
|
|
2959
|
+
const elementNewTo = flatElements?.find((item) => item.id === targetId);
|
|
2960
|
+
if (elementNewTo) {
|
|
2961
|
+
if (!elementNewTo.pre_other_id) {
|
|
2962
|
+
elementNewTo.pre_other_id = [];
|
|
2963
|
+
}
|
|
2964
|
+
elementNewTo.pre_other_id.push(element.id ?? '');
|
|
2965
|
+
}
|
|
2966
|
+
branch.next_other_id = [];
|
|
2967
|
+
branch.next_other_id.push(targetId);
|
|
2968
|
+
}
|
|
2969
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiDiagramDrawDirectionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2970
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiDiagramDrawDirectionService, providedIn: 'root' });
|
|
2971
|
+
}
|
|
2972
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiDiagramDrawDirectionService, decorators: [{
|
|
2973
|
+
type: Injectable,
|
|
2974
|
+
args: [{ providedIn: 'root' }]
|
|
2975
|
+
}] });
|
|
2976
|
+
|
|
2977
|
+
/**
|
|
2978
|
+
* Generated bundle index. Do not edit.
|
|
2979
|
+
*/
|
|
2980
|
+
|
|
2981
|
+
export { LibsUiDiagramDrawCanvasService, LibsUiDiagramDrawDirectionService, LibsUiDiagramDrawService, canvasConfigReadonly, storeDataDefault };
|
|
2982
|
+
//# sourceMappingURL=libs-ui-services-diagram-draw.mjs.map
|