@arronqzy/react-blueprint 1.0.19 → 1.0.22

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.
@@ -1,5 +1,7 @@
1
1
  import { useI18nOptional as useI18n } from "@arronqzy/i18n/react";
2
2
  import { cn } from "@arronqzy/ui";
3
+ import type { ReactNode } from "react";
4
+ import { localizeBlueprintNodeLabel } from "../graph/document";
3
5
 
4
6
  export type BlueprintNodeCardProps = {
5
7
  nodeId: string;
@@ -9,78 +11,130 @@ export type BlueprintNodeCardProps = {
9
11
  subtitle?: string;
10
12
  /** 调试执行进度(如时钟 2/3) */
11
13
  progressLabel?: string;
12
- variant?: "blueprint" | "logic" | "and" | "lifecycle" | "event" | "fetch" | "json" | "storage" | "clock";
14
+ variant?: "blueprint" | "view" | "logic" | "and" | "lifecycle" | "event" | "fetch" | "json" | "storage" | "clock";
13
15
  selected?: boolean;
14
16
  hideLeadingDot?: boolean;
15
17
  onSelect?: (nodeId: string) => void;
16
18
  };
17
19
 
18
- const variantStyle = {
19
- blueprint: {
20
- accent: "border-l-primary",
21
- badge: "bg-primary/10 text-primary",
22
- dot: "bg-primary",
23
- },
24
- logic: {
25
- accent: "border-l-sky-500 dark:border-l-sky-400",
26
- badge: "bg-sky-500/10 text-sky-700 dark:text-sky-300",
27
- dot: "bg-sky-500",
28
- },
29
- and: {
30
- accent: "border-l-indigo-500 dark:border-l-indigo-400",
31
- badge: "bg-indigo-500/10 text-indigo-700 dark:text-indigo-300",
32
- dot: "bg-indigo-500",
33
- },
34
- lifecycle: {
35
- accent: "border-l-amber-500 dark:border-l-amber-400",
36
- badge: "bg-amber-500/10 text-amber-700 dark:text-amber-300",
37
- dot: "bg-amber-500",
38
- },
39
- event: {
40
- accent: "border-l-fuchsia-500 dark:border-l-fuchsia-400",
41
- badge: "bg-fuchsia-500/10 text-fuchsia-700 dark:text-fuchsia-300",
42
- dot: "bg-fuchsia-500",
43
- },
44
- fetch: {
45
- accent: "border-l-violet-500 dark:border-l-violet-400",
46
- badge: "bg-violet-500/10 text-violet-700 dark:text-violet-300",
47
- dot: "bg-violet-500",
48
- },
49
- json: {
50
- accent: "border-l-teal-500 dark:border-l-teal-400",
51
- badge: "bg-teal-500/10 text-teal-700 dark:text-teal-300",
52
- dot: "bg-teal-500",
53
- },
54
- storage: {
55
- accent: "border-l-emerald-500 dark:border-l-emerald-400",
56
- badge: "bg-emerald-500/10 text-emerald-700 dark:text-emerald-300",
57
- dot: "bg-emerald-500",
58
- },
59
- clock: {
60
- accent: "border-l-rose-500 dark:border-l-rose-400",
61
- badge: "bg-rose-500/10 text-rose-700 dark:text-rose-300",
62
- dot: "bg-rose-500",
63
- },
64
- } as const;
65
-
66
- function NodeGrip() {
20
+ function IconSvg({ children }: { children: ReactNode }) {
67
21
  return (
68
- <div
69
- className="flex shrink-0 flex-col gap-[3px] opacity-35"
22
+ <svg
23
+ viewBox="0 0 24 24"
24
+ fill="none"
25
+ stroke="currentColor"
26
+ strokeWidth="2"
27
+ strokeLinecap="round"
28
+ strokeLinejoin="round"
70
29
  aria-hidden="true"
71
30
  >
72
- <span className="flex gap-[3px]">
73
- <span className="h-[3px] w-[3px] rounded-full bg-current" />
74
- <span className="h-[3px] w-[3px] rounded-full bg-current" />
75
- </span>
76
- <span className="flex gap-[3px]">
77
- <span className="h-[3px] w-[3px] rounded-full bg-current" />
78
- <span className="h-[3px] w-[3px] rounded-full bg-current" />
79
- </span>
80
- </div>
31
+ {children}
32
+ </svg>
81
33
  );
82
34
  }
83
35
 
36
+ function VariantIcon({
37
+ variant,
38
+ }: {
39
+ variant: NonNullable<BlueprintNodeCardProps["variant"]>;
40
+ }) {
41
+ switch (variant) {
42
+ case "logic":
43
+ return (
44
+ <IconSvg>
45
+ <path d="M6 4v6a4 4 0 0 0 4 4h4" />
46
+ <path d="M14 14v6" />
47
+ <circle cx="6" cy="4" r="1.5" fill="currentColor" stroke="none" />
48
+ <circle cx="14" cy="20" r="1.5" fill="currentColor" stroke="none" />
49
+ <circle cx="18" cy="14" r="1.5" fill="currentColor" stroke="none" />
50
+ </IconSvg>
51
+ );
52
+ case "and":
53
+ return (
54
+ <IconSvg>
55
+ <path d="M8 6v12" />
56
+ <path d="M8 12h8" />
57
+ <path d="M16 6v12" />
58
+ </IconSvg>
59
+ );
60
+ case "lifecycle":
61
+ return (
62
+ <IconSvg>
63
+ <circle cx="12" cy="12" r="7" />
64
+ <path d="M12 8v4l2.5 1.5" />
65
+ </IconSvg>
66
+ );
67
+ case "event":
68
+ return (
69
+ <IconSvg>
70
+ <path d="M13 2 4 14h7l-1 8 9-12h-7l1-8z" />
71
+ </IconSvg>
72
+ );
73
+ case "fetch":
74
+ return (
75
+ <IconSvg>
76
+ <path d="M12 3v12" />
77
+ <path d="m7 10 5 5 5-5" />
78
+ <path d="M5 19h14" />
79
+ </IconSvg>
80
+ );
81
+ case "json":
82
+ return (
83
+ <IconSvg>
84
+ <path d="M8 4c-2 0-3 1.5-3 4s1 4 3 4" />
85
+ <path d="M8 12c-2 0-3 1.5-3 4s1 4 3 4" />
86
+ <path d="M16 4c2 0 3 1.5 3 4s-1 4-3 4" />
87
+ <path d="M16 12c2 0 3 1.5 3 4s-1 4-3 4" />
88
+ </IconSvg>
89
+ );
90
+ case "storage":
91
+ return (
92
+ <IconSvg>
93
+ <ellipse cx="12" cy="6" rx="7" ry="3" />
94
+ <path d="M5 6v6c0 1.7 3.1 3 7 3s7-1.3 7-3V6" />
95
+ <path d="M5 12v6c0 1.7 3.1 3 7 3s7-1.3 7-3v-6" />
96
+ </IconSvg>
97
+ );
98
+ case "clock":
99
+ return (
100
+ <IconSvg>
101
+ <circle cx="12" cy="12" r="8" />
102
+ <path d="M12 8v4.5L15 15" />
103
+ </IconSvg>
104
+ );
105
+ case "blueprint":
106
+ return (
107
+ <IconSvg>
108
+ <circle cx="7" cy="7" r="2.5" />
109
+ <circle cx="17" cy="7" r="2.5" />
110
+ <circle cx="12" cy="17" r="2.5" />
111
+ <path d="M9.2 8.2 14.8 8.2" />
112
+ <path d="M8.2 9.2 11 14.8" />
113
+ <path d="M15.8 9.2 13 14.8" />
114
+ </IconSvg>
115
+ );
116
+ case "view":
117
+ return (
118
+ <IconSvg>
119
+ <rect x="3" y="5" width="18" height="14" rx="2" />
120
+ <path d="M3 9h18" />
121
+ <path d="M8 14h4" />
122
+ </IconSvg>
123
+ );
124
+ default:
125
+ return (
126
+ <IconSvg>
127
+ <circle cx="7" cy="7" r="2.5" />
128
+ <circle cx="17" cy="7" r="2.5" />
129
+ <circle cx="12" cy="17" r="2.5" />
130
+ <path d="M9.2 8.2 14.8 8.2" />
131
+ <path d="M8.2 9.2 11 14.8" />
132
+ <path d="M15.8 9.2 13 14.8" />
133
+ </IconSvg>
134
+ );
135
+ }
136
+ }
137
+
84
138
  export function BlueprintNodeCard({
85
139
  nodeId,
86
140
  label,
@@ -89,81 +143,43 @@ export function BlueprintNodeCard({
89
143
  progressLabel,
90
144
  variant = "blueprint",
91
145
  selected = false,
92
- hideLeadingDot = false,
93
146
  onSelect,
94
147
  }: BlueprintNodeCardProps) {
95
148
  const { t } = useI18n();
96
- const v = variantStyle[variant];
149
+ const displayLabel = localizeBlueprintNodeLabel(label, t);
150
+ const showMeta = Boolean(meta && meta !== displayLabel);
97
151
 
98
152
  return (
99
153
  <div
100
154
  data-blueprint-node-card
101
155
  className={cn(
102
- "bp-node-card w-[168px] overflow-hidden rounded-lg border border-border bg-card text-card-foreground shadow-sm",
103
- "transition-[box-shadow,border-color] duration-150",
104
- hideLeadingDot
105
- ? cn(
106
- "border-t-[3px]",
107
- variant === "event"
108
- ? "border-t-fuchsia-500 dark:border-t-fuchsia-400"
109
- : "border-t-amber-500 dark:border-t-amber-400"
110
- )
111
- : cn("border-l-[3px]", v.accent),
112
- selected &&
113
- "border-primary/50 shadow-[0_0_0_1px_hsl(var(--primary)/0.35),0_4px_12px_hsl(var(--primary)/0.12)]"
156
+ "bp-node-card bp-flow-drag-handle",
157
+ selected && "bp-node-card--selected"
114
158
  )}
159
+ title={t("blueprint.node.dragToMove")}
115
160
  >
116
- <div
117
- className={cn(
118
- "bp-flow-drag-handle flex cursor-grab items-center gap-2 border-b border-border/50 px-2 py-1.5",
119
- "bg-muted/25 text-muted-foreground active:cursor-grabbing"
120
- )}
121
- title={t("blueprint.node.dragToMove")}
122
- >
123
- <NodeGrip />
124
- {meta ? (
125
- <span
126
- className={cn(
127
- "min-w-0 flex-1 truncate rounded px-1.5 py-0.5 text-[10px] font-medium leading-none tracking-wide",
128
- v.badge
129
- )}
130
- title={meta}
131
- >
132
- {meta}
133
- </span>
134
- ) : null}
135
- </div>
136
-
137
161
  <button
138
162
  type="button"
139
- className={cn(
140
- "nodrag flex w-full text-left outline-none transition-colors hover:bg-accent/30",
141
- "focus-visible:ring-1 focus-visible:ring-primary focus-visible:ring-inset",
142
- hideLeadingDot
143
- ? cn("px-2.5 py-2", subtitle ? "items-start" : "items-center")
144
- : "items-start gap-2 px-2.5 py-2"
145
- )}
163
+ className="bp-node-card__body"
146
164
  onClick={(e) => {
147
165
  e.stopPropagation();
148
166
  onSelect?.(nodeId);
149
167
  }}
150
168
  >
151
- {!hideLeadingDot ? (
152
- <span
153
- className={cn("mt-1.5 h-1.5 w-1.5 shrink-0 rounded-full", v.dot)}
154
- aria-hidden="true"
155
- />
156
- ) : null}
157
- <div className="min-w-0 flex-1">
158
- <div className="flex items-start gap-1.5">
159
- <div className="min-w-0 flex-1 truncate text-[13px] font-medium leading-snug text-foreground">
160
- {label}
161
- </div>
169
+ <span
170
+ className={cn("bp-node-icon", `bp-node-icon--${variant}`)}
171
+ aria-hidden="true"
172
+ >
173
+ <VariantIcon variant={variant} />
174
+ </span>
175
+ <div className="bp-node-card__content">
176
+ <div className="bp-node-card__title-row">
177
+ <div className="bp-node-card__title">{displayLabel}</div>
162
178
  {progressLabel ? (
163
179
  <span
164
180
  className={cn(
165
- "shrink-0 rounded px-1.5 py-0.5 text-[10px] font-semibold leading-none",
166
- v.badge
181
+ "bp-node-card__badge",
182
+ `bp-node-card__badge--${variant}`
167
183
  )}
168
184
  title={t("blueprint.node.signalSentCount")}
169
185
  >
@@ -171,11 +187,13 @@ export function BlueprintNodeCard({
171
187
  </span>
172
188
  ) : null}
173
189
  </div>
190
+ {showMeta ? (
191
+ <div className="bp-node-card__meta" title={meta}>
192
+ {meta}
193
+ </div>
194
+ ) : null}
174
195
  {subtitle ? (
175
- <div
176
- className="mt-0.5 truncate font-mono text-[10px] leading-tight text-muted-foreground"
177
- title={subtitle}
178
- >
196
+ <div className="bp-node-card__subtitle" title={subtitle}>
179
197
  {subtitle}
180
198
  </div>
181
199
  ) : null}
@@ -27,6 +27,7 @@ export function BlueprintNodeShell({
27
27
  <div
28
28
  className={cn(
29
29
  "bp-node",
30
+ selected && "bp-node--selected",
30
31
  executionTone === "success" && "bp-node--execution-true",
31
32
  executionTone === "error" && "bp-node--execution-false"
32
33
  )}
@@ -20,6 +20,7 @@ export function LifecycleFlowNode({ id, data }: NodeProps) {
20
20
  <div
21
21
  className={cn(
22
22
  "bp-node bp-node--lifecycle",
23
+ nodeData.isSelected && "bp-node--selected",
23
24
  executionTone === "success" && "bp-node--execution-true",
24
25
  executionTone === "error" && "bp-node--execution-false"
25
26
  )}