pict-section-flow 0.0.3 → 0.0.5
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pict-section-flow",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Pict Section Flow Diagram",
|
|
5
5
|
"main": "source/Pict-Section-Flow.js",
|
|
6
6
|
"scripts": {
|
|
@@ -11,12 +11,14 @@
|
|
|
11
11
|
"author": "steven velozo <steven@velozo.com>",
|
|
12
12
|
"license": "MIT",
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"
|
|
14
|
+
"fable-serviceproviderbase": "^3.0.19",
|
|
15
15
|
"pict-provider": "^1.0.12",
|
|
16
|
-
"
|
|
16
|
+
"pict-section-form": "^1.0.194",
|
|
17
|
+
"pict-view": "^1.0.67"
|
|
17
18
|
},
|
|
18
19
|
"devDependencies": {
|
|
19
|
-
"pict": "^1.0.
|
|
20
|
-
"
|
|
20
|
+
"pict": "^1.0.357",
|
|
21
|
+
"pict-router": "^1.0.6",
|
|
22
|
+
"quackage": "^1.0.64"
|
|
21
23
|
}
|
|
22
24
|
}
|
package/source/PictFlowCard.js
CHANGED
|
@@ -105,6 +105,7 @@ class PictFlowCard extends libFableServiceProviderBase
|
|
|
105
105
|
this.cardPortLabelsOnHover = (typeof tmpOptions.PortLabelsOnHover === 'boolean') ? tmpOptions.PortLabelsOnHover : false;
|
|
106
106
|
this.cardPortLabelsVertical = (typeof tmpOptions.PortLabelsVertical === 'boolean') ? tmpOptions.PortLabelsVertical : false;
|
|
107
107
|
this.cardPortLabelPadding = (typeof tmpOptions.PortLabelPadding === 'boolean') ? tmpOptions.PortLabelPadding : false;
|
|
108
|
+
this.cardPortLabelsOutside = (typeof tmpOptions.PortLabelsOutside === 'boolean') ? tmpOptions.PortLabelsOutside : false;
|
|
108
109
|
this.cardLabelsInFront = (typeof tmpOptions.LabelsInFront === 'boolean') ? tmpOptions.LabelsInFront : true;
|
|
109
110
|
}
|
|
110
111
|
|
|
@@ -184,6 +185,7 @@ class PictFlowCard extends libFableServiceProviderBase
|
|
|
184
185
|
tmpResult.PortLabelsOnHover = this.cardPortLabelsOnHover;
|
|
185
186
|
tmpResult.PortLabelsVertical = this.cardPortLabelsVertical;
|
|
186
187
|
tmpResult.PortLabelPadding = this.cardPortLabelPadding;
|
|
188
|
+
tmpResult.PortLabelsOutside = this.cardPortLabelsOutside;
|
|
187
189
|
tmpResult.LabelsInFront = this.cardLabelsInFront;
|
|
188
190
|
|
|
189
191
|
// Include properties panel config if defined
|
|
@@ -251,5 +253,6 @@ module.exports.default_configuration =
|
|
|
251
253
|
PortLabelsOnHover: false,
|
|
252
254
|
PortLabelsVertical: false,
|
|
253
255
|
PortLabelPadding: false,
|
|
256
|
+
PortLabelsOutside: false,
|
|
254
257
|
LabelsInFront: true
|
|
255
258
|
};
|
|
@@ -67,7 +67,7 @@ class FlowCardPropertiesPanelTemplate extends libPictFlowCardPropertiesPanel
|
|
|
67
67
|
if (!tmpTemplateHash) return;
|
|
68
68
|
|
|
69
69
|
let tmpRecord = this._NodeData;
|
|
70
|
-
let tmpHTML = this.fable.
|
|
70
|
+
let tmpHTML = this.fable.parseTemplateByHash(tmpTemplateHash, tmpRecord, null, [tmpRecord]);
|
|
71
71
|
this._ContentContainer.innerHTML = tmpHTML;
|
|
72
72
|
}
|
|
73
73
|
}
|
|
@@ -6,6 +6,16 @@ const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
|
6
6
|
* Shared geometry utilities for the flow diagram.
|
|
7
7
|
* Provides direction vectors and edge center calculations used by
|
|
8
8
|
* connections, tethers, and other flow components.
|
|
9
|
+
*
|
|
10
|
+
* Port Side values (12 positions):
|
|
11
|
+
*
|
|
12
|
+
* Top edge: 'top-left' 'top' 'top-right'
|
|
13
|
+
* Left edge: 'left-top' 'left' 'left-bottom'
|
|
14
|
+
* Right edge: 'right-top' 'right' 'right-bottom'
|
|
15
|
+
* Bottom edge: 'bottom-left' 'bottom' 'bottom-right'
|
|
16
|
+
*
|
|
17
|
+
* The old 4-value sides ('left', 'right', 'top', 'bottom') map to
|
|
18
|
+
* the middle position on each edge for backward compatibility.
|
|
9
19
|
*/
|
|
10
20
|
class PictProviderFlowGeometry extends libFableServiceProviderBase
|
|
11
21
|
{
|
|
@@ -16,15 +26,55 @@ class PictProviderFlowGeometry extends libFableServiceProviderBase
|
|
|
16
26
|
this.serviceType = 'PictProviderFlowGeometry';
|
|
17
27
|
}
|
|
18
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Extract the edge name from a Side value.
|
|
31
|
+
*
|
|
32
|
+
* Maps all 12 positions (and the 4 legacy values) back to
|
|
33
|
+
* the edge they sit on: 'left', 'right', 'top', or 'bottom'.
|
|
34
|
+
*
|
|
35
|
+
* @param {string} pSide - Any valid Side value
|
|
36
|
+
* @returns {string} The edge: 'left', 'right', 'top', or 'bottom'
|
|
37
|
+
*/
|
|
38
|
+
getEdgeFromSide(pSide)
|
|
39
|
+
{
|
|
40
|
+
switch (pSide)
|
|
41
|
+
{
|
|
42
|
+
case 'left-top':
|
|
43
|
+
case 'left':
|
|
44
|
+
case 'left-bottom':
|
|
45
|
+
return 'left';
|
|
46
|
+
|
|
47
|
+
case 'right-top':
|
|
48
|
+
case 'right':
|
|
49
|
+
case 'right-bottom':
|
|
50
|
+
return 'right';
|
|
51
|
+
|
|
52
|
+
case 'top-left':
|
|
53
|
+
case 'top':
|
|
54
|
+
case 'top-right':
|
|
55
|
+
return 'top';
|
|
56
|
+
|
|
57
|
+
case 'bottom-left':
|
|
58
|
+
case 'bottom':
|
|
59
|
+
case 'bottom-right':
|
|
60
|
+
return 'bottom';
|
|
61
|
+
|
|
62
|
+
default:
|
|
63
|
+
return 'right';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
19
67
|
/**
|
|
20
68
|
* Get the outward unit direction vector for a given side.
|
|
21
69
|
*
|
|
22
|
-
*
|
|
70
|
+
* All positions on the same edge share the same direction vector.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} pSide - Any valid Side value (12 positions or 4 legacy)
|
|
23
73
|
* @returns {{dx: number, dy: number}}
|
|
24
74
|
*/
|
|
25
75
|
sideDirection(pSide)
|
|
26
76
|
{
|
|
27
|
-
switch (pSide)
|
|
77
|
+
switch (this.getEdgeFromSide(pSide))
|
|
28
78
|
{
|
|
29
79
|
case 'left': return { dx: -1, dy: 0 };
|
|
30
80
|
case 'right': return { dx: 1, dy: 0 };
|
|
@@ -63,12 +113,17 @@ class PictProviderFlowGeometry extends libFableServiceProviderBase
|
|
|
63
113
|
/**
|
|
64
114
|
* Calculate a port's local position relative to node origin.
|
|
65
115
|
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
116
|
+
* Supports 12 positions (3 zones per edge). For left/right edges,
|
|
117
|
+
* the body area below the title bar is divided into three vertical zones
|
|
118
|
+
* (start/middle/end). For top/bottom edges, the full width is divided
|
|
119
|
+
* into three horizontal zones.
|
|
68
120
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
121
|
+
* Multiple ports sharing the same Side value distribute evenly within
|
|
122
|
+
* their zone.
|
|
123
|
+
*
|
|
124
|
+
* @param {string} pSide - Side value (any of 12 positions or 4 legacy)
|
|
125
|
+
* @param {number} pIndex - Index of this port within its Side group
|
|
126
|
+
* @param {number} pTotal - Total ports with this Side value
|
|
72
127
|
* @param {number} pWidth - Node width
|
|
73
128
|
* @param {number} pHeight - Node height
|
|
74
129
|
* @param {number} pTitleBarHeight - Height of the node title bar
|
|
@@ -76,30 +131,66 @@ class PictProviderFlowGeometry extends libFableServiceProviderBase
|
|
|
76
131
|
*/
|
|
77
132
|
getPortLocalPosition(pSide, pIndex, pTotal, pWidth, pHeight, pTitleBarHeight)
|
|
78
133
|
{
|
|
79
|
-
let
|
|
134
|
+
let tmpEdge = this.getEdgeFromSide(pSide);
|
|
135
|
+
let tmpZone = this._getZoneFromSide(pSide);
|
|
136
|
+
|
|
137
|
+
if (tmpEdge === 'left' || tmpEdge === 'right')
|
|
138
|
+
{
|
|
139
|
+
let tmpX = (tmpEdge === 'left') ? 0 : pWidth;
|
|
140
|
+
let tmpBodyHeight = pHeight - pTitleBarHeight;
|
|
141
|
+
let tmpZoneStart = pTitleBarHeight + tmpBodyHeight * tmpZone.start;
|
|
142
|
+
let tmpZoneHeight = tmpBodyHeight * (tmpZone.end - tmpZone.start);
|
|
143
|
+
let tmpSpacing = tmpZoneHeight / (pTotal + 1);
|
|
144
|
+
let tmpY = tmpZoneStart + tmpSpacing * (pIndex + 1);
|
|
145
|
+
return { x: tmpX, y: tmpY };
|
|
146
|
+
}
|
|
80
147
|
|
|
148
|
+
// top or bottom
|
|
149
|
+
let tmpY = (tmpEdge === 'top') ? 0 : pHeight;
|
|
150
|
+
let tmpZoneStart = pWidth * tmpZone.start;
|
|
151
|
+
let tmpZoneWidth = pWidth * (tmpZone.end - tmpZone.start);
|
|
152
|
+
let tmpSpacing = tmpZoneWidth / (pTotal + 1);
|
|
153
|
+
let tmpX = tmpZoneStart + tmpSpacing * (pIndex + 1);
|
|
154
|
+
return { x: tmpX, y: tmpY };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get the zone fraction (start, end) for a Side value.
|
|
159
|
+
*
|
|
160
|
+
* Each edge is divided into three zones of equal size:
|
|
161
|
+
* start: 0.0 — 0.333
|
|
162
|
+
* middle: 0.333 — 0.667
|
|
163
|
+
* end: 0.667 — 1.0
|
|
164
|
+
*
|
|
165
|
+
* @param {string} pSide
|
|
166
|
+
* @returns {{start: number, end: number}}
|
|
167
|
+
*/
|
|
168
|
+
_getZoneFromSide(pSide)
|
|
169
|
+
{
|
|
81
170
|
switch (pSide)
|
|
82
171
|
{
|
|
83
|
-
|
|
84
|
-
{
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
case 'right':
|
|
90
|
-
{
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
case 'top':
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
172
|
+
// Left edge: top, middle, bottom
|
|
173
|
+
case 'left-top': return { start: 0.0, end: 0.333 };
|
|
174
|
+
case 'left': return { start: 0.333, end: 0.667 };
|
|
175
|
+
case 'left-bottom': return { start: 0.667, end: 1.0 };
|
|
176
|
+
|
|
177
|
+
// Right edge: top, middle, bottom
|
|
178
|
+
case 'right-top': return { start: 0.0, end: 0.333 };
|
|
179
|
+
case 'right': return { start: 0.333, end: 0.667 };
|
|
180
|
+
case 'right-bottom': return { start: 0.667, end: 1.0 };
|
|
181
|
+
|
|
182
|
+
// Top edge: left, middle, right
|
|
183
|
+
case 'top-left': return { start: 0.0, end: 0.333 };
|
|
184
|
+
case 'top': return { start: 0.333, end: 0.667 };
|
|
185
|
+
case 'top-right': return { start: 0.667, end: 1.0 };
|
|
186
|
+
|
|
187
|
+
// Bottom edge: left, middle, right
|
|
188
|
+
case 'bottom-left': return { start: 0.0, end: 0.333 };
|
|
189
|
+
case 'bottom': return { start: 0.333, end: 0.667 };
|
|
190
|
+
case 'bottom-right': return { start: 0.667, end: 1.0 };
|
|
191
|
+
|
|
192
|
+
// Fallback: full range (legacy behavior)
|
|
193
|
+
default: return { start: 0.0, end: 1.0 };
|
|
103
194
|
}
|
|
104
195
|
}
|
|
105
196
|
}
|
|
@@ -292,22 +292,28 @@ class PictViewFlowNode extends libPictView
|
|
|
292
292
|
|
|
293
293
|
let tmpPortLabelsVertical = (pNodeTypeConfig && pNodeTypeConfig.PortLabelsVertical);
|
|
294
294
|
let tmpPortLabelPadding = (pNodeTypeConfig && pNodeTypeConfig.PortLabelPadding);
|
|
295
|
+
let tmpPortLabelsOutside = (pNodeTypeConfig && pNodeTypeConfig.PortLabelsOutside);
|
|
296
|
+
let tmpGeometryProvider = this._FlowView._GeometryProvider;
|
|
295
297
|
|
|
296
|
-
// Group ports by
|
|
297
|
-
let tmpPortsBySide = {
|
|
298
|
+
// Group ports by their Side value (supports all 12 positions)
|
|
299
|
+
let tmpPortsBySide = {};
|
|
298
300
|
for (let i = 0; i < pNodeData.Ports.length; i++)
|
|
299
301
|
{
|
|
300
302
|
let tmpPort = pNodeData.Ports[i];
|
|
301
303
|
let tmpSide = tmpPort.Side || (tmpPort.Direction === 'input' ? 'left' : 'right');
|
|
302
|
-
if (tmpPortsBySide[tmpSide])
|
|
304
|
+
if (!tmpPortsBySide[tmpSide])
|
|
303
305
|
{
|
|
304
|
-
tmpPortsBySide[tmpSide]
|
|
306
|
+
tmpPortsBySide[tmpSide] = [];
|
|
305
307
|
}
|
|
308
|
+
tmpPortsBySide[tmpSide].push(tmpPort);
|
|
306
309
|
}
|
|
307
310
|
|
|
308
311
|
for (let tmpSide in tmpPortsBySide)
|
|
309
312
|
{
|
|
310
313
|
let tmpPorts = tmpPortsBySide[tmpSide];
|
|
314
|
+
// Determine the edge for label positioning
|
|
315
|
+
let tmpEdge = tmpGeometryProvider ? tmpGeometryProvider.getEdgeFromSide(tmpSide) : tmpSide;
|
|
316
|
+
|
|
311
317
|
for (let i = 0; i < tmpPorts.length; i++)
|
|
312
318
|
{
|
|
313
319
|
let tmpPort = tmpPorts[i];
|
|
@@ -334,7 +340,8 @@ class PictViewFlowNode extends libPictView
|
|
|
334
340
|
}
|
|
335
341
|
pGroup.appendChild(tmpCircle);
|
|
336
342
|
|
|
337
|
-
// Port label
|
|
343
|
+
// Port label — use the edge for alignment (all positions on the
|
|
344
|
+
// same edge share the same label direction)
|
|
338
345
|
if (tmpPort.Label)
|
|
339
346
|
{
|
|
340
347
|
let tmpLabel = this._FlowView._SVGHelperProvider.createSVGElement('text');
|
|
@@ -345,62 +352,64 @@ class PictViewFlowNode extends libPictView
|
|
|
345
352
|
let tmpLabelOffset = 12;
|
|
346
353
|
let tmpPaddingExtra = tmpPortLabelPadding ? 8 : 0;
|
|
347
354
|
|
|
348
|
-
|
|
355
|
+
// When PortLabelsOutside is true, labels render outside the node
|
|
356
|
+
// boundary (away from center) instead of inside (toward center).
|
|
357
|
+
// The direction multiplier flips the offset direction per edge.
|
|
358
|
+
let tmpOutsideFlip = tmpPortLabelsOutside ? -1 : 1;
|
|
359
|
+
|
|
360
|
+
if (tmpPortLabelsVertical)
|
|
349
361
|
{
|
|
350
|
-
|
|
351
|
-
// After rotation, text-anchor controls vertical centering, so 'middle'
|
|
352
|
-
// ensures the label is centered next to its port circle.
|
|
353
|
-
switch (tmpSide)
|
|
362
|
+
switch (tmpEdge)
|
|
354
363
|
{
|
|
355
364
|
case 'left':
|
|
356
|
-
tmpLabel.setAttribute('x', String(tmpPosition.x + tmpLabelOffset + tmpPaddingExtra));
|
|
365
|
+
tmpLabel.setAttribute('x', String(tmpPosition.x + (tmpLabelOffset + tmpPaddingExtra) * tmpOutsideFlip));
|
|
357
366
|
tmpLabel.setAttribute('y', String(tmpPosition.y));
|
|
358
367
|
tmpLabel.setAttribute('text-anchor', 'middle');
|
|
359
|
-
tmpLabel.setAttribute('transform', `rotate(-90, ${tmpPosition.x + tmpLabelOffset + tmpPaddingExtra}, ${tmpPosition.y})`);
|
|
368
|
+
tmpLabel.setAttribute('transform', `rotate(-90, ${tmpPosition.x + (tmpLabelOffset + tmpPaddingExtra) * tmpOutsideFlip}, ${tmpPosition.y})`);
|
|
360
369
|
break;
|
|
361
370
|
case 'right':
|
|
362
|
-
tmpLabel.setAttribute('x', String(tmpPosition.x - tmpLabelOffset
|
|
371
|
+
tmpLabel.setAttribute('x', String(tmpPosition.x - (tmpLabelOffset + tmpPaddingExtra) * tmpOutsideFlip));
|
|
363
372
|
tmpLabel.setAttribute('y', String(tmpPosition.y));
|
|
364
373
|
tmpLabel.setAttribute('text-anchor', 'middle');
|
|
365
|
-
tmpLabel.setAttribute('transform', `rotate(-90, ${tmpPosition.x - tmpLabelOffset
|
|
374
|
+
tmpLabel.setAttribute('transform', `rotate(-90, ${tmpPosition.x - (tmpLabelOffset + tmpPaddingExtra) * tmpOutsideFlip}, ${tmpPosition.y})`);
|
|
366
375
|
break;
|
|
367
376
|
case 'top':
|
|
368
377
|
tmpLabel.setAttribute('x', String(tmpPosition.x));
|
|
369
|
-
tmpLabel.setAttribute('y', String(tmpPosition.y + tmpLabelOffset + tmpPaddingExtra));
|
|
378
|
+
tmpLabel.setAttribute('y', String(tmpPosition.y + (tmpLabelOffset + tmpPaddingExtra) * tmpOutsideFlip));
|
|
370
379
|
tmpLabel.setAttribute('text-anchor', 'middle');
|
|
371
|
-
tmpLabel.setAttribute('transform', `rotate(-90, ${tmpPosition.x}, ${tmpPosition.y + tmpLabelOffset + tmpPaddingExtra})`);
|
|
380
|
+
tmpLabel.setAttribute('transform', `rotate(-90, ${tmpPosition.x}, ${tmpPosition.y + (tmpLabelOffset + tmpPaddingExtra) * tmpOutsideFlip})`);
|
|
372
381
|
break;
|
|
373
382
|
case 'bottom':
|
|
374
383
|
tmpLabel.setAttribute('x', String(tmpPosition.x));
|
|
375
|
-
tmpLabel.setAttribute('y', String(tmpPosition.y - tmpLabelOffset
|
|
384
|
+
tmpLabel.setAttribute('y', String(tmpPosition.y - (tmpLabelOffset + tmpPaddingExtra) * tmpOutsideFlip));
|
|
376
385
|
tmpLabel.setAttribute('text-anchor', 'middle');
|
|
377
|
-
tmpLabel.setAttribute('transform', `rotate(-90, ${tmpPosition.x}, ${tmpPosition.y - tmpLabelOffset
|
|
386
|
+
tmpLabel.setAttribute('transform', `rotate(-90, ${tmpPosition.x}, ${tmpPosition.y - (tmpLabelOffset + tmpPaddingExtra) * tmpOutsideFlip})`);
|
|
378
387
|
break;
|
|
379
388
|
}
|
|
380
389
|
}
|
|
381
390
|
else
|
|
382
391
|
{
|
|
383
392
|
// Horizontal labels (default)
|
|
384
|
-
switch (
|
|
393
|
+
switch (tmpEdge)
|
|
385
394
|
{
|
|
386
395
|
case 'left':
|
|
387
|
-
tmpLabel.setAttribute('x', String(tmpPosition.x + tmpLabelOffset + tmpPaddingExtra));
|
|
396
|
+
tmpLabel.setAttribute('x', String(tmpPosition.x + (tmpLabelOffset + tmpPaddingExtra) * tmpOutsideFlip));
|
|
388
397
|
tmpLabel.setAttribute('y', String(tmpPosition.y));
|
|
389
|
-
tmpLabel.setAttribute('text-anchor', 'start');
|
|
398
|
+
tmpLabel.setAttribute('text-anchor', tmpPortLabelsOutside ? 'end' : 'start');
|
|
390
399
|
break;
|
|
391
400
|
case 'right':
|
|
392
|
-
tmpLabel.setAttribute('x', String(tmpPosition.x - tmpLabelOffset
|
|
401
|
+
tmpLabel.setAttribute('x', String(tmpPosition.x - (tmpLabelOffset + tmpPaddingExtra) * tmpOutsideFlip));
|
|
393
402
|
tmpLabel.setAttribute('y', String(tmpPosition.y));
|
|
394
|
-
tmpLabel.setAttribute('text-anchor', 'end');
|
|
403
|
+
tmpLabel.setAttribute('text-anchor', tmpPortLabelsOutside ? 'start' : 'end');
|
|
395
404
|
break;
|
|
396
405
|
case 'top':
|
|
397
406
|
tmpLabel.setAttribute('x', String(tmpPosition.x));
|
|
398
|
-
tmpLabel.setAttribute('y', String(tmpPosition.y + tmpLabelOffset + tmpPaddingExtra));
|
|
407
|
+
tmpLabel.setAttribute('y', String(tmpPosition.y + (tmpLabelOffset + tmpPaddingExtra) * tmpOutsideFlip));
|
|
399
408
|
tmpLabel.setAttribute('text-anchor', 'middle');
|
|
400
409
|
break;
|
|
401
410
|
case 'bottom':
|
|
402
411
|
tmpLabel.setAttribute('x', String(tmpPosition.x));
|
|
403
|
-
tmpLabel.setAttribute('y', String(tmpPosition.y - tmpLabelOffset
|
|
412
|
+
tmpLabel.setAttribute('y', String(tmpPosition.y - (tmpLabelOffset + tmpPaddingExtra) * tmpOutsideFlip));
|
|
404
413
|
tmpLabel.setAttribute('text-anchor', 'middle');
|
|
405
414
|
break;
|
|
406
415
|
}
|