@elicdavis/node-flow 0.1.7 → 0.1.9

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/index.html CHANGED
@@ -296,6 +296,15 @@ node.addOutput({ name: "sum", type: "float32" })
296
296
  outputs: [
297
297
  { name: "sum", type: "float32", description: "The sum of all connected nodes to our 'numbers' port" }
298
298
  ],
299
+ messages: [
300
+ {
301
+ message: "I am a message attatched to the node"
302
+ },
303
+ {
304
+ message: "I am another message attatched to the node with type \"error\"",
305
+ type: "error"
306
+ }
307
+ ],
299
308
  });
300
309
 
301
310
  graph.addNode(sumNode)
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@elicdavis/node-flow",
3
3
  "description": "build node graphs",
4
4
  "author": "Eli C Davis",
5
- "version": "0.1.7",
5
+ "version": "0.1.9",
6
6
  "license": "MIT",
7
7
  "keywords": [
8
8
  "nodes",
package/src/graph.ts CHANGED
@@ -21,7 +21,7 @@ import { QuickMenu } from './quickMenu';
21
21
  export type GraphRenderer = (canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, position: Vector2, scale: number) => void;
22
22
 
23
23
  function BuildBackgroundRenderer(backgroundColor: string): GraphRenderer {
24
- return (canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, position: Vector2, scale: number) => {
24
+ return (canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, position: Vector2, scale: number): void => {
25
25
  context.fillStyle = backgroundColor;
26
26
  context.fillRect(0, 0, canvas.width, canvas.height);
27
27
 
package/src/index.ts CHANGED
@@ -1,7 +1,5 @@
1
- import { NodeFlowGraph } from './graph';
2
- import { FlowNode, FlowNodeConfig } from './node';
3
-
4
- export { NodeFlowGraph, FlowNode, FlowNodeConfig };
1
+ export * from './graph';
2
+ export * from './node';
5
3
 
6
4
  import { Theme } from './theme';
7
5
  export { Theme };
package/src/node.ts CHANGED
@@ -56,11 +56,39 @@ export interface FlowNodeStyle {
56
56
  portText?: TextStyleConfig;
57
57
  }
58
58
 
59
+ export enum MessageType {
60
+ Info = "info",
61
+ Warning = "warning",
62
+ Error = "error"
63
+ };
64
+
65
+ function MessageTypeColor(messageType: MessageType): string {
66
+ switch (messageType) {
67
+ case MessageType.Info:
68
+ return Theme.Node.Message.InfoColor;
69
+
70
+ case MessageType.Warning:
71
+ return Theme.Node.Message.WarnColor;
72
+
73
+ case MessageType.Error:
74
+ return Theme.Node.Message.ErrorColor;
75
+
76
+ default:
77
+ throw new Error(`unrecognized message type ${messageType}`)
78
+ }
79
+ }
80
+
81
+ export interface NodeMessageConfig {
82
+ message: string;
83
+ type?: MessageType;
84
+ }
85
+
59
86
  export interface FlowNodeConfig {
60
87
  position?: Vector2;
61
88
  title?: string;
62
89
  subTitle?: string;
63
90
  info?: string;
91
+ messages?: Array<NodeMessageConfig>
64
92
  locked?: boolean;
65
93
  data?: Metadata;
66
94
  contextMenu?: ContextMenuConfig;
@@ -111,6 +139,29 @@ export interface NodeIntersection {
111
139
 
112
140
  }
113
141
 
142
+ class MessageRenderer {
143
+
144
+ #text: Text;
145
+
146
+ constructor(config: NodeMessageConfig) {
147
+ this.#text = new Text(config.message,
148
+ {
149
+ color: MessageTypeColor(config.type ? config.type : MessageType.Info),
150
+ },
151
+ {
152
+ LineSpacing: 2.5,
153
+ MaxWidth: 200
154
+ }
155
+ );
156
+ }
157
+
158
+ render(ctx: CanvasRenderingContext2D, scale: number, position: Vector2): number {
159
+ ctx.textAlign = TextAlign.Center;
160
+ this.#text.render(ctx, scale, position);
161
+ return this.#text.height(ctx) * scale;
162
+ }
163
+ }
164
+
114
165
  export class FlowNode {
115
166
 
116
167
  #position: Vector2;
@@ -123,6 +174,8 @@ export class FlowNode {
123
174
 
124
175
  #infoText: string;
125
176
 
177
+ #messages: Array<MessageRenderer>
178
+
126
179
  #input: Array<Port>;
127
180
 
128
181
  #output: Array<Port>;
@@ -203,6 +256,14 @@ export class FlowNode {
203
256
  this.#contextMenu = config?.contextMenu === undefined ? null : config.contextMenu;
204
257
  this.#metadata = config?.metadata;
205
258
 
259
+ this.#messages = [];
260
+ if (config?.messages) {
261
+ for (let i = 0; i < config.messages.length; i++) {
262
+ const message = config.messages[i];
263
+ this.#messages.push(new MessageRenderer(message));
264
+ }
265
+ }
266
+
206
267
  this.#selected = false;
207
268
  this.#onSelect = new Array<() => void>();
208
269
  this.#onUnselect = new Array<() => void>();
@@ -879,7 +940,6 @@ export class FlowNode {
879
940
  console.warn("setInfo instruction ignored, as node has been marked un-editable");
880
941
  }
881
942
 
882
-
883
943
  let cleaned = newInfo;
884
944
  if (cleaned === null || cleaned === undefined) {
885
945
  cleaned = "";
@@ -935,6 +995,14 @@ export class FlowNode {
935
995
  return boxStyle;
936
996
  }
937
997
 
998
+ addMessage(message: NodeMessageConfig): void {
999
+ this.#messages.push(new MessageRenderer(message));
1000
+ }
1001
+
1002
+ clearMessages(): void {
1003
+ this.#messages = [];
1004
+ }
1005
+
938
1006
  render(ctx: CanvasRenderingContext2D, camera: Camera, state: NodeState, mousePosition: Vector2 | undefined, postProcess: PassSubsystem): void {
939
1007
  VectorPool.run(() => {
940
1008
  const tempMeasurement = VectorPool.get();
@@ -1113,6 +1181,7 @@ export class FlowNode {
1113
1181
  startY += tempMeasurement.y + scaledElementSpacing;
1114
1182
  }
1115
1183
 
1184
+ // Widgets
1116
1185
  for (let i = 0; i < this.#widgets.length; i++) {
1117
1186
  const widget = this.#widgets[i];
1118
1187
  const widgetSize = widget.Size();
@@ -1125,6 +1194,15 @@ export class FlowNode {
1125
1194
  this.#widgetPositions.Push(widget.Draw(ctx, position, camera.zoom, mousePosition));
1126
1195
  startY += (widgetSize.y * camera.zoom) + scaledElementSpacing;
1127
1196
  }
1197
+
1198
+ // Messages
1199
+ const messageStart = VectorPool.get();
1200
+ messageStart.x = nodeBounds.Position.x + (nodeBounds.Size.x / 2);
1201
+ messageStart.y = nodeBounds.Position.y + nodeBounds.Size.y + (15 * camera.zoom);
1202
+ for (let i = 0; i < this.#messages.length; i++) {
1203
+ const message = this.#messages[i];
1204
+ messageStart.y += message.render(ctx, camera.zoom, messageStart) + (10 * camera.zoom);
1205
+ }
1128
1206
  })
1129
1207
  }
1130
1208
  }
package/src/theme.ts CHANGED
@@ -20,6 +20,11 @@ export const Theme = {
20
20
  },
21
21
  Port: {
22
22
  FontColor: "#afb9bb"
23
+ },
24
+ Message: {
25
+ InfoColor: "#afb9bb",
26
+ WarnColor: "#f1f663ff",
27
+ ErrorColor: "#FF0000"
23
28
  }
24
29
  },
25
30
  BoxSelect: {
package/src/types/text.ts CHANGED
@@ -2,6 +2,11 @@ import { FontWeight, TextStyle, TextStyleConfig } from "../styles/text";
2
2
  import { splitString, splitStringIntoLines } from "../utils/string";
3
3
  import { CopyVector2, ScaleVector, Vector2, Zero } from "./vector2";
4
4
 
5
+ export interface MultilineTextConfig {
6
+ MaxWidth?: number;
7
+ LineSpacing?: number;
8
+ }
9
+
5
10
  export class Text {
6
11
 
7
12
  #measured: boolean
@@ -12,11 +17,20 @@ export class Text {
12
17
 
13
18
  #value: string
14
19
 
15
- constructor(value: string, style?: TextStyleConfig) {
20
+ #maxWidth: number;
21
+
22
+ #lineSpacing: number;
23
+
24
+ #textToRender: Array<string>;
25
+
26
+ constructor(value: string, style?: TextStyleConfig, config?: MultilineTextConfig) {
16
27
  this.#value = value;
17
28
  this.#measured = false;
18
29
  this.#size = Zero();
19
30
  this.#style = new TextStyle(style);
31
+ this.#maxWidth = config?.MaxWidth ? config?.MaxWidth : -1;
32
+ this.#lineSpacing = config?.LineSpacing ? config?.LineSpacing : 5;
33
+ this.#textToRender = [value];
20
34
 
21
35
  if (!document.fonts.check(`16px "${this.#style.getFont()}"`)) {
22
36
  document.fonts.addEventListener("loadingdone", (event) => {
@@ -96,10 +110,28 @@ export class Text {
96
110
  return;
97
111
  }
98
112
 
113
+
99
114
  this.#style.setupStyle(ctx, 1);
100
- const measurements = ctx.measureText(this.#value);
101
- this.#size.x = measurements.width;
102
- this.#size.y = measurements.actualBoundingBoxAscent + measurements.actualBoundingBoxDescent;
115
+
116
+ if (this.#maxWidth == -1) {
117
+ const measurements = ctx.measureText(this.#value);
118
+ this.#size.x = measurements.width;
119
+ this.#size.y = measurements.actualBoundingBoxAscent + measurements.actualBoundingBoxDescent;
120
+ this.#textToRender = [this.#value];
121
+ this.#measured = true;
122
+ return;
123
+ }
124
+
125
+ this.#textToRender = splitStringIntoLines(ctx, this.#value, this.#maxWidth);
126
+
127
+ this.#size.x = 0;
128
+ this.#size.y = 0;
129
+ for (let i = 0; i < this.#textToRender.length; i++) {
130
+ const measurements = ctx.measureText(this.#textToRender[i]);
131
+ this.#size.x = Math.max(this.#size.x, measurements.width);
132
+ }
133
+
134
+ this.#size.y += ((this.#textToRender.length - 1) * this.#lineSpacing) + (this.#style.getSize() * this.#textToRender.length);
103
135
  this.#measured = true;
104
136
  }
105
137
 
@@ -109,10 +141,15 @@ export class Text {
109
141
  ScaleVector(out, scale);
110
142
  }
111
143
 
144
+ height(ctx: CanvasRenderingContext2D): number {
145
+ this.#measure(ctx);
146
+ return this.#size.y;
147
+ }
148
+
112
149
  setColor(color: string): void {
113
150
  this.#style.setColor(color);
114
151
  }
115
-
152
+
116
153
  getColor(): string {
117
154
  return this.#style.getColor();
118
155
  }
@@ -126,8 +163,17 @@ export class Text {
126
163
  }
127
164
 
128
165
  render(ctx: CanvasRenderingContext2D, scale: number, position: Vector2): void {
129
- this.#style.setupStyle(ctx, scale)
130
- ctx.fillText(this.#value, position.x, position.y);
166
+ this.#measure(ctx);
167
+ this.#style.setupStyle(ctx, scale);
168
+ let yOffset = 0;
169
+ for (let i = 0; i < this.#textToRender.length; i++) {
170
+ ctx.fillText(
171
+ this.#textToRender[i],
172
+ position.x,
173
+ position.y + yOffset
174
+ );
175
+ yOffset += (this.#style.getSize() + this.#lineSpacing) * scale
176
+ }
131
177
  }
132
178
 
133
179
  style(): TextStyle {