w-flow-vue 1.0.0

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.
Files changed (144) hide show
  1. package/.editorconfig +9 -0
  2. package/.eslintignore +3 -0
  3. package/.eslintrc.js +55 -0
  4. package/.jsdoc +25 -0
  5. package/AGENT.md +223 -0
  6. package/LICENSE +21 -0
  7. package/README.md +37 -0
  8. package/SECURITY.md +5 -0
  9. package/babel.config.js +16 -0
  10. package/dist/w-flow-vue.umd.js +15 -0
  11. package/dist/w-flow-vue.umd.js.map +1 -0
  12. package/docs/components_WFlowVue.vue.html +1214 -0
  13. package/docs/examples/app.html +62 -0
  14. package/docs/examples/app.umd.js +20 -0
  15. package/docs/examples/app.umd.js.map +1 -0
  16. package/docs/examples/ex-AppBasic.html +440 -0
  17. package/docs/examples/ex-AppConnectivity.html +131 -0
  18. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  19. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  20. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  21. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  22. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  23. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  24. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  25. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  26. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  27. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
  28. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  29. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  30. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  31. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  32. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
  33. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  34. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  35. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  36. package/docs/global.html +1919 -0
  37. package/docs/index.html +84 -0
  38. package/docs/js_defaults.mjs.html +105 -0
  39. package/docs/js_edge-path.mjs.html +237 -0
  40. package/docs/js_geometry.mjs.html +298 -0
  41. package/docs/js_graph.mjs.html +103 -0
  42. package/docs/js_step-routing.mjs.html +346 -0
  43. package/docs/module-WFlowVue.html +2790 -0
  44. package/docs/scripts/collapse.js +39 -0
  45. package/docs/scripts/commonNav.js +28 -0
  46. package/docs/scripts/linenumber.js +25 -0
  47. package/docs/scripts/nav.js +12 -0
  48. package/docs/scripts/polyfill.js +4 -0
  49. package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
  50. package/docs/scripts/prettify/lang-css.js +2 -0
  51. package/docs/scripts/prettify/prettify.js +28 -0
  52. package/docs/scripts/search.js +99 -0
  53. package/docs/styles/jsdoc.css +776 -0
  54. package/docs/styles/prettify.css +80 -0
  55. package/jest.config.js +20 -0
  56. package/package.json +80 -0
  57. package/public/index.html +38 -0
  58. package/script.txt +22 -0
  59. package/src/App.vue +326 -0
  60. package/src/AppBasic.vue +125 -0
  61. package/src/AppConnectivity.vue +186 -0
  62. package/src/components/WFlowVue.vue +1142 -0
  63. package/src/components/canvas/BackgroundLayer.vue +78 -0
  64. package/src/components/canvas/FlowCanvas.vue +64 -0
  65. package/src/components/canvas/SelectionBox.vue +36 -0
  66. package/src/components/canvas/ViewportTransform.vue +35 -0
  67. package/src/components/edges/ConnectionLine.vue +65 -0
  68. package/src/components/edges/EdgeMarkerDefs.vue +76 -0
  69. package/src/components/edges/EdgeRenderer.vue +120 -0
  70. package/src/components/edges/EdgeWrapper.vue +379 -0
  71. package/src/components/nodes/DefaultNode.vue +276 -0
  72. package/src/components/nodes/Handle.vue +101 -0
  73. package/src/components/nodes/InputNode.vue +47 -0
  74. package/src/components/nodes/NodeBody.vue +103 -0
  75. package/src/components/nodes/NodeFace.vue +128 -0
  76. package/src/components/nodes/NodeRenderer.vue +95 -0
  77. package/src/components/nodes/NodeWrapper.vue +475 -0
  78. package/src/components/nodes/OutputNode.vue +47 -0
  79. package/src/components/ui/ConnSettingsForm.vue +158 -0
  80. package/src/components/ui/Controls.vue +83 -0
  81. package/src/components/ui/NodeSettingsForm.vue +185 -0
  82. package/src/js/defaults.mjs +33 -0
  83. package/src/js/edge-path.mjs +165 -0
  84. package/src/js/geometry.mjs +226 -0
  85. package/src/js/graph.mjs +31 -0
  86. package/src/js/step-routing.mjs +274 -0
  87. package/src/main.js +22 -0
  88. package/test/WFlowVue-features.test.mjs +760 -0
  89. package/test/WFlowVue.test.mjs +421 -0
  90. package/test/components-canvas.test.mjs +102 -0
  91. package/test/components-edge.test.mjs +147 -0
  92. package/test/components-node.test.mjs +174 -0
  93. package/test/components-ui.test.mjs +69 -0
  94. package/test/defaults.test.mjs +86 -0
  95. package/test/edge-path.test.mjs +102 -0
  96. package/test/generate-routing-snapshots.mjs +77 -0
  97. package/test/generate-visual-baselines.mjs +206 -0
  98. package/test/geometry.test.mjs +236 -0
  99. package/test/graph.test.mjs +72 -0
  100. package/test/jsons/routing-snapshots.json +24994 -0
  101. package/test/pics/_check2.png +0 -0
  102. package/test/pics/_check3.png +0 -0
  103. package/test/pics/_check4.png +0 -0
  104. package/test/pics/_check5.png +0 -0
  105. package/test/pics/_v1.png +0 -0
  106. package/test/pics/_v2.png +0 -0
  107. package/test/pics/_v3.png +0 -0
  108. package/test/pics/_v4.png +0 -0
  109. package/test/pics/_v5.png +0 -0
  110. package/test/pics/_v6.png +0 -0
  111. package/test/pics/_v7.png +0 -0
  112. package/test/pics/vb-edge-hovered.png +0 -0
  113. package/test/pics/vb-edges-normal.png +0 -0
  114. package/test/pics/vb-locked-edge-hovered.png +0 -0
  115. package/test/pics/vb-locked-node-hovered.png +0 -0
  116. package/test/pics/vb-locked-node-selected.png +0 -0
  117. package/test/pics/vb-locked-overview.png +0 -0
  118. package/test/pics/vb-node-1.png +0 -0
  119. package/test/pics/vb-node-10.png +0 -0
  120. package/test/pics/vb-node-11.png +0 -0
  121. package/test/pics/vb-node-12.png +0 -0
  122. package/test/pics/vb-node-2.png +0 -0
  123. package/test/pics/vb-node-3.png +0 -0
  124. package/test/pics/vb-node-4.png +0 -0
  125. package/test/pics/vb-node-5.png +0 -0
  126. package/test/pics/vb-node-6.png +0 -0
  127. package/test/pics/vb-node-7.png +0 -0
  128. package/test/pics/vb-node-8.png +0 -0
  129. package/test/pics/vb-node-9.png +0 -0
  130. package/test/pics/vb-node-hovered.png +0 -0
  131. package/test/pics/vb-node-selected.png +0 -0
  132. package/test/pics/vb-overview.png +0 -0
  133. package/test/step-routing-connectivity.test.mjs +78 -0
  134. package/test/step-routing.test.mjs +88 -0
  135. package/test/visual-regression.test.mjs +274 -0
  136. package/toolg/addVersion.mjs +4 -0
  137. package/toolg/cleanFolder.mjs +4 -0
  138. package/toolg/gDistApp.mjs +34 -0
  139. package/toolg/gDistRollupComps.mjs +22 -0
  140. package/toolg/gDocExams.mjs +47 -0
  141. package/toolg/gExtractHtml.mjs +179 -0
  142. package/toolg/modifyReadme.mjs +4 -0
  143. package/vue.config.js +9 -0
  144. package/vue2/344/271/213foreignObject/345/205/247/346/270/262/346/237/223/345/225/217/351/241/214/350/210/207/344/277/256/346/255/243.md +151 -0
