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
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Routing snapshot regression test.
3
+ *
4
+ * 576 combinations: 4 source handles × 4 target handles × 36 angles (10° steps).
5
+ * Each case verifies calculateStepPoints output matches the saved snapshot exactly.
6
+ * If a code change alters any routing result, this test will catch it.
7
+ *
8
+ * To regenerate snapshots after intentional changes:
9
+ * node --experimental-vm-modules test/generate-routing-snapshots.mjs
10
+ */
11
+ import { calculateStepPoints, clearStepCache } from '../src/js/step-routing'
12
+ import snapshots from './jsons/routing-snapshots.json'
13
+
14
+ const NODE_W = 100
15
+ const NODE_H = 40
16
+ const BUF = 24
17
+
18
+ describe('routing snapshot regression', () => {
19
+ beforeEach(() => {
20
+ clearStepCache()
21
+ })
22
+
23
+ test('snapshot file has 576 cases', () => {
24
+ expect(snapshots.length).toBe(576)
25
+ })
26
+
27
+ describe.each(
28
+ snapshots.map((c, i) => [
29
+ `[${i}] ${c.srcPos}→${c.tgtPos} ${c.deg}°`,
30
+ c,
31
+ ])
32
+ )('%s', (_label, c) => {
33
+ test('matches snapshot', () => {
34
+ let nodes = [
35
+ { id: 'A', position: c.aPos, width: NODE_W, height: NODE_H },
36
+ { id: 'B', position: c.bPos, width: NODE_W, height: NODE_H },
37
+ ]
38
+ let pts = calculateStepPoints(
39
+ c.sourceXY.x, c.sourceXY.y, c.srcPos,
40
+ c.targetXY.x, c.targetXY.y, c.tgtPos,
41
+ BUF, nodes, {}, 'A', 'B'
42
+ )
43
+ expect(pts).toEqual(c.pts)
44
+ })
45
+
46
+ test('no NaN in points', () => {
47
+ let nodes = [
48
+ { id: 'A', position: c.aPos, width: NODE_W, height: NODE_H },
49
+ { id: 'B', position: c.bPos, width: NODE_W, height: NODE_H },
50
+ ]
51
+ let pts = calculateStepPoints(
52
+ c.sourceXY.x, c.sourceXY.y, c.srcPos,
53
+ c.targetXY.x, c.targetXY.y, c.tgtPos,
54
+ BUF, nodes, {}, 'A', 'B'
55
+ )
56
+ pts.forEach((p, j) => {
57
+ expect(isNaN(p.x)).toBe(false)
58
+ expect(isNaN(p.y)).toBe(false)
59
+ })
60
+ })
61
+
62
+ test('first/last points match source/target', () => {
63
+ let nodes = [
64
+ { id: 'A', position: c.aPos, width: NODE_W, height: NODE_H },
65
+ { id: 'B', position: c.bPos, width: NODE_W, height: NODE_H },
66
+ ]
67
+ let pts = calculateStepPoints(
68
+ c.sourceXY.x, c.sourceXY.y, c.srcPos,
69
+ c.targetXY.x, c.targetXY.y, c.tgtPos,
70
+ BUF, nodes, {}, 'A', 'B'
71
+ )
72
+ expect(pts[0].x).toBe(c.sourceXY.x)
73
+ expect(pts[0].y).toBe(c.sourceXY.y)
74
+ expect(pts[pts.length - 1].x).toBe(c.targetXY.x)
75
+ expect(pts[pts.length - 1].y).toBe(c.targetXY.y)
76
+ })
77
+ })
78
+ })
@@ -0,0 +1,88 @@
1
+ import { calculateStepPoints, clearStepCache } from '../src/js/step-routing'
2
+
3
+ describe('step-routing', () => {
4
+ beforeEach(() => {
5
+ clearStepCache()
6
+ })
7
+
8
+ describe('calculateStepPoints', () => {
9
+ test('returns array of points', () => {
10
+ const pts = calculateStepPoints(100, 50, 'bottom', 300, 250, 'top', 20)
11
+ expect(Array.isArray(pts)).toBe(true)
12
+ expect(pts.length).toBeGreaterThanOrEqual(2)
13
+ })
14
+
15
+ test('first point matches source', () => {
16
+ const pts = calculateStepPoints(100, 50, 'bottom', 300, 250, 'top', 20)
17
+ expect(pts[0].x).toBe(100)
18
+ expect(pts[0].y).toBe(50)
19
+ })
20
+
21
+ test('last point matches target', () => {
22
+ const pts = calculateStepPoints(100, 50, 'bottom', 300, 250, 'top', 20)
23
+ const last = pts[pts.length - 1]
24
+ expect(last.x).toBe(300)
25
+ expect(last.y).toBe(250)
26
+ })
27
+
28
+ test('all points have x and y', () => {
29
+ const pts = calculateStepPoints(0, 0, 'right', 200, 100, 'left', 20)
30
+ pts.forEach(p => {
31
+ expect(typeof p.x).toBe('number')
32
+ expect(typeof p.y).toBe('number')
33
+ expect(isNaN(p.x)).toBe(false)
34
+ expect(isNaN(p.y)).toBe(false)
35
+ })
36
+ })
37
+
38
+ test('works with all position combinations', () => {
39
+ const positions = ['top', 'bottom', 'left', 'right']
40
+ positions.forEach(sp => {
41
+ positions.forEach(tp => {
42
+ const pts = calculateStepPoints(0, 0, sp, 200, 200, tp, 20)
43
+ expect(pts.length).toBeGreaterThanOrEqual(2)
44
+ expect(pts[0]).toEqual({ x: 0, y: 0 })
45
+ expect(pts[pts.length - 1]).toEqual({ x: 200, y: 200 })
46
+ })
47
+ })
48
+ })
49
+
50
+ test('avoids node obstacles', () => {
51
+ const nodes = [
52
+ { id: '1', position: { x: 0, y: 0 }, width: 100, height: 40 },
53
+ { id: '2', position: { x: 200, y: 200 }, width: 100, height: 40 },
54
+ { id: 'block', position: { x: 100, y: 80 }, width: 100, height: 100 },
55
+ ]
56
+ const ni = {}
57
+ const pts = calculateStepPoints(
58
+ 50, 40, 'bottom', 250, 200, 'top', 20,
59
+ nodes, ni, '1', '2'
60
+ )
61
+ expect(pts.length).toBeGreaterThanOrEqual(2)
62
+ expect(pts[0]).toEqual({ x: 50, y: 40 })
63
+ })
64
+
65
+ test('handles same source and target position', () => {
66
+ const pts = calculateStepPoints(100, 100, 'bottom', 100, 300, 'bottom', 20)
67
+ expect(pts.length).toBeGreaterThanOrEqual(2)
68
+ })
69
+
70
+ test('handles close source and target', () => {
71
+ const pts = calculateStepPoints(100, 100, 'bottom', 105, 105, 'top', 20)
72
+ expect(pts.length).toBeGreaterThanOrEqual(2)
73
+ })
74
+ })
75
+
76
+ describe('clearStepCache', () => {
77
+ test('does not throw', () => {
78
+ expect(() => clearStepCache()).not.toThrow()
79
+ })
80
+
81
+ test('can be called multiple times', () => {
82
+ clearStepCache()
83
+ clearStepCache()
84
+ clearStepCache()
85
+ })
86
+ })
87
+
88
+ })
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Visual regression test using Playwright screenshots.
3
+ * Compares current rendering against baseline images pixel-by-pixel.
4
+ *
5
+ * Requires: npm run serve (http://localhost:8080) running
6
+ * Run: node test/visual-regression.test.mjs
7
+ *
8
+ * To regenerate baselines after intentional visual changes:
9
+ * node test/generate-visual-baselines.mjs
10
+ */
11
+ import { chromium } from 'playwright'
12
+ import fs from 'fs'
13
+ import path from 'path'
14
+ import { fileURLToPath } from 'url'
15
+ import { PNG } from 'pngjs'
16
+
17
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
18
+ const baselineDir = path.join(__dirname, 'pics')
19
+
20
+ function comparePNG(baselinePath, currentBuffer, threshold = 0.01) {
21
+ if (!fs.existsSync(baselinePath)) {
22
+ return { match: false, reason: 'Baseline not found: ' + baselinePath }
23
+ }
24
+ const baseline = PNG.sync.read(fs.readFileSync(baselinePath))
25
+ const current = PNG.sync.read(currentBuffer)
26
+ if (baseline.width !== current.width || baseline.height !== current.height) {
27
+ return { match: false, reason: `Size: ${baseline.width}x${baseline.height} vs ${current.width}x${current.height}` }
28
+ }
29
+ let diffPixels = 0
30
+ const total = baseline.width * baseline.height
31
+ for (let i = 0; i < baseline.data.length; i += 4) {
32
+ let d = Math.abs(baseline.data[i] - current.data[i]) + Math.abs(baseline.data[i + 1] - current.data[i + 1]) + Math.abs(baseline.data[i + 2] - current.data[i + 2])
33
+ if (d > 30) diffPixels++
34
+ }
35
+ let diffRatio = diffPixels / total
36
+ return { match: diffRatio <= threshold, diffRatio, diffPixels, total }
37
+ }
38
+
39
+ let passed = 0, failed = 0, errors = []
40
+
41
+ async function assert(name, result) {
42
+ if (result.match) {
43
+ passed++
44
+ console.log(` ✓ ${name}`)
45
+ } else {
46
+ failed++
47
+ let msg = result.reason || `${(result.diffRatio * 100).toFixed(2)}% diff (${result.diffPixels}/${result.total})`
48
+ errors.push(`${name}: ${msg}`)
49
+ console.log(` ✗ ${name} — ${msg}`)
50
+ }
51
+ }
52
+
53
+ const VW = 1280
54
+ const VH = 900
55
+
56
+ /** Clip a region around a bounding box with padding, clamped to viewport */
57
+ function clipAround(box, pad) {
58
+ let x = Math.max(0, Math.floor(box.x - pad))
59
+ let y = Math.max(0, Math.floor(box.y - pad))
60
+ let w = Math.min(Math.ceil(box.width + pad * 2), VW - x)
61
+ let h = Math.min(Math.ceil(box.height + pad * 2), VH - y)
62
+ if (w < 1 || h < 1) return null
63
+ return { x, y, width: w, height: h }
64
+ }
65
+
66
+ /** Center viewport on a node by its data id */
67
+ function centerOnNode(page, nodeId) {
68
+ return page.evaluate((id) => {
69
+ let vm
70
+ for (let el of document.querySelectorAll('*')) {
71
+ if (el.__vue__ && el.__vue__.setViewport) {
72
+ vm = el.__vue__
73
+ break
74
+ }
75
+ }
76
+ if (!vm) return false
77
+ let n = vm.nodes.find(n => n.id === id)
78
+ if (!n) return false
79
+ let cw = vm.widthInp
80
+ let ch = vm.heightInp
81
+ let w = n.width || 100
82
+ let h = n.height || 40
83
+ vm.setViewport({ x: cw / 2 - (n.position.x + w / 2), y: ch / 2 - (n.position.y + h / 2), zoom: 1 })
84
+ return true
85
+ }, nodeId)
86
+ }
87
+
88
+ /** Reset viewport to origin */
89
+ function resetViewport(page) {
90
+ return page.evaluate(() => {
91
+ for (let el of document.querySelectorAll('*')) {
92
+ if (el.__vue__ && el.__vue__.setViewport) {
93
+ el.__vue__.setViewport({ x: 0, y: 0, zoom: 1 })
94
+ return
95
+ }
96
+ }
97
+ })
98
+ }
99
+
100
+ /** fitView to show all nodes */
101
+ function fitView(page) {
102
+ return page.evaluate(() => {
103
+ for (let el of document.querySelectorAll('*')) {
104
+ if (el.__vue__ && el.__vue__.fitView) {
105
+ el.__vue__.fitView(30)
106
+ return
107
+ }
108
+ }
109
+ })
110
+ }
111
+
112
+ /** Get the canvas container clip region */
113
+ function getCanvasClip(page) {
114
+ return page.evaluate(() => {
115
+ let el = document.querySelector('.vue-flow__viewport')
116
+ if (!el) return null
117
+ let container = el.closest('[style*="height"]')
118
+ if (!container) return null
119
+ let rect = container.getBoundingClientRect()
120
+ return { x: Math.floor(rect.x), y: Math.floor(rect.y), width: Math.ceil(rect.width), height: Math.ceil(rect.height) }
121
+ })
122
+ }
123
+
124
+ async function run() {
125
+ const browser = await chromium.launch()
126
+ const page = await browser.newPage({ viewport: { width: VW, height: VH } })
127
+ await page.goto('http://localhost:8080')
128
+ await page.waitForTimeout(2000)
129
+
130
+ console.log('Visual Regression Tests\n')
131
+ const pad = 60
132
+
133
+ // 1. Overview — fitView to show all nodes
134
+ await fitView(page)
135
+ await page.waitForTimeout(500)
136
+ let canvasClip = await getCanvasClip(page)
137
+ let buf = await page.screenshot({ clip: canvasClip })
138
+ await assert('overview', comparePNG(path.join(baselineDir, 'vb-overview.png'), buf))
139
+
140
+ // 2. Each node — center on node, clip with padding (includes surrounding edges)
141
+ let nodes = await page.$$('.vue-flow__node')
142
+ for (let node of nodes) {
143
+ let id = await node.getAttribute('data-id')
144
+ if (!id) continue
145
+ let bp = path.join(baselineDir, `vb-node-${id}.png`)
146
+ if (!fs.existsSync(bp)) continue
147
+ await centerOnNode(page, id)
148
+ await page.waitForTimeout(300)
149
+ let box = await node.boundingBox()
150
+ if (!box) continue
151
+ let clip = clipAround(box, pad)
152
+ if (!clip) continue
153
+ buf = await page.screenshot({ clip })
154
+ await assert(`node-${id}`, comparePNG(bp, buf))
155
+ }
156
+
157
+ // 3. Node hovered — resize handles + settings icon
158
+ let firstNode = nodes[0]
159
+ let firstId = firstNode ? await firstNode.getAttribute('data-id') : null
160
+ if (firstNode && firstId) {
161
+ await centerOnNode(page, firstId)
162
+ await page.waitForTimeout(300)
163
+ let box = await firstNode.boundingBox()
164
+ await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2)
165
+ await page.waitForTimeout(500)
166
+ box = await firstNode.boundingBox()
167
+ buf = await page.screenshot({ clip: clipAround(box, pad) })
168
+ await assert('node-hovered', comparePNG(path.join(baselineDir, 'vb-node-hovered.png'), buf))
169
+ await page.mouse.move(0, 0)
170
+ await page.waitForTimeout(300)
171
+ }
172
+
173
+ // 4. Node selected
174
+ if (firstNode && firstId) {
175
+ await centerOnNode(page, firstId)
176
+ await page.waitForTimeout(300)
177
+ await firstNode.click()
178
+ await page.waitForTimeout(300)
179
+ let box = await firstNode.boundingBox()
180
+ buf = await page.screenshot({ clip: clipAround(box, pad) })
181
+ await assert('node-selected', comparePNG(path.join(baselineDir, 'vb-node-selected.png'), buf))
182
+ await page.mouse.click(10, 10)
183
+ await page.waitForTimeout(300)
184
+ }
185
+
186
+ // 5. Edge hovered — settings icon
187
+ await resetViewport(page)
188
+ await page.waitForTimeout(300)
189
+ let edges = await page.$$('.vue-flow__edge-interaction')
190
+ if (edges.length > 0) {
191
+ let box = await edges[0].boundingBox()
192
+ if (box) {
193
+ await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2)
194
+ await page.waitForTimeout(500)
195
+ box = await edges[0].boundingBox()
196
+ buf = await page.screenshot({ clip: clipAround(box, 40) })
197
+ await assert('edge-hovered', comparePNG(path.join(baselineDir, 'vb-edge-hovered.png'), buf))
198
+ await page.mouse.move(0, 0)
199
+ await page.waitForTimeout(300)
200
+ }
201
+ }
202
+
203
+ // 6. Edges normal
204
+ await fitView(page)
205
+ await page.waitForTimeout(500)
206
+ buf = await page.screenshot({ clip: canvasClip })
207
+ await assert('edges-normal', comparePNG(path.join(baselineDir, 'vb-edges-normal.png'), buf))
208
+
209
+ // ===== LOCKED STATE =====
210
+ console.log('')
211
+ let lockBtn = await page.$('.vue-flow__controls button[title="Lock"]')
212
+ if (lockBtn) {
213
+ await lockBtn.click()
214
+ await page.waitForTimeout(500)
215
+ }
216
+
217
+ // 7. Locked overview
218
+ buf = await page.screenshot({ clip: canvasClip })
219
+ await assert('locked-overview', comparePNG(path.join(baselineDir, 'vb-locked-overview.png'), buf))
220
+
221
+ // 8. Locked node hovered — no resize handles, no settings icon, no handles
222
+ if (firstNode && firstId) {
223
+ await centerOnNode(page, firstId)
224
+ await page.waitForTimeout(300)
225
+ let box = await firstNode.boundingBox()
226
+ await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2)
227
+ await page.waitForTimeout(500)
228
+ box = await firstNode.boundingBox()
229
+ buf = await page.screenshot({ clip: clipAround(box, pad) })
230
+ await assert('locked-node-hovered', comparePNG(path.join(baselineDir, 'vb-locked-node-hovered.png'), buf))
231
+ await page.mouse.move(0, 0)
232
+ await page.waitForTimeout(300)
233
+ }
234
+
235
+ // 9. Locked node selected
236
+ if (firstNode && firstId) {
237
+ await centerOnNode(page, firstId)
238
+ await page.waitForTimeout(300)
239
+ await firstNode.click()
240
+ await page.waitForTimeout(300)
241
+ let box = await firstNode.boundingBox()
242
+ buf = await page.screenshot({ clip: clipAround(box, pad) })
243
+ await assert('locked-node-selected', comparePNG(path.join(baselineDir, 'vb-locked-node-selected.png'), buf))
244
+ await page.mouse.click(10, 10)
245
+ await page.waitForTimeout(300)
246
+ }
247
+
248
+ // 10. Locked edge hovered — no settings icon
249
+ await resetViewport(page)
250
+ await page.waitForTimeout(300)
251
+ if (edges.length > 0) {
252
+ let box = await edges[0].boundingBox()
253
+ if (box) {
254
+ await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2)
255
+ await page.waitForTimeout(500)
256
+ box = await edges[0].boundingBox()
257
+ buf = await page.screenshot({ clip: clipAround(box, 40) })
258
+ await assert('locked-edge-hovered', comparePNG(path.join(baselineDir, 'vb-locked-edge-hovered.png'), buf))
259
+ await page.mouse.move(0, 0)
260
+ await page.waitForTimeout(300)
261
+ }
262
+ }
263
+
264
+ console.log(`\nResults: ${passed} passed, ${failed} failed`)
265
+ if (errors.length > 0) {
266
+ console.log('\nFailed:')
267
+ errors.forEach(e => console.log(' ' + e))
268
+ }
269
+
270
+ await browser.close()
271
+ process.exit(failed > 0 ? 1 : 0)
272
+ }
273
+
274
+ run().catch(e => { console.error(e); process.exit(1) })
@@ -0,0 +1,4 @@
1
+ import addVersion from 'w-package-tools/src/addVersion.mjs'
2
+
3
+
4
+ addVersion()
@@ -0,0 +1,4 @@
1
+ import cleanFolder from 'w-package-tools/src/cleanFolder.mjs'
2
+
3
+
4
+ cleanFolder('docs')
@@ -0,0 +1,34 @@
1
+ import rollupVueToHtml from 'w-package-tools/src/rollupVueToHtml.mjs'
2
+
3
+
4
+ let opt = {
5
+ title: `w-flow-vue`,
6
+ head: `
7
+
8
+ <!-- rollupVueToHtml已自動添加@babel/polyfill與vue -->
9
+
10
+ <!-- fontawesome -->
11
+ <link href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.4.2/css/all.min.css" rel="stylesheet">
12
+
13
+ <!-- mdi, 各組件使用mdi/js故不需引用 -->
14
+ <link _href="https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css" rel="stylesheet">
15
+
16
+ <!-- google, 各組件使用mdi/js故不需引用 -->
17
+ <link _href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
18
+ <link _href="https://fonts.googleapis.com/css?family=Material+Icons" rel="stylesheet">
19
+
20
+ <!-- lodash -->
21
+ <script src="https://cdn.jsdelivr.net/npm/lodash/lodash.min.js"></script>
22
+
23
+ <!-- wsemi -->
24
+ <script src="https://cdn.jsdelivr.net/npm/wsemi/dist/wsemi.umd.min.js"></script>
25
+
26
+ `,
27
+ newVue: ``,
28
+ globals: {
29
+ },
30
+ external: [
31
+ ],
32
+ }
33
+ rollupVueToHtml('./src/App.vue', './docs/examples/app.html', opt)
34
+
@@ -0,0 +1,22 @@
1
+ import rollupFiles from 'w-package-tools/src/rollupFiles.mjs'
2
+
3
+
4
+ let fdSrc = './src/components/'
5
+ let fdTar = './dist'
6
+
7
+
8
+ rollupFiles({
9
+ fns: 'WFlowVue.vue',
10
+ fdSrc,
11
+ fdTar,
12
+ format: 'umd',
13
+ //nameDistType: 'kebabCase',
14
+ hookNameDist: () => {
15
+ return 'w-flow-vue'
16
+ },
17
+ globals: {
18
+ },
19
+ external: [
20
+ ],
21
+ })
22
+
@@ -0,0 +1,47 @@
1
+ import _ from 'lodash-es'
2
+ import fs from 'fs'
3
+ import getFiles from 'w-package-tools/src/getFiles.mjs'
4
+ import getPks from 'w-package-tools/src/getPks.mjs'
5
+
6
+
7
+ let fdSrc = './test-html/'
8
+ let fdTar = './docs/examples/'
9
+
10
+
11
+ async function main() {
12
+ //把example裡面cdn更換, 再複製到docs的example內, 作為日後發佈為靜態網站
13
+
14
+ //pks
15
+ let pks = getPks()
16
+
17
+ //cdn
18
+ let cdn = `<script src="https://cdn.jsdelivr.net/npm/w-flow-vue@${pks.version}/dist/w-flow-vue.umd.js"></script>`
19
+
20
+ //mkdirSync
21
+ if (!fs.existsSync(fdTar)) {
22
+ fs.mkdirSync(fdTar)
23
+ }
24
+
25
+ //getFiles
26
+ let ltfs = getFiles(fdSrc)
27
+
28
+ _.each(ltfs, function(v) {
29
+
30
+ //fn
31
+ let fn = fdSrc + v
32
+
33
+ //c
34
+ let c = fs.readFileSync(fn, 'utf8')
35
+
36
+ //replace
37
+ let r = `<script src="../dist/w-flow-vue.umd.js"></script>`
38
+ c = c.replace(r, cdn)
39
+
40
+ //write
41
+ //console.log(c)
42
+ fs.writeFileSync(fdTar + v, c, 'utf8')
43
+
44
+ })
45
+
46
+ }
47
+ main()