@elicdavis/node-flow 0.1.16 → 0.1.18
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/dist/index.d.mts +97 -4
- package/dist/index.d.ts +97 -4
- package/dist/index.js +674 -79
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +668 -78
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/graph.ts +234 -41
- package/src/index.ts +16 -0
- package/src/input.ts +22 -14
- package/src/markdown/entry.ts +6 -2
- package/src/minimap.ts +178 -0
- package/src/node.ts +83 -1
- package/src/nodes/factory.ts +9 -5
- package/src/nodes/publisher.ts +39 -4
- package/src/nodes/subsystem.ts +214 -2
- package/src/notes/subsystem.ts +3 -0
- package/src/popup.ts +27 -6
- package/src/port.ts +17 -4
- package/src/render_scheduler.ts +49 -0
- package/src/theme.ts +8 -0
- package/src/types/text.ts +2 -0
- package/src/widgets/image.ts +2 -0
- package/src/widgets/number.ts +2 -1
- package/src/widgets/string.ts +3 -1
- package/src/widgets/toggle.ts +3 -1
- package/test/minimap.test.ts +103 -0
- package/test/node_menu_filter.test.ts +119 -0
- package/test/popup.test.ts +104 -0
- package/test/render_scheduler.test.ts +119 -0
- package/test/widget_click.test.ts +126 -0
- package/test/widget_construction.test.ts +48 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { strict as assert } from 'assert';
|
|
2
|
+
import { NodeSubsystem } from '../src/nodes/subsystem';
|
|
3
|
+
import { PassSubsystem } from '../src/pass/subsystem';
|
|
4
|
+
import { FlowNode } from '../src/node';
|
|
5
|
+
import { Camera } from '../src/camera';
|
|
6
|
+
import { Box } from '../src/types/box';
|
|
7
|
+
import { Vector2 } from '../src/types/vector2';
|
|
8
|
+
|
|
9
|
+
function stubCtx(): CanvasRenderingContext2D {
|
|
10
|
+
const ctx: any = {
|
|
11
|
+
canvas: { width: 800, height: 600 },
|
|
12
|
+
lineWidth: 1,
|
|
13
|
+
font: '',
|
|
14
|
+
textAlign: 'left',
|
|
15
|
+
textBaseline: 'top',
|
|
16
|
+
fillStyle: '',
|
|
17
|
+
strokeStyle: '',
|
|
18
|
+
globalAlpha: 1,
|
|
19
|
+
measureText: () => ({ width: 40 }),
|
|
20
|
+
save: () => { }, restore: () => { }, translate: () => { }, scale: () => { },
|
|
21
|
+
beginPath: () => { }, closePath: () => { }, roundRect: () => { }, rect: () => { },
|
|
22
|
+
moveTo: () => { }, lineTo: () => { }, arc: () => { }, ellipse: () => { },
|
|
23
|
+
bezierCurveTo: () => { }, quadraticCurveTo: () => { },
|
|
24
|
+
fill: () => { }, stroke: () => { }, fillText: () => { }, strokeText: () => { },
|
|
25
|
+
clip: () => { }, drawImage: () => { }, setLineDash: () => { },
|
|
26
|
+
createLinearGradient: () => ({ addColorStop: () => { } }),
|
|
27
|
+
};
|
|
28
|
+
return ctx as CanvasRenderingContext2D;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function recordingWidget() {
|
|
32
|
+
let clicks = 0;
|
|
33
|
+
let drawnAt: Box | null = null;
|
|
34
|
+
const widget = {
|
|
35
|
+
Size: (): Vector2 => ({ x: 100, y: 20 }),
|
|
36
|
+
Draw: (_ctx: CanvasRenderingContext2D, position: Vector2, scale: number): Box => {
|
|
37
|
+
drawnAt = { Position: { x: position.x, y: position.y }, Size: { x: 100 * scale, y: 20 * scale } };
|
|
38
|
+
return drawnAt;
|
|
39
|
+
},
|
|
40
|
+
ClickStart: (): void => { },
|
|
41
|
+
ClickEnd: (): void => { clicks++; },
|
|
42
|
+
};
|
|
43
|
+
return {
|
|
44
|
+
widget,
|
|
45
|
+
clicks: () => clicks,
|
|
46
|
+
center: (): Vector2 => {
|
|
47
|
+
assert.ok(drawnAt !== null, 'widget was never drawn');
|
|
48
|
+
const b = drawnAt as Box;
|
|
49
|
+
return { x: b.Position.x + b.Size.x / 2, y: b.Position.y + b.Size.y / 2 };
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function scene() {
|
|
55
|
+
const graph = new NodeSubsystem(new PassSubsystem());
|
|
56
|
+
const node = new FlowNode({ title: 'test', position: { x: 100, y: 100 } });
|
|
57
|
+
const rec = recordingWidget();
|
|
58
|
+
node.addWidget(rec.widget as any);
|
|
59
|
+
graph.addNode(node);
|
|
60
|
+
|
|
61
|
+
const camera = new Camera();
|
|
62
|
+
const ctx = stubCtx();
|
|
63
|
+
return { graph, camera, ctx, rec };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
describe('clicking a widget', () => {
|
|
67
|
+
it('opens it when the press and release are both on it', () => {
|
|
68
|
+
const { graph, camera, ctx, rec } = scene();
|
|
69
|
+
graph.render(ctx, camera, undefined);
|
|
70
|
+
const on = rec.center();
|
|
71
|
+
|
|
72
|
+
graph.render(ctx, camera, on);
|
|
73
|
+
graph.clickStart(on, camera, false);
|
|
74
|
+
graph.render(ctx, camera, on);
|
|
75
|
+
graph.clickEnd();
|
|
76
|
+
|
|
77
|
+
assert.equal(rec.clicks(), 1);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('leaves it alone when the gesture drags away before releasing', () => {
|
|
81
|
+
const { graph, camera, ctx, rec } = scene();
|
|
82
|
+
graph.render(ctx, camera, undefined);
|
|
83
|
+
const on = rec.center();
|
|
84
|
+
|
|
85
|
+
graph.render(ctx, camera, on);
|
|
86
|
+
graph.clickStart(on, camera, false);
|
|
87
|
+
graph.render(ctx, camera, { x: on.x + 400, y: on.y + 300 });
|
|
88
|
+
graph.clickEnd();
|
|
89
|
+
|
|
90
|
+
assert.equal(rec.clicks(), 0);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('a node panned off screen', () => {
|
|
95
|
+
it('stops hit-testing as a widget where it used to be drawn', () => {
|
|
96
|
+
const graph = new NodeSubsystem(new PassSubsystem());
|
|
97
|
+
const node = new FlowNode({
|
|
98
|
+
title: 'test',
|
|
99
|
+
position: { x: 100, y: 100 },
|
|
100
|
+
inputs: [{ name: 'In', type: 'float64' }],
|
|
101
|
+
});
|
|
102
|
+
const rec = recordingWidget();
|
|
103
|
+
node.addWidget(rec.widget as any);
|
|
104
|
+
graph.addNode(node);
|
|
105
|
+
|
|
106
|
+
const camera = new Camera();
|
|
107
|
+
const ctx = stubCtx();
|
|
108
|
+
|
|
109
|
+
graph.render(ctx, camera, undefined);
|
|
110
|
+
const on = rec.center();
|
|
111
|
+
|
|
112
|
+
camera.position.x = -50000;
|
|
113
|
+
graph.render(ctx, camera, undefined);
|
|
114
|
+
|
|
115
|
+
assert.equal(
|
|
116
|
+
node.inBounds(ctx, camera, on).Widget,
|
|
117
|
+
undefined,
|
|
118
|
+
'a node that was not drawn has no widget under any screen point',
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
graph.clickStart(on, camera, false);
|
|
122
|
+
graph.clickEnd();
|
|
123
|
+
|
|
124
|
+
assert.equal(rec.clicks(), 0);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { strict as assert } from 'assert';
|
|
2
|
+
import { NumberWidget } from '../src/widgets/number';
|
|
3
|
+
import { StringWidget } from '../src/widgets/string';
|
|
4
|
+
import { ToggleWidget } from '../src/widgets/toggle';
|
|
5
|
+
import { SliderWidget } from '../src/widgets/slider';
|
|
6
|
+
import { FlowNode } from '../src/node';
|
|
7
|
+
|
|
8
|
+
function stubNode(): FlowNode {
|
|
9
|
+
const properties = new Map<string, unknown>();
|
|
10
|
+
return {
|
|
11
|
+
getProperty: (name: string) => properties.get(name),
|
|
12
|
+
setProperty: (name: string, value: unknown) => { properties.set(name, value); },
|
|
13
|
+
addPropertyChangeListener: () => { },
|
|
14
|
+
} as unknown as FlowNode;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe('widget construction', () => {
|
|
18
|
+
it('does not fire a number callback for its initial value', () => {
|
|
19
|
+
let fired = 0;
|
|
20
|
+
new NumberWidget(stubNode(), { value: 42, callback: () => { fired++; } });
|
|
21
|
+
assert.equal(fired, 0);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('does not fire a string callback for its initial value', () => {
|
|
25
|
+
let fired = 0;
|
|
26
|
+
new StringWidget(stubNode(), { value: 'hello', callback: () => { fired++; } });
|
|
27
|
+
assert.equal(fired, 0);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('does not fire a toggle callback for its initial value', () => {
|
|
31
|
+
let fired = 0;
|
|
32
|
+
new ToggleWidget(stubNode(), { value: true, callback: () => { fired++; } });
|
|
33
|
+
assert.equal(fired, 0);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('does not fire a slider callback for its initial value', () => {
|
|
37
|
+
let fired = 0;
|
|
38
|
+
new SliderWidget(stubNode(), { value: 7, min: 0, max: 10, change: () => { fired++; } });
|
|
39
|
+
assert.equal(fired, 0);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('still fires the number callback on a real change', () => {
|
|
43
|
+
const values: Array<number> = [];
|
|
44
|
+
const widget = new NumberWidget(stubNode(), { value: 42, callback: (v) => { values.push(v); } });
|
|
45
|
+
widget.Set(43);
|
|
46
|
+
assert.deepEqual(values, [43]);
|
|
47
|
+
});
|
|
48
|
+
});
|