@@ -0,0 +1,125 @@
1
+ <template>
2
+ <div>
3
+
4
+ <div class="bkh">
5
+ <div style="font-size:1.5rem;">size</div>
6
+ <a href="//yuda-lyu.github.io/w-flow-vue/examples/ex-AppBasic.html" target="_blank" class="item-link">example</a>
7
+ <a href="//github.com/yuda-lyu/w-flow-vue/blob/master/docs/examples/ex-AppBasic.html" target="_blank" class="item-link">code</a>
8
+ </div>
9
+
10
+ <div class="bkp">
11
+
12
+ <div v-if="!loading">
13
+ <button style="margin:0px 3px 3px 0px;" @click="setWidth(800)">set width to 800</button>
14
+ <button style="margin:0px 3px 3px 0px;" @click="setWidth(300)">set width to 300</button>
15
+ <button style="margin:0px 3px 3px 0px;" @click="setHeight(600)">set height to 600</button>
16
+ <button style="margin:0px 3px 3px 0px;" @click="setHeight(300)">set height to 300</button>
17
+ </div>
18
+
19
+ <div style="display:flex; padding-bottom:40px; overflow-x:auto;">
20
+
21
+ <div style="position:relative; border:1px solid #ddd;">
22
+ <WFlowVue
23
+ :opt="opt"
24
+ @init="loading=false"
25
+ ></WFlowVue>
26
+ </div>
27
+
28
+ <div style="padding:0px 20px;">
29
+
30
+ <div :style="`border:1px solid #ddd; width:590px; min-width:590px; height:${opt.height}px; overflow-y:auto;`">
31
+ <div style="padding-left:5px;">
32
+ <div id="optjson" style="font-size:10pt;"></div>
33
+ </div>
34
+ </div>
35
+
36
+ </div>
37
+
38
+ </div>
39
+
40
+ </div>
41
+
42
+ </div>
43
+ </template>
44
+
45
+ <script>
46
+ import WFlowVue from './components/WFlowVue.vue'
47
+ import jv from 'w-jsonview-tree'
48
+
49
+ export default {
50
+ components: {
51
+ WFlowVue,
52
+ },
53
+ data: function() {
54
+ return {
55
+ 'loading': true,
56
+ 'opt': {
57
+ width: 800,
58
+ height: 600,
59
+ nodes: [
60
+ { id: '1', type: 'input', name: '資料來源', description: '從外部 API 取得原始資料', position: { x: 260, y: 0 }, edgeColor: '#0041d0', width: 100, height: 40 },
61
+ { id: '2', type: 'basic', name: '資料驗證', description: '檢查資料格式與完整性', position: { x: 100, y: 120 }, width: 100, height: 40 },
62
+ { id: '3', type: 'basic', name: '資料轉換', description: '將原始資料轉換為標準格式', position: { x: 400, y: 120 }, width: 100, height: 40 },
63
+ { id: '8', type: 'basic', name: '條件判斷', description: '根據條件分流處理', position: { x: 100, y: 220 }, shape: 'diamond', width: 120, height: 80 },
64
+ { id: '4', type: 'basic', name: '商業邏輯', description: '執行核心商業規則與計算', position: { x: 80, y: 380 }, shape: 'ellipse', width: 140, height: 60 },
65
+ { id: '5', type: 'basic', name: '資料聚合', description: '合併多個來源的處理結果', position: { x: 280, y: 500 }, edgeColor: '#ffa500', edgeWidth: 2, width: 100, height: 40 },
66
+ { id: '6', type: 'output', name: '輸出結果', description: '將最終結果寫入資料庫', position: { x: 160, y: 620 }, width: 100, height: 40 },
67
+ { id: '7', type: 'output', name: '通知服務', description: '發送處理完成通知給相關人員', position: { x: 400, y: 620 }, edgeColor: '#ff69b4', width: 100, height: 40 },
68
+ { id: '9', type: 'basic', name: '過濾器', description: '過濾不符合條件的資料', position: { x: 620, y: 80 }, shape: 'triangle', width: 100, height: 80 },
69
+ { id: '10', type: 'basic', name: '路由分配', description: '根據規則分配至不同處理管道', position: { x: 620, y: 220 }, shape: 'triangle-right', width: 100, height: 80 },
70
+ { id: '11', type: 'basic', name: '資料壓縮', description: '壓縮資料以減少儲存空間', position: { x: 620, y: 360 }, shape: 'triangle-down', width: 100, height: 80 },
71
+ { id: '12', type: 'basic', name: '快取處理', description: '從快取中讀取或寫入資料', position: { x: 620, y: 500 }, shape: 'triangle-left', width: 100, height: 80 },
72
+ ],
73
+ conns: [
74
+ { id: 'e1-2', from: '1', to: '2', type: 'bezier', animated: true, name: '原始資料', description: '未經處理的 API 回應' },
75
+ { id: 'e1-3', from: '1', to: '3', type: 'bezier', name: '備份資料', description: '同步至轉換流程', markerEnd: 'arrowclosed' },
76
+ { id: 'e2-8', from: '2', to: '8', type: 'smoothstep', name: '驗證通過', description: '通過格式檢查的資料' },
77
+ { id: 'e8-4', from: '8', to: '4', type: 'bezier', name: '條件成立', description: '符合條件進入商業邏輯' },
78
+ { id: 'e8-5', from: '8', to: '5', type: 'bezier', name: '條件不成立', description: '不符條件直接聚合' },
79
+ { id: 'e3-5', from: '3', to: '5', type: 'step', name: '轉換完成', description: '標準化後的資料' },
80
+ { id: 'e4-5', from: '4', to: '5', type: 'bezier', name: '處理結果', description: '商業邏輯計算結果' },
81
+ { id: 'e5-6', from: '5', to: '6', type: 'smoothstep', name: '最終資料', description: '聚合後準備寫入的資料' },
82
+ { id: 'e5-7', from: '5', to: '7', type: 'bezier', animated: true, name: '觸發通知', description: '處理完成的事件訊號' },
83
+ { id: 'e3-9', from: '3', to: '9', type: 'step', name: '轉換副本', description: '備份至過濾流程' },
84
+ { id: 'e9-10', from: '9', to: '10', type: 'smoothstep', name: '已過濾', description: '通過過濾條件的資料' },
85
+ { id: 'e10-11', from: '10', to: '11', type: 'bezier', name: '管道A', description: '分配至壓縮管道' },
86
+ { id: 'e11-12', from: '11', to: '12', type: 'step', name: '已壓縮', description: '壓縮完成的資料' },
87
+ { id: 'e12-7', from: '12', to: '7', type: 'bezier', name: '快取完成', description: '快取處理後通知' },
88
+ ],
89
+ },
90
+ 'action': [
91
+ ],
92
+ }
93
+ },
94
+ mounted: function() {
95
+ let vo = this
96
+ vo.showOptJson()
97
+ },
98
+ watch: {
99
+ opt: {
100
+ handler: function() {
101
+ let vo = this
102
+ vo.showOptJson()
103
+ },
104
+ deep: true,
105
+ },
106
+ },
107
+ methods: {
108
+ showOptJson: function() {
109
+ let vo = this
110
+ jv(vo.opt, document.querySelector('#optjson'), { expanded: true })
111
+ },
112
+ setWidth: function(r) {
113
+ let vo = this
114
+ vo.opt.width = r
115
+ },
116
+ setHeight: function(r) {
117
+ let vo = this
118
+ vo.opt.height = r
119
+ },
120
+ },
121
+ }
122
+ </script>
123
+
124
+ <style>
125
+ </style>
@@ -0,0 +1,186 @@
1
+ <template>
2
+ <div>
3
+
4
+ <div class="bkh">
5
+ <div style="font-size:1.5rem;">connectivity</div>
6
+ <a href="//yuda-lyu.github.io/w-flow-vue/examples/ex-AppExamConnectivity.html" target="_blank" class="item-link">example</a>
7
+ <a href="//github.com/yuda-lyu/w-flow-vue/blob/master/docs/examples/ex-AppExamConnectivity.html" target="_blank" class="item-link">code</a>
8
+ </div>
9
+
10
+ <div class="bkp">
11
+
12
+ <div v-if="!loading">
13
+ <div style="display:flex; gap:16px; margin-bottom:10px; align-items:center; flex-wrap:wrap;">
14
+ <label>A 連出點
15
+ <select v-model="srcHandle" @change="rebuild">
16
+ <option value="top">Top</option>
17
+ <option value="bottom">Bottom</option>
18
+ <option value="left">Left</option>
19
+ <option value="right">Right</option>
20
+ </select>
21
+ </label>
22
+ <label>B 方位
23
+ <select v-model="tgtQuadrant" @change="resetPositions(); rebuild()">
24
+ <option value="N">上 (N)</option>
25
+ <option value="NE">右上 (NE)</option>
26
+ <option value="E">右 (E)</option>
27
+ <option value="SE">右下 (SE)</option>
28
+ <option value="S">下 (S)</option>
29
+ <option value="SW">左下 (SW)</option>
30
+ <option value="W">左 (W)</option>
31
+ <option value="NW">左上 (NW)</option>
32
+ </select>
33
+ </label>
34
+ <label>B 連入點
35
+ <select v-model="tgtHandle" @change="rebuild">
36
+ <option value="top">Top</option>
37
+ <option value="bottom">Bottom</option>
38
+ <option value="left">Left</option>
39
+ <option value="right">Right</option>
40
+ </select>
41
+ </label>
42
+ </div>
43
+ </div>
44
+
45
+ <div style="display:flex; padding-bottom:40px; overflow-x:auto;">
46
+
47
+ <div style="position:relative; border:1px solid #ddd;">
48
+ <WFlowVue
49
+ :opt="opt"
50
+ @init="loading=false"
51
+ ></WFlowVue>
52
+ </div>
53
+
54
+ <div style="padding:0px 20px;">
55
+
56
+ <div :style="`border:1px solid #ddd; width:590px; min-width:590px; height:${opt.height}px; overflow-y:auto;`">
57
+ <div style="padding-left:5px;">
58
+ <div id="optjson" style="font-size:10pt;"></div>
59
+ </div>
60
+ </div>
61
+
62
+ </div>
63
+
64
+ </div>
65
+
66
+ </div>
67
+
68
+ </div>
69
+ </template>
70
+
71
+ <script>
72
+ import WFlowVue from './components/WFlowVue.vue'
73
+ import jv from 'w-jsonview-tree'
74
+
75
+ export default {
76
+ components: {
77
+ WFlowVue,
78
+ },
79
+ data: function() {
80
+ return {
81
+ 'loading': true,
82
+ 'srcHandle': 'bottom',
83
+ 'tgtQuadrant': 'SE',
84
+ 'tgtHandle': 'top',
85
+ 'CENTER_X': 350,
86
+ 'CENTER_Y': 250,
87
+ 'OFFSET': 200,
88
+ 'NODE_W': 100,
89
+ 'NODE_H': 40,
90
+ 'opt': {
91
+ width: 800,
92
+ height: 600,
93
+ nodes: [],
94
+ conns: [],
95
+ },
96
+ 'action': [
97
+ ],
98
+ }
99
+ },
100
+ mounted: function() {
101
+ let vo = this
102
+ vo.rebuild()
103
+ vo.showOptJson()
104
+ },
105
+ watch: {
106
+ opt: {
107
+ handler: function() {
108
+ let vo = this
109
+ vo.showOptJson()
110
+ },
111
+ deep: true,
112
+ },
113
+ },
114
+ computed: {
115
+ quadrantOffsets: function() {
116
+ let vo = this
117
+ // Offsets are from A's top-left corner.
118
+ // N/S: align B center-x with A center-x → dx = 0
119
+ // E/W: align B center-y with A center-y → dy = 0
120
+ let OFFSET = vo.OFFSET
121
+ let r = {
122
+ N: { x: 0, y: -OFFSET },
123
+ NE: { x: OFFSET, y: -OFFSET },
124
+ E: { x: OFFSET, y: 0 },
125
+ SE: { x: OFFSET, y: OFFSET },
126
+ S: { x: 0, y: OFFSET },
127
+ SW: { x: -OFFSET, y: OFFSET },
128
+ W: { x: -OFFSET, y: 0 },
129
+ NW: { x: -OFFSET, y: -OFFSET },
130
+ }
131
+ return r
132
+ },
133
+ caseLabel: function() {
134
+ return 'A.' + this.srcHandle + ' -> ' + this.tgtQuadrant + ' -> B.' + this.tgtHandle
135
+ },
136
+ },
137
+ methods: {
138
+ rebuild: function() {
139
+ let vo = this
140
+ if (vo.opt.nodes.length === 0) {
141
+ // Initial build — B position offset from A's top-left
142
+ let qOff = vo.quadrantOffsets[vo.tgtQuadrant]
143
+ let bx = vo.CENTER_X + qOff.x
144
+ let by = vo.CENTER_Y + qOff.y
145
+ if (bx < 10) bx = 10
146
+ if (by < 10) by = 10
147
+ vo.opt.nodes = [
148
+ { id: 'A', type: 'basic', name: 'A', position: { x: vo.CENTER_X, y: vo.CENTER_Y }, width: vo.NODE_W, height: vo.NODE_H, toPosition: vo.srcHandle },
149
+ { id: 'B', type: 'basic', name: 'B', position: { x: bx, y: by }, width: vo.NODE_W, height: vo.NODE_H, fromPosition: vo.tgtHandle },
150
+ ]
151
+ vo.opt.conns = [
152
+ { id: 'eA-B', from: 'A', to: 'B', type: 'step', name: vo.caseLabel },
153
+ ]
154
+ }
155
+ else {
156
+ // Update handle positions and conn name, preserve node positions
157
+ vo.opt.nodes[0].toPosition = vo.srcHandle
158
+ vo.opt.nodes[1].fromPosition = vo.tgtHandle
159
+ vo.opt.conns[0].name = vo.caseLabel
160
+ }
161
+ },
162
+ resetPositions: function() {
163
+ let vo = this
164
+ let qOff = vo.quadrantOffsets[vo.tgtQuadrant]
165
+ let bx = vo.CENTER_X + qOff.x
166
+ let by = vo.CENTER_Y + qOff.y
167
+ if (bx < 10) bx = 10
168
+ if (by < 10) by = 10
169
+ vo.opt.nodes[0].position = { x: vo.CENTER_X, y: vo.CENTER_Y }
170
+ vo.opt.nodes[1].position = { x: bx, y: by }
171
+ },
172
+ showOptJson: function() {
173
+ let vo = this
174
+ jv(vo.opt, document.querySelector('#optjson'), { expanded: true })
175
+ // let pathD = ''
176
+ // let edgeEl = document.querySelector('[data-id="eA-B"] path')
177
+ // if (edgeEl) pathD = edgeEl.getAttribute('d')
178
+ // let BUILD_HASH = 'f2h7k4'
179
+ // console.log(BUILD_HASH + ': ' + JSON.stringify({ opt: vo.opt, path: pathD }))
180
+ },
181
+ },
182
+ }
183
+ </script>
184
+
185
+ <style>
186
+ </style>