@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.
- package/CHANGELOG.md +65 -1
- package/SPEC.md +12 -2
- package/dist/index.cjs +361 -177
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +361 -177
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +361 -177
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +361 -177
- package/dist/react.js.map +1 -1
- package/dist/{render-DwH8v5Mb.d.cts → render-C7FKvMqq.d.cts} +5 -0
- package/dist/{render-DwH8v5Mb.d.ts → render-C7FKvMqq.d.ts} +5 -0
- package/package.json +1 -1
- package/src/kinds/box-lines.ts +12 -2
- package/src/kinds/chart-bar.tsx +3 -7
- package/src/kinds/chart-line.tsx +4 -10
- package/src/kinds/chart-pie.tsx +3 -7
- package/src/kinds/draw-progress.ts +100 -0
- package/src/kinds/funnel.tsx +101 -58
- package/src/kinds/gantt.tsx +25 -1
- package/src/kinds/mind-map.tsx +70 -16
- package/src/kinds/tree.tsx +57 -7
- package/src/kinds/user-journey.tsx +29 -1
- package/src/render/nodes.tsx +45 -5
- package/src/types.ts +5 -0
- package/src/validate.ts +15 -1
package/src/kinds/mind-map.tsx
CHANGED
|
@@ -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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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({
|
|
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({
|
|
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
|
|
package/src/kinds/tree.tsx
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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"
|
package/src/render/nodes.tsx
CHANGED
|
@@ -190,17 +190,57 @@ export function CdlNodeView({
|
|
|
190
190
|
/>
|
|
191
191
|
);
|
|
192
192
|
case "gantt-timeline":
|
|
193
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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([
|
|
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
|
|