@cardenelabs/cdl 0.11.0 → 0.12.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.
@@ -1,8 +1,9 @@
1
1
  import type { JSX } from "react";
2
- import { 箱に積む } from "./box-lines";
2
+ import { 使える幅, 幅で切る, 箱に積む } from "./box-lines";
3
3
  import type { LaidNode, Tone, MindBranchNode } from "../types";
4
4
  import { TONE } from "../render/tone";
5
5
  import { resolveMindData } from "../render/payload-binding";
6
+ import { 進みを畳む, 伸ばすdash, 段の伸び } from "./draw-progress";
6
7
 
7
8
  const MIND_TONES: Tone[] = ["accent", "teal", "success", "warning", "info", "error"];
8
9
 
@@ -31,10 +32,16 @@ export function MindMapNode({
31
32
  node,
32
33
  active,
33
34
  stateValues = {},
35
+ drawing = false,
36
+ progress = 1,
34
37
  }: {
35
38
  node: LaidNode;
36
39
  active: boolean;
37
40
  stateValues?: Record<string, string>;
41
+ /** この phase の `draw` に載っているか (cdl#520) */
42
+ drawing?: boolean;
43
+ /** phase の進み (0→1)。 `drawing` が true の時だけ読む */
44
+ progress?: number;
38
45
  }): JSX.Element {
39
46
  const x0 = node.cx - node.w / 2;
40
47
  const y0 = node.cy - node.h / 2;
@@ -50,6 +57,16 @@ export function MindMapNode({
50
57
 
51
58
  const layout = computeMindMapLayout(mindData, canvasCx, canvasCy, canvasW, canvasH);
52
59
 
60
+ /**
61
+ * 枝がどこまで伸びたか (0..1、 cdl#520)。 描く指定が無ければ常に 1 = 全部出ている。
62
+ *
63
+ * 中心に近い段から順に伸ばす。 全部を同時に伸ばすと、離れた枝が宙に浮いたまま伸びる。
64
+ */
65
+ const 伸び = 進みを畳む(drawing, progress);
66
+ const 最大深さ = layout.nodes.reduce((m, n) => Math.max(m, n.depth), 0);
67
+ /** その箱が出るか。 中心 (深さ 0) は起点なので進み 0 でも出す */
68
+ const 箱が出る = (深さ: number): boolean => !drawing || 段の伸び(伸び, 深さ, 最大深さ) >= 1;
69
+
53
70
  return (
54
71
  <g transform={`translate(${x0} ${y0})`}>
55
72
  <rect
@@ -64,20 +81,29 @@ export function MindMapNode({
64
81
  strokeWidth={active ? 3 : 1.25}
65
82
  />
66
83
 
67
- {layout.edges.map((e, i) => (
68
- <path
69
- key={`edge-${i}`}
70
- data-cdl-role="mind-edge"
71
- d={e.d}
72
- fill="none"
73
- stroke={toneColor(e.tone)}
74
- strokeWidth={2.5}
75
- strokeOpacity={0.55}
76
- strokeLinecap="round"
77
- />
78
- ))}
84
+ {layout.edges.map((e, i) => {
85
+ // 循環を閉じる枝は根から外へ向かう枝ではないため、全 node が出てから表示する
86
+ const 枝の伸び = e.deferUntilComplete
87
+ ? (伸び >= 1 ? 1 : 0)
88
+ : 段の伸び(伸び, e.depth, 最大深さ);
89
+ return (
90
+ <path
91
+ key={`edge-${i}`}
92
+ data-cdl-role="mind-edge"
93
+ d={e.d}
94
+ fill="none"
95
+ stroke={toneColor(e.tone)}
96
+ strokeWidth={2.5}
97
+ strokeOpacity={0.55}
98
+ strokeLinecap="round"
99
+ {...伸ばすdash(drawing, 枝の伸び)}
100
+ />
101
+ );
102
+ })}
79
103
 
80
104
  {layout.nodes.map((n) => {
105
+ // 枝が届いてから箱を出す。 届く前に出すと、繋がっていない箱が宙に浮く
106
+ if (!箱が出る(n.depth)) return null;
81
107
  // 補足は箱の中の 2 行目に出す (#471)。 箱の高さは変えないため、 入らない時は落ちる
82
108
  const 行 = 箱に積む(
83
109
  [
@@ -121,6 +147,9 @@ export function MindMapNode({
121
147
  </text>
122
148
  ))
123
149
  ) : (
150
+ // 補足が無くても **幅に収める** (#526)。 `箱に積む` を通らないこの経路だけが
151
+ // 幅を見ておらず、長い名前がそのまま箱の外へ出て枝の線と重なっていた
152
+ // (実測 = 中心の箱で文字 163px に対し箱 120px)
124
153
  <text
125
154
  x={n.x}
126
155
  y={n.y + 5}
@@ -129,7 +158,7 @@ export function MindMapNode({
129
158
  textAnchor="middle"
130
159
  fill={文字の色}
131
160
  >
132
- {n.title}
161
+ {幅で切る(n.title, n.isRoot ? 15 : 13, 使える幅(n.w))}
133
162
  </text>
134
163
  )}
135
164
  </g>
@@ -150,6 +179,8 @@ type LayoutNode = {
150
179
  h: number;
151
180
  tone?: Tone;
152
181
  isRoot: boolean;
182
+ /** 中心からの段数 (中心が 0、 cdl#520)。 段ごとに順に伸ばすのに使う */
183
+ depth: number;
153
184
  /** 名前を状態から解決できなかった項目に付く印 (#467) */
154
185
  unresolved?: true;
155
186
  };
@@ -157,6 +188,10 @@ type LayoutNode = {
157
188
  type LayoutEdge = {
158
189
  d: string;
159
190
  tone?: Tone;
191
+ /** 枝の先 (子) の段数 (cdl#520)。 段ごとに順に伸ばすのに使う */
192
+ depth: number;
193
+ /** 循環により、元の親が layout 上で子と同じ深さか深くなった枝 */
194
+ deferUntilComplete: boolean;
160
195
  };
161
196
 
162
197
  export function computeMindMapLayout(
@@ -179,6 +214,8 @@ export function computeMindMapLayout(
179
214
  w: rootW,
180
215
  h: rootH,
181
216
  isRoot: true,
217
+ // 中心は起点なので段数 0 (cdl#520)
218
+ depth: 0,
182
219
  ...((mindData as { unresolved?: true }).unresolved ? { unresolved: true as const } : {}),
183
220
  });
184
221
 
@@ -339,6 +376,8 @@ export function computeMindMapLayout(
339
376
  y,
340
377
  w,
341
378
  h,
379
+ // 第 1 階層が段数 1 (`subDepth` は 0 始まり、 cdl#520)
380
+ depth: subDepth + 1,
342
381
  tone: myTone,
343
382
  isRoot: false,
344
383
  ...((node as { unresolved?: true }).unresolved ? { unresolved: true as const } : {}),
@@ -388,6 +427,9 @@ export function computeMindMapLayout(
388
427
  }
389
428
  }
390
429
 
430
+ // 枝の段数は「その枝の先 (子)」 の段数にする (cdl#520)。 箱を置き終えた後に引く
431
+ const 段数 = new Map(nodes.map((n) => [n.id, n.depth]));
432
+
391
433
  // edge = 親の辺 → 子の辺。 root → 第 1 branch は root の辺から。
392
434
  for (const br of mindData.branches) {
393
435
  const childPos = nodePos.get(br.id);
@@ -395,17 +437,29 @@ export function computeMindMapLayout(
395
437
  const isLeft = nodeSide.get(br.id) === "left";
396
438
  const childHalfW = nodeHalfW.get(br.id) ?? subBranchW / 2;
397
439
  const tone = nodeTone.get(br.id);
440
+ const childDepth = 段数.get(br.id) ?? 1;
441
+ const parentDepth = 段数.get(br.parent) ?? 0;
398
442
  if (br.parent === mindData.rootId) {
399
443
  const startX = rootEdgeXFor(isLeft);
400
444
  const endX = childPos.x + (isLeft ? childHalfW : -childHalfW);
401
- edges.push({ d: makeCurve(startX, cy, endX, childPos.y, isLeft), tone });
445
+ edges.push({
446
+ d: makeCurve(startX, cy, endX, childPos.y, isLeft),
447
+ tone,
448
+ depth: childDepth,
449
+ deferUntilComplete: false,
450
+ });
402
451
  } else {
403
452
  const parentPos = nodePos.get(br.parent);
404
453
  if (!parentPos) continue;
405
454
  const parentHalfW = nodeHalfW.get(br.parent) ?? branchW / 2;
406
455
  const startX = parentPos.x + (isLeft ? -parentHalfW : parentHalfW);
407
456
  const endX = childPos.x + (isLeft ? childHalfW : -childHalfW);
408
- edges.push({ d: makeCurve(startX, parentPos.y, endX, childPos.y, isLeft), tone });
457
+ edges.push({
458
+ d: makeCurve(startX, parentPos.y, endX, childPos.y, isLeft),
459
+ tone,
460
+ depth: Math.max(parentDepth, childDepth),
461
+ deferUntilComplete: parentDepth >= childDepth,
462
+ });
409
463
  }
410
464
  }
411
465
 
@@ -2,7 +2,8 @@ import type { JSX } from "react";
2
2
  import type { LaidNode, TreeNodePayload } from "../types";
3
3
  import { TONE } from "../render/tone";
4
4
  import { resolveTreeData } from "../render/payload-binding";
5
- import { 箱に積む, type 積んだ行 } from "../kinds/box-lines";
5
+ import { 使える幅, 幅で切る, 箱に積む, type 積んだ行 } from "../kinds/box-lines";
6
+ import { 進みを畳む, 伸ばすdash, 段の伸び } from "./draw-progress";
6
7
 
7
8
  /**
8
9
  * tree-hierarchy kind ... 1 node に tree node 配列 (treeData) を保持し、
@@ -53,10 +54,16 @@ export function TreeNode({
53
54
  node,
54
55
  active,
55
56
  stateValues = {},
57
+ drawing = false,
58
+ progress = 1,
56
59
  }: {
57
60
  node: LaidNode;
58
61
  active: boolean;
59
62
  stateValues?: Record<string, string>;
63
+ /** この phase の `draw` に載っているか (cdl#520) */
64
+ drawing?: boolean;
65
+ /** phase の進み (0→1)。 `drawing` が true の時だけ読む */
66
+ progress?: number;
60
67
  }): JSX.Element {
61
68
  const x0 = node.cx - node.w / 2;
62
69
  const y0 = node.cy - node.h / 2;
@@ -72,6 +79,16 @@ export function TreeNode({
72
79
  const canvasH = node.h - PAD_TOP - PAD_BOTTOM;
73
80
 
74
81
  const layout = computeTreeLayout(treeNodes, canvasW, canvasH);
82
+
83
+ /**
84
+ * 枝がどこまで伸びたか (0..1、 cdl#520)。 描く指定が無ければ常に 1 = 全部出ている。
85
+ *
86
+ * 根に近い段から順に伸ばす。 全部を同時に伸ばすと、離れた枝が宙に浮いたまま伸びる。
87
+ */
88
+ const 伸び = 進みを畳む(drawing, progress);
89
+ const 最大深さ = layout.nodes.reduce((m, n) => Math.max(m, n.depth), 0);
90
+ /** その箱が出るか。 根 (深さ 0) は起点なので進み 0 でも出す */
91
+ const 箱が出る = (深さ: number): boolean => !drawing || 段の伸び(伸び, 深さ, 最大深さ) >= 1;
75
92
  const depthColors = [TONE.accent, TONE.teal, TONE.success, TONE.warning, TONE.info];
76
93
 
77
94
  return (
@@ -106,6 +123,10 @@ export function TreeNode({
106
123
  const y2 = PAD_TOP + e.y2;
107
124
  const midY = (y1 + y2) / 2;
108
125
  const path = `M ${x1} ${y1} L ${x1} ${midY} L ${x2} ${midY} L ${x2} ${y2}`;
126
+ // 循環を閉じる枝は根から外へ向かう枝ではないため、全 node が出てから表示する
127
+ const 枝の伸び = e.deferUntilComplete
128
+ ? (伸び >= 1 ? 1 : 0)
129
+ : 段の伸び(伸び, e.depth, 最大深さ);
109
130
  return (
110
131
  <g key={`edge-${i}`}>
111
132
  <path
@@ -116,13 +137,19 @@ export function TreeNode({
116
137
  strokeOpacity={0.55}
117
138
  strokeWidth={1.5}
118
139
  strokeLinejoin="round"
140
+ {...伸ばすdash(drawing, 枝の伸び)}
119
141
  />
120
- <circle cx={x1} cy={midY} r={2.5} fill={TONE.accent} fillOpacity={0.75} />
142
+ {/* 曲がり角の点は枝が届いてから出す。 先に出ると線の無い場所に点が浮く */}
143
+ {(!drawing || 枝の伸び >= 1) && (
144
+ <circle cx={x1} cy={midY} r={2.5} fill={TONE.accent} fillOpacity={0.75} />
145
+ )}
121
146
  </g>
122
147
  );
123
148
  })}
124
149
 
125
150
  {layout.nodes.map((n) => {
151
+ // 枝が届いてから箱を出す。 届く前に出すと、繋がっていない箱が宙に浮く
152
+ if (!箱が出る(n.depth)) return null;
126
153
  const accent = depthColors[Math.min(n.depth, depthColors.length - 1)] ?? depthColors[0]!;
127
154
  const isRoot = n.depth === 0;
128
155
  // 補足は箱の中の 2 行目に出す (#471)。 箱の高さは変えないため、 入らない時は落ちる
@@ -163,6 +190,8 @@ export function TreeNode({
163
190
  名前の太さ: 700,
164
191
  })
165
192
  ) : (
193
+ // 補足が無くても幅に収める (#526)。 `箱の文字` を通らないこの経路だけが
194
+ // 幅を見ておらず、長い名前が箱の外へ出ていた
166
195
  <text
167
196
  x={n.w / 2}
168
197
  y={n.h / 2 + 5}
@@ -171,7 +200,7 @@ export function TreeNode({
171
200
  textAnchor="middle"
172
201
  fill="var(--cdl-text-on-tone, #ffffff)"
173
202
  >
174
- {n.title}
203
+ {幅で切る(n.title, 13, 使える幅(n.w))}
175
204
  </text>
176
205
  )}
177
206
  </>
@@ -206,7 +235,7 @@ export function TreeNode({
206
235
  textAnchor="middle"
207
236
  fill="var(--cdl-chip-text, #1a1f2a)"
208
237
  >
209
- {n.title}
238
+ {幅で切る(n.title, 12, 使える幅(n.w))}
210
239
  </text>
211
240
  )}
212
241
  </>
@@ -232,13 +261,23 @@ type LayoutNode = {
232
261
  unresolved?: true;
233
262
  };
234
263
 
264
+ type LayoutEdge = {
265
+ x1: number;
266
+ y1: number;
267
+ x2: number;
268
+ y2: number;
269
+ depth: number;
270
+ /** 循環により、元の親が layout 上で子と同じ深さか深くなった枝 */
271
+ deferUntilComplete: boolean;
272
+ };
273
+
235
274
  export function computeTreeLayout(
236
275
  treeNodes: TreeNodePayload[],
237
276
  canvasW: number,
238
277
  canvasH: number,
239
278
  ): {
240
279
  nodes: LayoutNode[];
241
- edges: Array<{ x1: number; y1: number; x2: number; y2: number }>;
280
+ edges: LayoutEdge[];
242
281
  } {
243
282
  // parent が実在しない node は root 扱いにする。 そうしないと root からの再帰で辿れず、
244
283
  // node が silent に消える (旧実装は全 node を列挙していたので消えなかった)。
@@ -357,7 +396,9 @@ export function computeTreeLayout(
357
396
  }
358
397
  const hById = new Map(nodes.map((n) => [n.id, n.h]));
359
398
 
360
- const edges: Array<{ x1: number; y1: number; x2: number; y2: number }> = [];
399
+ // 枝の段数は「その枝の先 (子)」 の段数 (cdl#520)。 段ごとに順に伸ばすのに使う
400
+ const 段数 = new Map(nodes.map((n) => [n.id, n.depth]));
401
+ const edges: LayoutEdge[] = [];
361
402
  for (const n of treeNodes) {
362
403
  if (!n.parent) continue;
363
404
  const parent = posOf.get(n.parent);
@@ -365,7 +406,16 @@ export function computeTreeLayout(
365
406
  if (!parent || !child) continue;
366
407
  const parentH = hById.get(n.parent) ?? nodeH;
367
408
  const childH = hById.get(n.id) ?? nodeH;
368
- edges.push({ x1: parent.x, y1: parent.y + parentH / 2, x2: child.x, y2: child.y - childH / 2 });
409
+ const parentDepth = 段数.get(n.parent) ?? 0;
410
+ const childDepth = 段数.get(n.id) ?? 1;
411
+ edges.push({
412
+ x1: parent.x,
413
+ y1: parent.y + parentH / 2,
414
+ x2: child.x,
415
+ y2: child.y - childH / 2,
416
+ depth: Math.max(parentDepth, childDepth),
417
+ deferUntilComplete: parentDepth >= childDepth,
418
+ });
369
419
  }
370
420
 
371
421
  return { nodes, edges };
@@ -2,6 +2,7 @@ import type { JSX } from "react";
2
2
  import type { LaidNode, JourneyEmotion } from "../types";
3
3
  import { TONE } from "../render/tone";
4
4
  import { resolveJourneyData } from "../render/payload-binding";
5
+ import { 進みを畳む, 伸ばすdash, ベジエの累積割合 } from "./draw-progress";
5
6
 
6
7
  /**
7
8
  * journey-map kind ... 1 node に step 配列 (journeyData) を保持し、 UX journey map を SVG 描画する。
@@ -18,10 +19,16 @@ export function UserJourneyNode({
18
19
  node,
19
20
  active,
20
21
  stateValues = {},
22
+ drawing = false,
23
+ progress = 1,
21
24
  }: {
22
25
  node: LaidNode;
23
26
  active: boolean;
24
27
  stateValues?: Record<string, string>;
28
+ /** この phase の `draw` に載っているか (cdl#520) */
29
+ drawing?: boolean;
30
+ /** phase の進み (0→1)。 `drawing` が true の時だけ読む */
31
+ progress?: number;
25
32
  }): JSX.Element {
26
33
  const x0 = node.cx - node.w / 2;
27
34
  const y0 = node.cy - node.h / 2;
@@ -70,6 +77,21 @@ export function UserJourneyNode({
70
77
 
71
78
  // smooth curve for emotion path
72
79
  const pts = steps.map((s, i) => ({ x: xAt(i), y: yAt(s.emotion) }));
80
+ /**
81
+ * 線がどこまで伸びたか (0..1、 cdl#520)。 描く指定が無ければ常に 1 = 全長。
82
+ */
83
+ const 伸び = 進みを畳む(drawing, progress);
84
+ /**
85
+ * 各段までの累積割合。 **点番号の比率ではなく実際の経路長で測る** (#513 の指摘)。
86
+ * 気持ちの上下で区間の長さが変わるため、比率で判定すると先端と丸がずれる。
87
+ */
88
+ const 段までの割合 = ベジエの累積割合(pts, (前, 後) => {
89
+ const midX = (前.x + 後.x) / 2;
90
+ return [{ x: midX, y: 前.y }, { x: midX, y: 後.y }];
91
+ });
92
+ /** その段まで線の先が届いたか。 左端は進み 0 でも出す (そこが線の起点) */
93
+ const 線の先が届いた = (idx: number): boolean => !drawing || 伸び >= (段までの割合[idx] ?? 0);
94
+
73
95
  const smoothPath = pts.length > 1
74
96
  ? pts.reduce((acc, p, i) => {
75
97
  if (i === 0) return `M ${p.x} ${p.y}`;
@@ -144,6 +166,10 @@ export function UserJourneyNode({
144
166
 
145
167
  {steps.length > 1 && (
146
168
  <>
169
+ {/*
170
+ 影と本体は同じ進みで伸ばす。 片方だけ先行すると、線の先に影だけが伸びた
171
+ 形になる (cdl#520)
172
+ */}
147
173
  <path
148
174
  data-cdl-role="journey-line-glow"
149
175
  d={smoothPath}
@@ -152,6 +178,7 @@ export function UserJourneyNode({
152
178
  strokeOpacity={0.18}
153
179
  strokeWidth={10}
154
180
  strokeLinecap="round"
181
+ {...伸ばすdash(drawing, 伸び)}
155
182
  />
156
183
  <path
157
184
  data-cdl-role="journey-line"
@@ -161,11 +188,12 @@ export function UserJourneyNode({
161
188
  strokeWidth={2.75}
162
189
  strokeLinejoin="round"
163
190
  strokeLinecap="round"
191
+ {...伸ばすdash(drawing, 伸び)}
164
192
  />
165
193
  </>
166
194
  )}
167
195
 
168
- {steps.map((s, idx) => (
196
+ {steps.map((s, idx) => !線の先が届いた(idx) ? null : (
169
197
  <g
170
198
  key={`step-${s.id}`}
171
199
  data-cdl-role="journey-step"
@@ -190,17 +190,57 @@ export function CdlNodeView({
190
190
  />
191
191
  );
192
192
  case "gantt-timeline":
193
- return <GanttNode node={resolvedNode} active={active} stateValues={stateValues} />;
193
+ return (
194
+ <GanttNode
195
+ node={resolvedNode}
196
+ active={active}
197
+ stateValues={stateValues}
198
+ drawing={drawing}
199
+ progress={progress}
200
+ />
201
+ );
194
202
  case "mind-map":
195
- return <MindMapNode node={resolvedNode} active={active} stateValues={stateValues} />;
203
+ return (
204
+ <MindMapNode
205
+ node={resolvedNode}
206
+ active={active}
207
+ stateValues={stateValues}
208
+ drawing={drawing}
209
+ progress={progress}
210
+ />
211
+ );
196
212
  case "funnel-stages":
197
- return <FunnelNode node={resolvedNode} active={active} stateValues={stateValues} />;
213
+ return (
214
+ <FunnelNode
215
+ node={resolvedNode}
216
+ active={active}
217
+ stateValues={stateValues}
218
+ drawing={drawing}
219
+ progress={progress}
220
+ />
221
+ );
198
222
  case "quadrant-matrix":
199
223
  return <QuadrantNode node={resolvedNode} active={active} stateValues={stateValues} />;
200
224
  case "tree-hierarchy":
201
- return <TreeHierarchyNode node={resolvedNode} active={active} stateValues={stateValues} />;
225
+ return (
226
+ <TreeHierarchyNode
227
+ node={resolvedNode}
228
+ active={active}
229
+ stateValues={stateValues}
230
+ drawing={drawing}
231
+ progress={progress}
232
+ />
233
+ );
202
234
  case "journey-map":
203
- return <UserJourneyNode node={resolvedNode} active={active} stateValues={stateValues} />;
235
+ return (
236
+ <UserJourneyNode
237
+ node={resolvedNode}
238
+ active={active}
239
+ stateValues={stateValues}
240
+ drawing={drawing}
241
+ progress={progress}
242
+ />
243
+ );
204
244
  case "shape-file":
205
245
  return <ShapeFileNode node={resolvedNode} active={active} />;
206
246
  case "shape-folder":
package/src/types.ts CHANGED
@@ -2135,6 +2135,11 @@ export type CdlPhase = {
2135
2135
  * | `chart-line` | 左端 | 線が右へ伸びる | 点と数字は線の先が届いた時 |
2136
2136
  * | `chart-bar` | 横軸 | 棒が上へ伸びる | 数字は棒の頭に付いて動く |
2137
2137
  * | `chart-pie` | 12 時 | 扇が時計回りに開く | 凡例はその扇が開き始めた時 |
2138
+ * | `journey-map` | 左端 | 気持ちの線が右へ伸びる | 段はその点に線の先が届いた時 |
2139
+ * | `mind-map` | 中心 | 枝が段ごとに外へ伸びる | 箱はその枝が届いた時 |
2140
+ * | `tree-hierarchy` | 根 | 枝が段ごとに下へ伸びる | 箱はその枝が届いた時 |
2141
+ * | `gantt-timeline` | 各帯の始端 | 帯が右へ伸びる | 依存の矢印は帯が出揃った時 |
2142
+ * | `funnel-stages` | 上端 | 段が上から順に積まれる | 件数と離脱率はその段が出切った時 |
2138
2143
  *
2139
2144
  * 対象種別の一覧は `validate.ts` の `DRAW_KINDS` が持つ。
2140
2145
  *
package/src/validate.ts CHANGED
@@ -17,8 +17,22 @@ const TONES = new Set<string>(TONE_NAMES);
17
17
  * | `chart-line` | 左端 | 線が右へ伸びる |
18
18
  * | `chart-bar` | 横軸 | 棒が上へ伸びる |
19
19
  * | `chart-pie` | 12 時 | 扇が時計回りに開く |
20
+ * | `journey-map` | 左端 | 気持ちの線が右へ伸びる |
21
+ * | `mind-map` | 中心 | 枝が中心から外へ伸びる |
22
+ * | `tree-hierarchy` | 根 | 枝が上の段から下の段へ伸びる |
23
+ * | `gantt-timeline` | 各帯の始端 | 帯が右へ伸びる |
24
+ * | `funnel-stages` | 上端 | 段が上から順に積まれる |
20
25
  */
21
- export const DRAW_KINDS: ReadonlySet<string> = new Set(["chart-line", "chart-bar", "chart-pie"]);
26
+ export const DRAW_KINDS: ReadonlySet<string> = new Set([
27
+ "chart-line",
28
+ "chart-bar",
29
+ "chart-pie",
30
+ "journey-map",
31
+ "mind-map",
32
+ "tree-hierarchy",
33
+ "gantt-timeline",
34
+ "funnel-stages",
35
+ ]);
22
36
  const KINDS = new Set<string>(NODE_KINDS);
23
37
  const ENG_BANNED = /\b(FUNCTION|STORAGE|EVENT LOG|BALANCES MAPPING|READ|WRITE|EMIT|CALL)\b/;
24
38