@elicdavis/node-flow 0.2.0 → 0.2.1
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 +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +82 -42
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +82 -42
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/graph.ts +7 -4
- package/src/index.ts +2 -2
- package/src/widgets/inplace.ts +75 -23
- package/src/widgets/number.ts +5 -12
- package/src/widgets/string.ts +1 -8
- package/src/widgets/widget.ts +201 -180
- package/test/support.ts +15 -2
- package/test/widgets.test.ts +54 -1
package/package.json
CHANGED
package/src/graph.ts
CHANGED
|
@@ -19,6 +19,7 @@ export { CameraOrientation };
|
|
|
19
19
|
import { PassSubsystem } from './pass/subsystem';
|
|
20
20
|
import { QuickMenu } from './quickMenu';
|
|
21
21
|
import { onRenderRequested, renderContinuously, requestRender } from './render_scheduler';
|
|
22
|
+
import { placeEditsDuring } from './widgets/inplace';
|
|
22
23
|
import { Minimap, MinimapConfig } from './minimap';
|
|
23
24
|
|
|
24
25
|
export type GraphRenderer = (canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, position: Vector2, scale: number) => void;
|
|
@@ -761,10 +762,12 @@ export class NodeFlowGraph {
|
|
|
761
762
|
TimeExecution("Render_Background", this.#renderBackground.bind(this));
|
|
762
763
|
|
|
763
764
|
TimeExecution("Render_View_" + this.#currentView, () => {
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
765
|
+
placeEditsDuring(this.#canvas, () => {
|
|
766
|
+
let results = this.currentView().render(this.#ctx, this.#camera, this.#mousePosition);
|
|
767
|
+
if (results?.cursorStyle) {
|
|
768
|
+
this.#cursor = results?.cursorStyle;
|
|
769
|
+
}
|
|
770
|
+
});
|
|
768
771
|
});
|
|
769
772
|
|
|
770
773
|
TimeExecution("Render_Minimap", () => {
|
package/src/index.ts
CHANGED
|
@@ -66,7 +66,7 @@ import { SliderWidget, SliderWidgetConfig } from './widgets/slider';
|
|
|
66
66
|
import { ImageWidget, ImageWidgetConfig } from './widgets/image';
|
|
67
67
|
import { RowWidget, RowWidgetConfig } from './widgets/row';
|
|
68
68
|
import { drawField, drawFieldBox, drawFieldText, FieldLook, widgetFont } from './widgets/field';
|
|
69
|
-
import { editInPlace, InPlaceEditConfig } from './widgets/inplace';
|
|
69
|
+
import { editInPlace, InPlaceEdit, InPlaceEditConfig } from './widgets/inplace';
|
|
70
70
|
export {
|
|
71
71
|
WidgetBase, ValueWidget,
|
|
72
72
|
NumberWidget, ColorWidget, StringWidget, TextWidget,
|
|
@@ -74,7 +74,7 @@ export {
|
|
|
74
74
|
drawField, drawFieldBox, drawFieldText, widgetFont, editInPlace, formatNumber,
|
|
75
75
|
};
|
|
76
76
|
export type {
|
|
77
|
-
Widget, WidgetPointer, ValueWidgetConfig, FieldLook, InPlaceEditConfig,
|
|
77
|
+
Widget, WidgetPointer, ValueWidgetConfig, FieldLook, InPlaceEdit, InPlaceEditConfig,
|
|
78
78
|
NumberWidgetConfig, ColorWidgetConfig, StringWidgetConfig, TextWidgetConfig,
|
|
79
79
|
ButtonWidgetConfig, ToggleWidgetConfig, SliderWidgetConfig,
|
|
80
80
|
ImageWidgetConfig, RowWidgetConfig,
|
package/src/widgets/inplace.ts
CHANGED
|
@@ -17,40 +17,90 @@ export interface InPlaceEditConfig {
|
|
|
17
17
|
onCommit: (text: string) => void;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
export interface InPlaceEdit {
|
|
21
|
+
input: HTMLInputElement;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Moves the input over where the widget was just drawn, in canvas pixels.
|
|
25
|
+
*/
|
|
26
|
+
place(box: Box, scale: number): void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface OpenEdit {
|
|
30
|
+
canvas: HTMLCanvasElement;
|
|
31
|
+
input: HTMLInputElement;
|
|
32
|
+
placed: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const open = new Set<OpenEdit>();
|
|
36
|
+
|
|
37
|
+
export function placeEditsDuring(canvas: HTMLCanvasElement, draw: () => void): void {
|
|
38
|
+
open.forEach((edit) => {
|
|
39
|
+
if (edit.canvas === canvas) {
|
|
40
|
+
edit.placed = false;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
draw();
|
|
44
|
+
open.forEach((edit) => {
|
|
45
|
+
if (edit.canvas === canvas && !edit.placed) {
|
|
46
|
+
// Not visibility or display: hiding either way blurs the input, which ends the edit.
|
|
47
|
+
edit.input.style.opacity = "0";
|
|
48
|
+
edit.input.style.pointerEvents = "none";
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
20
53
|
/**
|
|
21
54
|
* Lays a text input over a widget. Enter or clicking away keeps what was
|
|
22
55
|
* typed; Escape throws it away.
|
|
23
56
|
*/
|
|
24
|
-
export function editInPlace(config: InPlaceEditConfig):
|
|
57
|
+
export function editInPlace(config: InPlaceEditConfig): InPlaceEdit | null {
|
|
25
58
|
const canvas = config.canvas;
|
|
26
59
|
if (canvas === null) {
|
|
27
60
|
return null;
|
|
28
61
|
}
|
|
29
62
|
|
|
30
|
-
const rect = canvas.getBoundingClientRect();
|
|
31
|
-
const sx = canvas.width > 0 ? rect.width / canvas.width : 1;
|
|
32
|
-
const sy = canvas.height > 0 ? rect.height / canvas.height : 1;
|
|
33
|
-
|
|
34
63
|
const input = document.createElement("input");
|
|
35
64
|
input.type = "text";
|
|
36
65
|
input.value = config.value;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
66
|
+
const edit: OpenEdit = { canvas, input, placed: true };
|
|
67
|
+
|
|
68
|
+
const place = (box: Box, scale: number) => {
|
|
69
|
+
const rect = canvas.getBoundingClientRect();
|
|
70
|
+
const sx = canvas.width > 0 ? rect.width / canvas.width : 1;
|
|
71
|
+
const sy = canvas.height > 0 ? rect.height / canvas.height : 1;
|
|
72
|
+
const left = rect.left + (box.Position.x * sx);
|
|
73
|
+
const top = rect.top + (box.Position.y * sy);
|
|
74
|
+
const width = box.Size.x * sx;
|
|
75
|
+
const height = box.Size.y * sy;
|
|
76
|
+
const clip = [
|
|
77
|
+
Math.max(0, rect.top - top),
|
|
78
|
+
Math.max(0, (left + width) - (rect.left + rect.width)),
|
|
79
|
+
Math.max(0, (top + height) - (rect.top + rect.height)),
|
|
80
|
+
Math.max(0, rect.left - left),
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
edit.placed = true;
|
|
84
|
+
input.style.cssText = [
|
|
85
|
+
"position: fixed",
|
|
86
|
+
`left: ${left}px`,
|
|
87
|
+
`top: ${top}px`,
|
|
88
|
+
`width: ${width}px`,
|
|
89
|
+
`height: ${height}px`,
|
|
90
|
+
`clip-path: inset(${clip.map((c) => `${c}px`).join(" ")})`,
|
|
91
|
+
"box-sizing: border-box",
|
|
92
|
+
"margin: 0",
|
|
93
|
+
`padding: 0 ${Theme.Widget.Padding * scale}px`,
|
|
94
|
+
`font: ${Theme.Widget.FontSize * scale * sy}px ${Theme.FontFamily}`,
|
|
95
|
+
`color: ${Theme.Widget.FontColor}`,
|
|
96
|
+
`background: ${Theme.Widget.Hover.BackgroundColor}`,
|
|
97
|
+
`border: ${Theme.Widget.Border.Size * scale}px solid ${Theme.Widget.Border.Color}`,
|
|
98
|
+
`border-radius: ${Theme.Widget.Border.Radius * scale}px`,
|
|
99
|
+
"outline: none",
|
|
100
|
+
"z-index: 1000",
|
|
101
|
+
].join(";");
|
|
102
|
+
};
|
|
103
|
+
place(config.box, config.scale);
|
|
54
104
|
|
|
55
105
|
let done = false;
|
|
56
106
|
const finish = (keep: boolean) => {
|
|
@@ -58,6 +108,7 @@ export function editInPlace(config: InPlaceEditConfig): HTMLInputElement | null
|
|
|
58
108
|
return;
|
|
59
109
|
}
|
|
60
110
|
done = true;
|
|
111
|
+
open.delete(edit);
|
|
61
112
|
input.remove();
|
|
62
113
|
if (keep) {
|
|
63
114
|
config.onCommit(input.value);
|
|
@@ -77,8 +128,9 @@ export function editInPlace(config: InPlaceEditConfig): HTMLInputElement | null
|
|
|
77
128
|
});
|
|
78
129
|
input.addEventListener("blur", () => finish(true));
|
|
79
130
|
|
|
131
|
+
open.add(edit);
|
|
80
132
|
document.body.appendChild(input);
|
|
81
133
|
input.focus();
|
|
82
134
|
input.select();
|
|
83
|
-
return input;
|
|
135
|
+
return { input, place };
|
|
84
136
|
}
|
package/src/widgets/number.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { FlowNode } from "../node";
|
|
|
2
2
|
import { CursorStyle } from "../styles/cursor";
|
|
3
3
|
import { Box } from "../types/box";
|
|
4
4
|
import { drawField } from "./field";
|
|
5
|
-
import { editInPlace } from "./inplace";
|
|
6
5
|
import { ValueWidget, ValueWidgetConfig } from "./value";
|
|
7
6
|
import { WidgetPointer } from "./widget";
|
|
8
7
|
|
|
@@ -90,17 +89,11 @@ export class NumberWidget extends ValueWidget<number> {
|
|
|
90
89
|
return;
|
|
91
90
|
}
|
|
92
91
|
const before = this.value();
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
onCommit: (text) => {
|
|
99
|
-
const parsed = Number.parseFloat(text);
|
|
100
|
-
if (Number.isFinite(parsed)) {
|
|
101
|
-
this.commit(this.#clean(parsed), before);
|
|
102
|
-
}
|
|
103
|
-
},
|
|
92
|
+
this.editText(String(before), (text) => {
|
|
93
|
+
const parsed = Number.parseFloat(text);
|
|
94
|
+
if (Number.isFinite(parsed)) {
|
|
95
|
+
this.commit(this.#clean(parsed), before);
|
|
96
|
+
}
|
|
104
97
|
});
|
|
105
98
|
}
|
|
106
99
|
|
package/src/widgets/string.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { FlowNode } from "../node";
|
|
|
2
2
|
import { CursorStyle } from "../styles/cursor";
|
|
3
3
|
import { Box } from "../types/box";
|
|
4
4
|
import { drawField } from "./field";
|
|
5
|
-
import { editInPlace } from "./inplace";
|
|
6
5
|
import { ValueWidget, ValueWidgetConfig } from "./value";
|
|
7
6
|
|
|
8
7
|
export interface StringWidgetConfig extends ValueWidgetConfig<string> { }
|
|
@@ -28,12 +27,6 @@ export class StringWidget extends ValueWidget<string> {
|
|
|
28
27
|
|
|
29
28
|
protected onClick(): void {
|
|
30
29
|
const before = this.value();
|
|
31
|
-
|
|
32
|
-
canvas: this.canvas(),
|
|
33
|
-
box: this.box(),
|
|
34
|
-
scale: this.scale(),
|
|
35
|
-
value: before,
|
|
36
|
-
onCommit: (text) => this.commit(text, before),
|
|
37
|
-
});
|
|
30
|
+
this.editText(before, (text) => this.commit(text, before));
|
|
38
31
|
}
|
|
39
32
|
}
|
package/src/widgets/widget.ts
CHANGED
|
@@ -1,180 +1,201 @@
|
|
|
1
|
-
import { Box, InBox } from '../types/box';
|
|
2
|
-
import { Vector2 } from '../types/vector2';
|
|
3
|
-
import { CursorStyle } from '../styles/cursor';
|
|
4
|
-
import { requestRender } from '../render_scheduler';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
*
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
this.#
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
this.#
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
this.#pressed
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
protected
|
|
137
|
-
return this.#
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
protected
|
|
141
|
-
return this.#
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
):
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
1
|
+
import { Box, InBox } from '../types/box';
|
|
2
|
+
import { Vector2 } from '../types/vector2';
|
|
3
|
+
import { CursorStyle } from '../styles/cursor';
|
|
4
|
+
import { requestRender } from '../render_scheduler';
|
|
5
|
+
import { editInPlace, InPlaceEdit } from './inplace';
|
|
6
|
+
|
|
7
|
+
export const width = 150;
|
|
8
|
+
export const height = 25;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Where the pointer is, in canvas pixels, and whether it is over the widget.
|
|
12
|
+
* A press that starts on a widget keeps reporting to it until it is let go,
|
|
13
|
+
* wherever the pointer wanders.
|
|
14
|
+
*/
|
|
15
|
+
export interface WidgetPointer {
|
|
16
|
+
position: Vector2;
|
|
17
|
+
inside: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface Widget {
|
|
21
|
+
/**
|
|
22
|
+
* The smallest room the widget needs, in graph units. The node may hand
|
|
23
|
+
* it a wider box.
|
|
24
|
+
*/
|
|
25
|
+
Size(): Vector2;
|
|
26
|
+
|
|
27
|
+
Draw(ctx: CanvasRenderingContext2D, box: Box, scale: number, mousePosition: Vector2 | undefined): void;
|
|
28
|
+
|
|
29
|
+
PointerDown?(pointer: WidgetPointer): void;
|
|
30
|
+
PointerMove?(pointer: WidgetPointer): void;
|
|
31
|
+
PointerUp?(pointer: WidgetPointer): void;
|
|
32
|
+
|
|
33
|
+
Cursor?(): CursorStyle;
|
|
34
|
+
Hidden?(): boolean;
|
|
35
|
+
Disabled?(): boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export abstract class WidgetBase implements Widget {
|
|
39
|
+
|
|
40
|
+
#hidden = false;
|
|
41
|
+
|
|
42
|
+
#disabled = false;
|
|
43
|
+
|
|
44
|
+
#hovered = false;
|
|
45
|
+
|
|
46
|
+
#pressed = false;
|
|
47
|
+
|
|
48
|
+
#box: Box = { Position: { x: 0, y: 0 }, Size: { x: 0, y: 0 } };
|
|
49
|
+
|
|
50
|
+
#canvas: HTMLCanvasElement | null = null;
|
|
51
|
+
|
|
52
|
+
#scale = 1;
|
|
53
|
+
|
|
54
|
+
#editing: InPlaceEdit | null = null;
|
|
55
|
+
|
|
56
|
+
abstract Size(): Vector2;
|
|
57
|
+
|
|
58
|
+
protected abstract render(ctx: CanvasRenderingContext2D, box: Box, scale: number): void;
|
|
59
|
+
|
|
60
|
+
Draw(ctx: CanvasRenderingContext2D, box: Box, scale: number, mousePosition: Vector2 | undefined): void {
|
|
61
|
+
this.#box = { Position: { x: box.Position.x, y: box.Position.y }, Size: { x: box.Size.x, y: box.Size.y } };
|
|
62
|
+
this.#canvas = ctx.canvas ?? null;
|
|
63
|
+
this.#scale = scale;
|
|
64
|
+
this.#hovered = !this.#disabled && mousePosition !== undefined && InBox(box, mousePosition);
|
|
65
|
+
|
|
66
|
+
if (this.#editing !== null) {
|
|
67
|
+
if (this.#editing.input.isConnected) {
|
|
68
|
+
this.#editing.place(this.#box, scale);
|
|
69
|
+
} else {
|
|
70
|
+
this.#editing = null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
ctx.save();
|
|
75
|
+
if (this.#disabled) {
|
|
76
|
+
ctx.globalAlpha *= 0.4;
|
|
77
|
+
}
|
|
78
|
+
this.render(ctx, box, scale);
|
|
79
|
+
ctx.restore();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
PointerDown(pointer: WidgetPointer): void {
|
|
83
|
+
if (this.#disabled) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this.#pressed = true;
|
|
87
|
+
this.onPointerDown(pointer);
|
|
88
|
+
requestRender();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
PointerMove(pointer: WidgetPointer): void {
|
|
92
|
+
if (!this.#pressed) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
this.onPointerMove(pointer);
|
|
96
|
+
requestRender();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
PointerUp(pointer: WidgetPointer): void {
|
|
100
|
+
if (!this.#pressed) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
this.#pressed = false;
|
|
104
|
+
this.onPointerUp(pointer);
|
|
105
|
+
if (pointer.inside) {
|
|
106
|
+
this.onClick(pointer);
|
|
107
|
+
}
|
|
108
|
+
requestRender();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
Cursor(): CursorStyle {
|
|
112
|
+
return CursorStyle.Pointer;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
Hidden(): boolean {
|
|
116
|
+
return this.#hidden;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
setHidden(hidden: boolean): void {
|
|
120
|
+
this.#hidden = hidden;
|
|
121
|
+
requestRender();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
Disabled(): boolean {
|
|
125
|
+
return this.#disabled;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
setDisabled(disabled: boolean): void {
|
|
129
|
+
this.#disabled = disabled;
|
|
130
|
+
if (disabled) {
|
|
131
|
+
this.#pressed = false;
|
|
132
|
+
}
|
|
133
|
+
requestRender();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
protected hovered(): boolean {
|
|
137
|
+
return this.#hovered;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
protected pressed(): boolean {
|
|
141
|
+
return this.#pressed;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Where the widget was last drawn, in canvas pixels.
|
|
146
|
+
*/
|
|
147
|
+
protected box(): Box {
|
|
148
|
+
return this.#box;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
protected canvas(): HTMLCanvasElement | null {
|
|
152
|
+
return this.#canvas;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
protected scale(): number {
|
|
156
|
+
return this.#scale;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
protected editText(value: string, onCommit: (text: string) => void): void {
|
|
160
|
+
this.#editing = editInPlace({
|
|
161
|
+
canvas: this.#canvas,
|
|
162
|
+
box: this.#box,
|
|
163
|
+
scale: this.#scale,
|
|
164
|
+
value,
|
|
165
|
+
onCommit,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
protected onPointerDown(pointer: WidgetPointer): void { }
|
|
170
|
+
|
|
171
|
+
protected onPointerMove(pointer: WidgetPointer): void { }
|
|
172
|
+
|
|
173
|
+
protected onPointerUp(pointer: WidgetPointer): void { }
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* A press and release that both landed on the widget.
|
|
177
|
+
*/
|
|
178
|
+
protected onClick(pointer: WidgetPointer): void { }
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
interface PropertyReader {
|
|
182
|
+
getProperty(name: string): any;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function startingWidgetValue<T>(
|
|
186
|
+
node: PropertyReader,
|
|
187
|
+
property: string | undefined,
|
|
188
|
+
configured: T | undefined,
|
|
189
|
+
fallback: T,
|
|
190
|
+
): T {
|
|
191
|
+
if (configured !== undefined) {
|
|
192
|
+
return configured;
|
|
193
|
+
}
|
|
194
|
+
if (property !== undefined && property !== null) {
|
|
195
|
+
const existing = node.getProperty(property);
|
|
196
|
+
if (existing !== undefined && existing !== null) {
|
|
197
|
+
return existing as T;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return fallback;
|
|
201
|
+
}
|
package/test/support.ts
CHANGED
|
@@ -94,7 +94,7 @@ export interface StubElement {
|
|
|
94
94
|
tagName: string;
|
|
95
95
|
type: string;
|
|
96
96
|
value: string;
|
|
97
|
-
style: { cssText: string };
|
|
97
|
+
style: { cssText: string, opacity?: string, pointerEvents?: string };
|
|
98
98
|
children: Array<StubElement>;
|
|
99
99
|
isConnected: boolean;
|
|
100
100
|
clicked: number;
|
|
@@ -115,6 +115,19 @@ export interface StubElement {
|
|
|
115
115
|
fire(name: string, event?: any): void;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
function stubStyle(): StubElement['style'] {
|
|
119
|
+
const style: any = {};
|
|
120
|
+
let css = '';
|
|
121
|
+
Object.defineProperty(style, 'cssText', {
|
|
122
|
+
get: () => css,
|
|
123
|
+
set: (value: string) => {
|
|
124
|
+
css = value;
|
|
125
|
+
Object.keys(style).forEach((key) => delete style[key]);
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
return style;
|
|
129
|
+
}
|
|
130
|
+
|
|
118
131
|
export function withDocument(run: (body: StubElement, created: Array<StubElement>) => void): void {
|
|
119
132
|
const created: Array<StubElement> = [];
|
|
120
133
|
const element = (tagName: string): StubElement => {
|
|
@@ -123,7 +136,7 @@ export function withDocument(run: (body: StubElement, created: Array<StubElement
|
|
|
123
136
|
tagName,
|
|
124
137
|
type: '',
|
|
125
138
|
value: '',
|
|
126
|
-
style:
|
|
139
|
+
style: stubStyle(),
|
|
127
140
|
children: [],
|
|
128
141
|
isConnected: false,
|
|
129
142
|
clicked: 0,
|