@arronqzy/react-blueprint 1.0.6 → 1.0.8
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/package.json +3 -3
- package/src/BlueprintContextMenu.tsx +1 -1
- package/src/BlueprintNodeConfigSidebar.tsx +115 -28
- package/src/blueprintNodeTypes.ts +2 -0
- package/src/components/ClockNodeConfigPanel.tsx +17 -24
- package/src/components/ConfigHintIcon.tsx +83 -0
- package/src/components/FetchNodeConfigPanel.tsx +204 -32
- package/src/components/FetchUrlAutocomplete.tsx +1 -0
- package/src/components/JsonNodeConfigPanel.tsx +5 -6
- package/src/components/LogicNodeConfigPanel.tsx +5 -6
- package/src/components/ScopeTemplateAutocompleteHost.tsx +245 -0
- package/src/graph/blueprint-graph.ts +8 -0
- package/src/graph/document.ts +136 -8
- package/src/graph/node-summary.ts +20 -0
- package/src/graph/node-types.ts +1 -0
- package/src/graph/react-flow-adapter.ts +4 -0
- package/src/graph/sync-nodes.ts +2 -1
- package/src/hooks/useBlueprintFlowState.ts +40 -7
- package/src/hooks/useBlueprintPageLifecycle.ts +106 -14
- package/src/index.tsx +2 -0
- package/src/nodes/BlueprintNodeCard.tsx +12 -2
- package/src/nodes/BlueprintNodeShell.tsx +6 -2
- package/src/nodes/EventFlowNode.tsx +31 -0
- package/src/runtime/document-to-runnable-graph.ts +3 -0
package/src/graph/document.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type {
|
|
|
4
4
|
JsonNodeConfig,
|
|
5
5
|
LogicNodeConfig,
|
|
6
6
|
PageLifecyclePhase,
|
|
7
|
+
ViewEventNodeConfig,
|
|
8
|
+
ViewEventType,
|
|
7
9
|
} from "@arronqzy/blueprint-dsl";
|
|
8
10
|
import {
|
|
9
11
|
BLUEPRINT_NODE_TYPE,
|
|
@@ -14,10 +16,13 @@ import {
|
|
|
14
16
|
DEFAULT_JSON_NODE_CONFIG,
|
|
15
17
|
DEFAULT_LOGIC_NODE_CONFIG,
|
|
16
18
|
DEFAULT_LOGIC_NODE_TYPE,
|
|
19
|
+
DEFAULT_VIEW_EVENT_NODE_CONFIG,
|
|
20
|
+
EVENT_NODE_TYPE,
|
|
17
21
|
FETCH_NODE_TYPE,
|
|
18
22
|
JSON_NODE_TYPE,
|
|
19
23
|
LIFECYCLE_NODE_TYPE,
|
|
20
24
|
normalizeClockConfig,
|
|
25
|
+
normalizeViewEventConfig,
|
|
21
26
|
VIEW_NODE_TYPE,
|
|
22
27
|
} from "@arronqzy/blueprint-dsl";
|
|
23
28
|
|
|
@@ -25,8 +30,9 @@ export type { FetchRequestConfig as BlueprintFetchConfig } from "@arronqzy/bluep
|
|
|
25
30
|
export type { JsonNodeConfig as BlueprintJsonConfig } from "@arronqzy/blueprint-dsl";
|
|
26
31
|
export type { LogicNodeConfig as BlueprintLogicConfig } from "@arronqzy/blueprint-dsl";
|
|
27
32
|
export type { ClockNodeConfig as BlueprintClockConfig } from "@arronqzy/blueprint-dsl";
|
|
33
|
+
export type { ViewEventNodeConfig as BlueprintEventConfig } from "@arronqzy/blueprint-dsl";
|
|
28
34
|
|
|
29
|
-
export type BlueprintNodeRole = "blueprint" | "logic" | "and" | "lifecycle" | "fetch" | "json" | "clock";
|
|
35
|
+
export type BlueprintNodeRole = "blueprint" | "logic" | "and" | "lifecycle" | "fetch" | "json" | "clock" | "event";
|
|
30
36
|
|
|
31
37
|
/** 决定右侧配置面板展示哪类配置 */
|
|
32
38
|
export type BlueprintConfigSource =
|
|
@@ -37,7 +43,8 @@ export type BlueprintConfigSource =
|
|
|
37
43
|
| "view"
|
|
38
44
|
| "fetch"
|
|
39
45
|
| "json"
|
|
40
|
-
| "clock"
|
|
46
|
+
| "clock"
|
|
47
|
+
| "event";
|
|
41
48
|
|
|
42
49
|
export type BlueprintGraphNode = {
|
|
43
50
|
id: string;
|
|
@@ -68,6 +75,8 @@ export type BlueprintGraphNode = {
|
|
|
68
75
|
logicConfig?: LogicNodeConfig;
|
|
69
76
|
/** 时钟节点的间隔与时间格式配置 */
|
|
70
77
|
clockConfig?: ClockNodeConfig;
|
|
78
|
+
/** 视图事件节点监听的事件类型 */
|
|
79
|
+
eventConfig?: ViewEventNodeConfig;
|
|
71
80
|
};
|
|
72
81
|
|
|
73
82
|
export function resolveViewElementIds(
|
|
@@ -94,6 +103,7 @@ export function resolveBlueprintConfigSource(
|
|
|
94
103
|
): BlueprintConfigSource {
|
|
95
104
|
if (node.configSource) return node.configSource;
|
|
96
105
|
if (node.role === "lifecycle") return "lifecycle";
|
|
106
|
+
if (node.role === "event") return "event";
|
|
97
107
|
if (node.role === "clock") return "clock";
|
|
98
108
|
if (node.role === "and") return "and";
|
|
99
109
|
if (node.role === "fetch") return "fetch";
|
|
@@ -101,11 +111,60 @@ export function resolveBlueprintConfigSource(
|
|
|
101
111
|
if (node.nodeType === FETCH_NODE_TYPE) return "fetch";
|
|
102
112
|
if (node.nodeType === JSON_NODE_TYPE) return "json";
|
|
103
113
|
if (node.nodeType === CLOCK_NODE_TYPE) return "clock";
|
|
114
|
+
if (node.nodeType === EVENT_NODE_TYPE) return "event";
|
|
104
115
|
if (node.nodeType === VIEW_NODE_TYPE) return "view";
|
|
105
116
|
if (resolveViewElementIds(node).length > 0) return "view";
|
|
106
117
|
return node.role === "logic" ? "logic" : "blueprint";
|
|
107
118
|
}
|
|
108
119
|
|
|
120
|
+
export type BlueprintConfigSourceNode = Pick<
|
|
121
|
+
BlueprintGraphNode,
|
|
122
|
+
"role" | "nodeType" | "configSource" | "viewElementId" | "viewElementIds"
|
|
123
|
+
>;
|
|
124
|
+
|
|
125
|
+
export type BlueprintConnectionIssue =
|
|
126
|
+
| "lifecycle-no-input"
|
|
127
|
+
| "event-requires-lifecycle";
|
|
128
|
+
|
|
129
|
+
/** 连入校验:生命周期无输入;事件节点输入必须全部来自生命周期节点 */
|
|
130
|
+
export function resolveBlueprintConnectionIssue(
|
|
131
|
+
source: BlueprintConfigSourceNode | undefined,
|
|
132
|
+
target: BlueprintConfigSourceNode | undefined
|
|
133
|
+
): BlueprintConnectionIssue | null {
|
|
134
|
+
if (!source || !target) return null;
|
|
135
|
+
const targetSource = resolveBlueprintConfigSource(target);
|
|
136
|
+
if (targetSource === "lifecycle") return "lifecycle-no-input";
|
|
137
|
+
if (
|
|
138
|
+
targetSource === "event" &&
|
|
139
|
+
resolveBlueprintConfigSource(source) !== "lifecycle"
|
|
140
|
+
) {
|
|
141
|
+
return "event-requires-lifecycle";
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function inspectEventNodeInputs(
|
|
147
|
+
nodeId: string,
|
|
148
|
+
nodes: Array<BlueprintConfigSourceNode & { id: string }>,
|
|
149
|
+
edges: Array<{ source: string; target: string }>
|
|
150
|
+
) {
|
|
151
|
+
const byId = new Map(nodes.map((node) => [node.id, node]));
|
|
152
|
+
let incomingCount = 0;
|
|
153
|
+
let lifecycleCount = 0;
|
|
154
|
+
let nonLifecycleCount = 0;
|
|
155
|
+
for (const edge of edges) {
|
|
156
|
+
if (edge.target !== nodeId) continue;
|
|
157
|
+
incomingCount += 1;
|
|
158
|
+
const source = byId.get(edge.source);
|
|
159
|
+
if (source && resolveRunnableNodeType(source) === LIFECYCLE_NODE_TYPE) {
|
|
160
|
+
lifecycleCount += 1;
|
|
161
|
+
} else {
|
|
162
|
+
nonLifecycleCount += 1;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return { incomingCount, lifecycleCount, nonLifecycleCount };
|
|
166
|
+
}
|
|
167
|
+
|
|
109
168
|
/** 可执行图节点类型:与配置类型对齐,避免文档里残留旧 nodeType */
|
|
110
169
|
export function resolveRunnableNodeType(
|
|
111
170
|
node: Pick<
|
|
@@ -116,6 +175,8 @@ export function resolveRunnableNodeType(
|
|
|
116
175
|
switch (resolveBlueprintConfigSource(node)) {
|
|
117
176
|
case "view":
|
|
118
177
|
return VIEW_NODE_TYPE;
|
|
178
|
+
case "event":
|
|
179
|
+
return EVENT_NODE_TYPE;
|
|
119
180
|
case "lifecycle":
|
|
120
181
|
return LIFECYCLE_NODE_TYPE;
|
|
121
182
|
case "clock":
|
|
@@ -146,6 +207,7 @@ export const BLUEPRINT_CONFIG_TYPE_LABEL_KEYS: Record<
|
|
|
146
207
|
and: "blueprint.node.typeAnd",
|
|
147
208
|
lifecycle: "blueprint.node.typeLifecycle",
|
|
148
209
|
view: "blueprint.node.typeView",
|
|
210
|
+
event: "blueprint.node.typeEvent",
|
|
149
211
|
fetch: "blueprint.node.typeFetch",
|
|
150
212
|
json: "blueprint.node.typeJson",
|
|
151
213
|
clock: "blueprint.node.typeClock",
|
|
@@ -164,6 +226,14 @@ export const BLUEPRINT_LIFECYCLE_PHASE_KEYS: Record<PageLifecyclePhase, string>
|
|
|
164
226
|
blueprintActivated: "blueprint.node.phaseBlueprintActivated",
|
|
165
227
|
};
|
|
166
228
|
|
|
229
|
+
export const BLUEPRINT_VIEW_EVENT_TYPE_KEYS: Record<ViewEventType, string> = {
|
|
230
|
+
click: "blueprint.node.eventClick",
|
|
231
|
+
dblclick: "blueprint.node.eventDblclick",
|
|
232
|
+
contextmenu: "blueprint.node.eventContextmenu",
|
|
233
|
+
mouseenter: "blueprint.node.eventMouseenter",
|
|
234
|
+
mouseleave: "blueprint.node.eventMouseleave",
|
|
235
|
+
};
|
|
236
|
+
|
|
167
237
|
function defaultTranslate(): TranslateFn {
|
|
168
238
|
return tForLocale(resolveLocale());
|
|
169
239
|
}
|
|
@@ -183,6 +253,11 @@ export function getLifecyclePhaseLabel(
|
|
|
183
253
|
return key ? t(key) : phase;
|
|
184
254
|
}
|
|
185
255
|
|
|
256
|
+
export function getViewEventTypeLabel(t: TranslateFn, eventType: ViewEventType): string {
|
|
257
|
+
const key = BLUEPRINT_VIEW_EVENT_TYPE_KEYS[eventType];
|
|
258
|
+
return key ? t(key) : eventType;
|
|
259
|
+
}
|
|
260
|
+
|
|
186
261
|
export function resolveBlueprintNodeTypeLabel(
|
|
187
262
|
node: Pick<
|
|
188
263
|
BlueprintGraphNode,
|
|
@@ -211,6 +286,7 @@ export function resolveNodeFetchConfig(
|
|
|
211
286
|
...DEFAULT_FETCH_REQUEST_CONFIG.headers,
|
|
212
287
|
...node.fetchConfig?.headers,
|
|
213
288
|
},
|
|
289
|
+
headersJson: node.fetchConfig?.headersJson,
|
|
214
290
|
swaggerEndpoints: node.fetchConfig?.swaggerEndpoints,
|
|
215
291
|
};
|
|
216
292
|
}
|
|
@@ -242,6 +318,15 @@ export function resolveNodeClockConfig(
|
|
|
242
318
|
});
|
|
243
319
|
}
|
|
244
320
|
|
|
321
|
+
export function resolveNodeEventConfig(
|
|
322
|
+
node: Pick<BlueprintGraphNode, "eventConfig">
|
|
323
|
+
): ViewEventNodeConfig {
|
|
324
|
+
return normalizeViewEventConfig({
|
|
325
|
+
...DEFAULT_VIEW_EVENT_NODE_CONFIG,
|
|
326
|
+
...node.eventConfig,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
245
330
|
export type BlueprintGraphEdge = {
|
|
246
331
|
id: string;
|
|
247
332
|
source: string;
|
|
@@ -274,12 +359,18 @@ const noInputTargetNodeIds = (document: BlueprintDocument) =>
|
|
|
274
359
|
.map((node) => node.id)
|
|
275
360
|
);
|
|
276
361
|
|
|
277
|
-
|
|
362
|
+
function isLifecycleSourceNode(node: BlueprintGraphNode | undefined): boolean {
|
|
363
|
+
return Boolean(node) && resolveRunnableNodeType(node!) === LIFECYCLE_NODE_TYPE;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function isEventTargetNode(node: BlueprintGraphNode | undefined): boolean {
|
|
367
|
+
return Boolean(node) && resolveBlueprintConfigSource(node!) === "event";
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** 生命周期节点无输入口;事件节点输入必须来自生命周期节点 */
|
|
278
371
|
export function sanitizeBlueprintDocument(
|
|
279
372
|
document: BlueprintDocument
|
|
280
373
|
): BlueprintDocument {
|
|
281
|
-
const blockedTargets = noInputTargetNodeIds(document);
|
|
282
|
-
|
|
283
374
|
const nodes = document.nodes.map((node) => {
|
|
284
375
|
let next = node;
|
|
285
376
|
if (node.viewElementId && !node.viewElementIds?.length) {
|
|
@@ -296,6 +387,9 @@ export function sanitizeBlueprintDocument(
|
|
|
296
387
|
if (next.role === "lifecycle") {
|
|
297
388
|
return { ...next, configSource: "lifecycle" as const };
|
|
298
389
|
}
|
|
390
|
+
if (next.role === "event") {
|
|
391
|
+
return { ...next, configSource: "event" as const };
|
|
392
|
+
}
|
|
299
393
|
if (next.role === "clock") {
|
|
300
394
|
return { ...next, configSource: "clock" as const };
|
|
301
395
|
}
|
|
@@ -308,13 +402,16 @@ export function sanitizeBlueprintDocument(
|
|
|
308
402
|
if (next.role === "and") {
|
|
309
403
|
return { ...next, configSource: "and" as const };
|
|
310
404
|
}
|
|
311
|
-
if (next.configSource === "lifecycle" || next.configSource === "clock") {
|
|
405
|
+
if (next.configSource === "lifecycle" || next.configSource === "clock" || next.configSource === "event") {
|
|
312
406
|
return { ...next, configSource: undefined };
|
|
313
407
|
}
|
|
314
408
|
return next;
|
|
315
409
|
});
|
|
316
410
|
|
|
317
|
-
const edges =
|
|
411
|
+
const edges = filterInvalidBlueprintEdges(
|
|
412
|
+
{ ...document, nodes },
|
|
413
|
+
document.edges
|
|
414
|
+
);
|
|
318
415
|
|
|
319
416
|
return { ...document, nodes, edges };
|
|
320
417
|
}
|
|
@@ -324,7 +421,15 @@ export function filterInvalidBlueprintEdges(
|
|
|
324
421
|
edges: BlueprintGraphEdge[]
|
|
325
422
|
): BlueprintGraphEdge[] {
|
|
326
423
|
const blockedTargets = noInputTargetNodeIds(document);
|
|
327
|
-
|
|
424
|
+
const byId = new Map(document.nodes.map((node) => [node.id, node]));
|
|
425
|
+
return edges.filter((edge) => {
|
|
426
|
+
if (blockedTargets.has(edge.target)) return false;
|
|
427
|
+
const target = byId.get(edge.target);
|
|
428
|
+
if (isEventTargetNode(target)) {
|
|
429
|
+
return isLifecycleSourceNode(byId.get(edge.source));
|
|
430
|
+
}
|
|
431
|
+
return true;
|
|
432
|
+
});
|
|
328
433
|
}
|
|
329
434
|
|
|
330
435
|
/** 切换配置类型时同步节点 role / nodeType,确保生命周期节点渲染为无输入口 */
|
|
@@ -346,6 +451,7 @@ export function patchNodeConfigSource(
|
|
|
346
451
|
jsonConfig: undefined,
|
|
347
452
|
logicConfig: undefined,
|
|
348
453
|
clockConfig: undefined,
|
|
454
|
+
eventConfig: undefined,
|
|
349
455
|
};
|
|
350
456
|
}
|
|
351
457
|
|
|
@@ -362,6 +468,7 @@ export function patchNodeConfigSource(
|
|
|
362
468
|
fetchConfig: undefined,
|
|
363
469
|
jsonConfig: undefined,
|
|
364
470
|
logicConfig: undefined,
|
|
471
|
+
eventConfig: undefined,
|
|
365
472
|
};
|
|
366
473
|
}
|
|
367
474
|
|
|
@@ -378,6 +485,7 @@ export function patchNodeConfigSource(
|
|
|
378
485
|
jsonConfig: undefined,
|
|
379
486
|
logicConfig: undefined,
|
|
380
487
|
clockConfig: undefined,
|
|
488
|
+
eventConfig: undefined,
|
|
381
489
|
};
|
|
382
490
|
}
|
|
383
491
|
|
|
@@ -394,6 +502,7 @@ export function patchNodeConfigSource(
|
|
|
394
502
|
jsonConfig: resolveNodeJsonConfig(node),
|
|
395
503
|
logicConfig: undefined,
|
|
396
504
|
clockConfig: undefined,
|
|
505
|
+
eventConfig: undefined,
|
|
397
506
|
};
|
|
398
507
|
}
|
|
399
508
|
|
|
@@ -410,6 +519,7 @@ export function patchNodeConfigSource(
|
|
|
410
519
|
jsonConfig: undefined,
|
|
411
520
|
logicConfig: undefined,
|
|
412
521
|
clockConfig: undefined,
|
|
522
|
+
eventConfig: undefined,
|
|
413
523
|
};
|
|
414
524
|
}
|
|
415
525
|
|
|
@@ -426,6 +536,7 @@ export function patchNodeConfigSource(
|
|
|
426
536
|
jsonConfig: undefined,
|
|
427
537
|
logicConfig: resolveNodeLogicConfig(node),
|
|
428
538
|
clockConfig: undefined,
|
|
539
|
+
eventConfig: undefined,
|
|
429
540
|
};
|
|
430
541
|
}
|
|
431
542
|
|
|
@@ -442,6 +553,22 @@ export function patchNodeConfigSource(
|
|
|
442
553
|
jsonConfig: undefined,
|
|
443
554
|
logicConfig: undefined,
|
|
444
555
|
clockConfig: undefined,
|
|
556
|
+
eventConfig: undefined,
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (configSource === "event") {
|
|
561
|
+
return {
|
|
562
|
+
role: "event",
|
|
563
|
+
nodeType: EVENT_NODE_TYPE,
|
|
564
|
+
configSource: "event",
|
|
565
|
+
eventConfig: resolveNodeEventConfig(node),
|
|
566
|
+
lifecyclePhase: undefined,
|
|
567
|
+
libraryBlueprintId: undefined,
|
|
568
|
+
fetchConfig: undefined,
|
|
569
|
+
jsonConfig: undefined,
|
|
570
|
+
logicConfig: undefined,
|
|
571
|
+
clockConfig: undefined,
|
|
445
572
|
};
|
|
446
573
|
}
|
|
447
574
|
|
|
@@ -455,5 +582,6 @@ export function patchNodeConfigSource(
|
|
|
455
582
|
jsonConfig: undefined,
|
|
456
583
|
logicConfig: undefined,
|
|
457
584
|
clockConfig: undefined,
|
|
585
|
+
eventConfig: undefined,
|
|
458
586
|
};
|
|
459
587
|
}
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
resolveNodeJsonConfig,
|
|
11
11
|
resolveNodeLogicConfig,
|
|
12
12
|
resolveViewElementIds,
|
|
13
|
+
resolveNodeEventConfig,
|
|
14
|
+
getViewEventTypeLabel,
|
|
13
15
|
} from "./document";
|
|
14
16
|
import type { BlueprintFlowNodeData } from "./react-flow-adapter";
|
|
15
17
|
|
|
@@ -94,6 +96,24 @@ export function resolveBlueprintNodeSummary(
|
|
|
94
96
|
}
|
|
95
97
|
return t("blueprint.node.summaryViewMany", { count: ids.length });
|
|
96
98
|
}
|
|
99
|
+
case "event": {
|
|
100
|
+
const ids = resolveViewElementIds(data);
|
|
101
|
+
const types = resolveNodeEventConfig(data).eventTypes;
|
|
102
|
+
const typeLabel = types.map((type) => getViewEventTypeLabel(t, type)).join(" / ");
|
|
103
|
+
if (ids.length === 0) {
|
|
104
|
+
return truncate(
|
|
105
|
+
t("blueprint.node.summaryEventNoView", { events: typeLabel }),
|
|
106
|
+
44
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return truncate(
|
|
110
|
+
t("blueprint.node.summaryEvent", {
|
|
111
|
+
events: typeLabel,
|
|
112
|
+
count: ids.length,
|
|
113
|
+
}),
|
|
114
|
+
44
|
|
115
|
+
);
|
|
116
|
+
}
|
|
97
117
|
case "and":
|
|
98
118
|
return t("blueprint.node.summaryAnd");
|
|
99
119
|
default:
|
package/src/graph/node-types.ts
CHANGED
|
@@ -20,6 +20,7 @@ export type BlueprintFlowNodeData = {
|
|
|
20
20
|
fetchConfig?: BlueprintGraphNode["fetchConfig"];
|
|
21
21
|
jsonConfig?: BlueprintGraphNode["jsonConfig"];
|
|
22
22
|
clockConfig?: BlueprintGraphNode["clockConfig"];
|
|
23
|
+
eventConfig?: BlueprintGraphNode["eventConfig"];
|
|
23
24
|
logicConfig?: BlueprintGraphNode["logicConfig"];
|
|
24
25
|
/** 调试执行时时钟节点已发送次数 */
|
|
25
26
|
clockEmitProgress?: { current: number; total: number } | null;
|
|
@@ -70,6 +71,8 @@ export function toReactFlowNodes(document: BlueprintDocument): ReactFlowNodeView
|
|
|
70
71
|
? "fetch"
|
|
71
72
|
: n.role === "json"
|
|
72
73
|
? "json"
|
|
74
|
+
: n.role === "event"
|
|
75
|
+
? "event"
|
|
73
76
|
: "logic",
|
|
74
77
|
position: { ...n.position },
|
|
75
78
|
width: BP_FLOW_NODE_WIDTH,
|
|
@@ -91,6 +94,7 @@ export function toReactFlowNodes(document: BlueprintDocument): ReactFlowNodeView
|
|
|
91
94
|
fetchConfig: n.fetchConfig,
|
|
92
95
|
jsonConfig: n.jsonConfig,
|
|
93
96
|
clockConfig: n.clockConfig,
|
|
97
|
+
eventConfig: n.eventConfig,
|
|
94
98
|
logicConfig: n.logicConfig,
|
|
95
99
|
},
|
|
96
100
|
}));
|
package/src/graph/sync-nodes.ts
CHANGED
|
@@ -48,12 +48,13 @@ export function nodeStructureSignature(
|
|
|
48
48
|
jsonConfig?: { jsonString?: string };
|
|
49
49
|
logicConfig?: { sourceCode?: string };
|
|
50
50
|
clockConfig?: { intervalSeconds?: number; timeFormat?: string; outputCount?: number; emitImmediately?: boolean };
|
|
51
|
+
eventConfig?: { eventTypes?: string[] };
|
|
51
52
|
}>
|
|
52
53
|
): string {
|
|
53
54
|
return nodes
|
|
54
55
|
.map(
|
|
55
56
|
(n) =>
|
|
56
|
-
`${n.id}:${n.role}:${n.label}:${n.configSource ?? ""}:${n.lifecyclePhase ?? ""}:${n.libraryBlueprintId ?? ""}:${n.nodeType ?? ""}:${(n.viewElementIds ?? (n.viewElementId ? [n.viewElementId] : [])).join(",")}:${n.fetchConfig?.url ?? ""}:${n.fetchConfig?.method ?? ""}:${n.fetchConfig?.apiBaseUrl ?? ""}:${n.fetchConfig?.swaggerDocsUrl ?? ""}:${n.jsonConfig?.jsonString ?? ""}:${n.logicConfig?.sourceCode ?? ""}:${n.clockConfig?.intervalSeconds ?? ""}:${n.clockConfig?.timeFormat ?? ""}:${n.clockConfig?.outputCount ?? ""}:${n.clockConfig?.emitImmediately ?? ""}`
|
|
57
|
+
`${n.id}:${n.role}:${n.label}:${n.configSource ?? ""}:${n.lifecyclePhase ?? ""}:${n.libraryBlueprintId ?? ""}:${n.nodeType ?? ""}:${(n.viewElementIds ?? (n.viewElementId ? [n.viewElementId] : [])).join(",")}:${n.fetchConfig?.url ?? ""}:${n.fetchConfig?.method ?? ""}:${n.fetchConfig?.apiBaseUrl ?? ""}:${n.fetchConfig?.swaggerDocsUrl ?? ""}:${n.jsonConfig?.jsonString ?? ""}:${n.logicConfig?.sourceCode ?? ""}:${n.clockConfig?.intervalSeconds ?? ""}:${n.clockConfig?.timeFormat ?? ""}:${n.clockConfig?.outputCount ?? ""}:${n.clockConfig?.emitImmediately ?? ""}:${(n.eventConfig?.eventTypes ?? []).join(",")}`
|
|
57
58
|
)
|
|
58
59
|
.sort()
|
|
59
60
|
.join("|");
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/react";
|
|
3
|
+
import { toast } from "@arronqzy/ui";
|
|
2
4
|
import {
|
|
3
5
|
addEdge,
|
|
4
6
|
useEdgesState,
|
|
@@ -7,12 +9,16 @@ import {
|
|
|
7
9
|
type Connection,
|
|
8
10
|
type Edge,
|
|
9
11
|
type EdgeChange,
|
|
12
|
+
type FinalConnectionState,
|
|
10
13
|
type Node,
|
|
11
14
|
type NodeChange,
|
|
12
15
|
} from "@xyflow/react";
|
|
13
16
|
|
|
14
17
|
import type { BlueprintGraph } from "../graph/blueprint-graph";
|
|
15
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
resolveBlueprintConnectionIssue,
|
|
20
|
+
sanitizeBlueprintDocument,
|
|
21
|
+
} from "../graph/document";
|
|
16
22
|
import {
|
|
17
23
|
applyFlowNodePositions,
|
|
18
24
|
BP_EDGE_STYLE,
|
|
@@ -452,6 +458,8 @@ export function useBlueprintFlowState({
|
|
|
452
458
|
[getNodes, onGraphChange]
|
|
453
459
|
);
|
|
454
460
|
|
|
461
|
+
const { t } = useI18n();
|
|
462
|
+
|
|
455
463
|
const isValidConnection = useCallback(
|
|
456
464
|
(connection: Edge | Connection) => {
|
|
457
465
|
if (!connection.source || !connection.target) return false;
|
|
@@ -464,16 +472,40 @@ export function useBlueprintFlowState({
|
|
|
464
472
|
}
|
|
465
473
|
|
|
466
474
|
const targetNode = rfNodes.find((n) => n.id === connection.target);
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
return false;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
return true;
|
|
475
|
+
const sourceNode = rfNodes.find((n) => n.id === connection.source);
|
|
476
|
+
return !resolveBlueprintConnectionIssue(sourceNode?.data, targetNode?.data);
|
|
473
477
|
},
|
|
474
478
|
[getNodes]
|
|
475
479
|
);
|
|
476
480
|
|
|
481
|
+
const onConnectEnd = useCallback(
|
|
482
|
+
(_event: MouseEvent | TouchEvent, state: FinalConnectionState) => {
|
|
483
|
+
if (state.isValid) return;
|
|
484
|
+
const fromNode = state.fromNode as Node<BlueprintFlowNodeData> | null;
|
|
485
|
+
const toNode = state.toNode as Node<BlueprintFlowNodeData> | null;
|
|
486
|
+
if (!fromNode?.data || !toNode?.data) return;
|
|
487
|
+
|
|
488
|
+
const fromIsSource = state.fromHandle?.type !== "target";
|
|
489
|
+
const sourceData = fromIsSource ? fromNode.data : toNode.data;
|
|
490
|
+
const targetData = fromIsSource ? toNode.data : fromNode.data;
|
|
491
|
+
const issue = resolveBlueprintConnectionIssue(sourceData, targetData);
|
|
492
|
+
if (issue === "event-requires-lifecycle") {
|
|
493
|
+
toast({
|
|
494
|
+
title: t("blueprint.config.eventConnectNeedLifecycle"),
|
|
495
|
+
variant: "destructive",
|
|
496
|
+
});
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (issue === "lifecycle-no-input") {
|
|
500
|
+
toast({
|
|
501
|
+
title: t("blueprint.config.lifecycleNoInput"),
|
|
502
|
+
variant: "destructive",
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
[t]
|
|
507
|
+
);
|
|
508
|
+
|
|
477
509
|
return {
|
|
478
510
|
nodes,
|
|
479
511
|
edges,
|
|
@@ -481,6 +513,7 @@ export function useBlueprintFlowState({
|
|
|
481
513
|
onEdgesChange,
|
|
482
514
|
onNodeDragStop,
|
|
483
515
|
onConnect,
|
|
516
|
+
onConnectEnd,
|
|
484
517
|
isValidConnection,
|
|
485
518
|
};
|
|
486
519
|
}
|