@cardenelabs/cdl 0.5.0 → 0.6.0
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/README.md +3 -3
- package/dist/index.cjs +1350 -1258
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -48
- package/dist/index.d.ts +43 -48
- package/dist/index.js +1349 -1258
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +2732 -2592
- 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 +2732 -2592
- package/dist/react.js.map +1 -1
- package/dist/{render-C78lIXeC.d.cts → render-DquCvgOB.d.cts} +65 -14
- package/dist/{render-C78lIXeC.d.ts → render-DquCvgOB.d.ts} +65 -14
- package/package.json +1 -1
- package/src/formula/parser.ts +35 -0
- package/src/index.ts +8 -4
- package/src/kinds/chart-bar.tsx +13 -2
- package/src/kinds/chart-line.tsx +13 -3
- package/src/kinds/chart-pie.tsx +13 -2
- package/src/kinds/dyn-shape.tsx +3 -2
- package/src/kinds/funnel.tsx +16 -3
- package/src/kinds/gantt.tsx +16 -3
- package/src/kinds/generic.tsx +9 -5
- package/src/kinds/quadrant.tsx +16 -3
- package/src/kinds/user-journey.tsx +24 -9
- package/src/layout/spec.ts +1 -2
- package/src/layout/tokens.ts +1 -2
- package/src/layout.ts +1 -0
- package/src/presets.ts +7 -100
- package/src/render/derived-values.ts +261 -0
- package/src/render/nodes.tsx +14 -12
- package/src/render/payload-binding.ts +196 -0
- package/src/render/utils.ts +47 -3
- package/src/template-name.ts +43 -0
- package/src/types.ts +72 -18
- package/src/validate.ts +42 -9
- package/src/visual-validate.ts +10 -10
- package/src/kinds/mind-radial.tsx +0 -209
package/src/visual-validate.ts
CHANGED
|
@@ -87,6 +87,7 @@ import {
|
|
|
87
87
|
segmentsCrossRect,
|
|
88
88
|
} from "./layout/collisions";
|
|
89
89
|
import { measureTextWidth } from "./layout/text-width";
|
|
90
|
+
import { templateRefIds } from "./render/utils";
|
|
90
91
|
import {
|
|
91
92
|
bboxOverlaps,
|
|
92
93
|
isNearAny,
|
|
@@ -164,7 +165,7 @@ export const MERMAID_PARITY_BASIS_VERSION = "11.16";
|
|
|
164
165
|
* | classDiagram | classDiagram |
|
|
165
166
|
* | gantt | gantt |
|
|
166
167
|
* | quadrant | quadrantChart |
|
|
167
|
-
* | mindMap
|
|
168
|
+
* | mindMap | mindmap |
|
|
168
169
|
* | userJourney | journey |
|
|
169
170
|
* | chart / pie | xyChart / pie |
|
|
170
171
|
* | topology / infrastructure / network | architecture-beta (v11.1.0、 service / group / edge で構成を描く) |
|
|
@@ -190,8 +191,6 @@ export const MERMAID_COMPATIBLE_PRESETS: ReadonlySet<string> = new Set([
|
|
|
190
191
|
"quad",
|
|
191
192
|
"mindmap",
|
|
192
193
|
"mind",
|
|
193
|
-
"mindmapradial",
|
|
194
|
-
"mindradial",
|
|
195
194
|
"userjourney",
|
|
196
195
|
"journey",
|
|
197
196
|
"chart",
|
|
@@ -418,7 +417,6 @@ const MIN_NODE_W = 80;
|
|
|
418
417
|
const MIN_NODE_H = 40;
|
|
419
418
|
const MIN_FONT_SIZE = 12;
|
|
420
419
|
const ROW_FORMAT = /^[^:]+:\s*.+$/;
|
|
421
|
-
const PLACEHOLDER_RE = /\{(\w+)\}/g;
|
|
422
420
|
|
|
423
421
|
function emptyCounts(): Record<VisualAxis, number> {
|
|
424
422
|
return {
|
|
@@ -1864,7 +1862,11 @@ function runAxes(
|
|
|
1864
1862
|
// 行を描かない種別の `rows` は画面に出ないので、 形式を要求しても意味がない。 書かれている
|
|
1865
1863
|
// こと自体が破綻なのは Axis 68 (`rows-not-rendered`) が別に見る。
|
|
1866
1864
|
const DIVIDER_ONLY = /^[─━—-]+$/;
|
|
1867
|
-
|
|
1865
|
+
// `{名前}` で読める値。 他の値から自動で決まる値 (`derived`) も読めるので足す
|
|
1866
|
+
const stateIds = new Set([
|
|
1867
|
+
...diag.states.map((s) => s.id),
|
|
1868
|
+
...(diag.derived ?? []).map((d) => d.id),
|
|
1869
|
+
]);
|
|
1868
1870
|
for (const n of diag.nodes) {
|
|
1869
1871
|
if (!n.rows) continue;
|
|
1870
1872
|
if (!rendersRows(n.kind)) continue;
|
|
@@ -1874,11 +1876,9 @@ function runAxes(
|
|
|
1874
1876
|
if (!ROW_FORMAT.test(row)) {
|
|
1875
1877
|
push("row-format", `node "${n.id}" row "${row}" が "key: value" 形式でない`);
|
|
1876
1878
|
}
|
|
1877
|
-
|
|
1878
|
-
const
|
|
1879
|
-
|
|
1880
|
-
const id = m[1];
|
|
1881
|
-
if (id && !stateIds.has(id)) {
|
|
1879
|
+
// 名前の切り出しは描画側と同じ式を使う (`templateRefIds`)
|
|
1880
|
+
for (const id of templateRefIds(row)) {
|
|
1881
|
+
if (!stateIds.has(id)) {
|
|
1882
1882
|
push("row-format", `node "${n.id}" row "${row}" placeholder "{${id}}" が未定義 state`);
|
|
1883
1883
|
}
|
|
1884
1884
|
}
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import type { JSX } from "react";
|
|
2
|
-
import type { LaidNode, Tone, MindBranchNode } from "../types";
|
|
3
|
-
import { TONE } from "../render/tone";
|
|
4
|
-
|
|
5
|
-
const MIND_TONES: Tone[] = ["accent", "teal", "success", "warning", "info", "error"];
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* mind-radial kind ... mindMapRadial preset 用、 中心 + 8 方向 (45 度間隔) の
|
|
9
|
-
* 放射状 branch 配置。 mind-map との違いは「第 1 階層を 45 度で 8 分割」 固定。
|
|
10
|
-
*
|
|
11
|
-
* デザイン方針 (batch3):
|
|
12
|
-
* - canvas 背景に subtle radial gradient (中心を強調)
|
|
13
|
-
* - 中心 circle を 2 重 (外周 halo + 本体) + drop shadow で目立たせる
|
|
14
|
-
* - 各 branch は tone-tinted rounded rect + 中心方向へ tapered edge
|
|
15
|
-
* - branch 番号 (方位) を dot annotation で示す
|
|
16
|
-
*/
|
|
17
|
-
export function MindRadialNode({ node, active }: { node: LaidNode; active: boolean }): JSX.Element {
|
|
18
|
-
const x0 = node.cx - node.w / 2;
|
|
19
|
-
const y0 = node.cy - node.h / 2;
|
|
20
|
-
const mindData = node.mindData;
|
|
21
|
-
if (!mindData) return <g transform={`translate(${x0} ${y0})`} />;
|
|
22
|
-
|
|
23
|
-
const canvasCx = node.w / 2;
|
|
24
|
-
const canvasCy = node.h / 2;
|
|
25
|
-
const maxRadius = Math.min(node.w, node.h) / 2 - 100;
|
|
26
|
-
const gradientId = `mind-radial-bg-${node.id}`;
|
|
27
|
-
const shadowId = `mind-radial-shadow-${node.id}`;
|
|
28
|
-
|
|
29
|
-
const layout = computeRadialLayout(mindData, canvasCx, canvasCy, maxRadius);
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<g transform={`translate(${x0} ${y0})`}>
|
|
33
|
-
<defs>
|
|
34
|
-
<radialGradient id={gradientId} cx="50%" cy="50%" r="60%">
|
|
35
|
-
<stop offset="0%" stopColor="var(--cdl-tone-accent, #2d6a8f)" stopOpacity="0.14" />
|
|
36
|
-
<stop offset="55%" stopColor="var(--cdl-tone-accent, #2d6a8f)" stopOpacity="0.04" />
|
|
37
|
-
<stop offset="100%" stopColor="var(--cdl-tone-accent, #2d6a8f)" stopOpacity="0" />
|
|
38
|
-
</radialGradient>
|
|
39
|
-
<filter id={shadowId} x="-50%" y="-50%" width="200%" height="200%">
|
|
40
|
-
<feGaussianBlur in="SourceAlpha" stdDeviation="4" />
|
|
41
|
-
<feOffset dx="0" dy="2" result="offsetblur" />
|
|
42
|
-
<feComponentTransfer>
|
|
43
|
-
<feFuncA type="linear" slope="0.4" />
|
|
44
|
-
</feComponentTransfer>
|
|
45
|
-
<feMerge>
|
|
46
|
-
<feMergeNode />
|
|
47
|
-
<feMergeNode in="SourceGraphic" />
|
|
48
|
-
</feMerge>
|
|
49
|
-
</filter>
|
|
50
|
-
</defs>
|
|
51
|
-
|
|
52
|
-
<rect
|
|
53
|
-
data-cdl-role="node-body"
|
|
54
|
-
x={0}
|
|
55
|
-
y={0}
|
|
56
|
-
width={node.w}
|
|
57
|
-
height={node.h}
|
|
58
|
-
rx={16}
|
|
59
|
-
fill="var(--cdl-node-fill, #ffffff)"
|
|
60
|
-
stroke={active ? "var(--cdl-tone-accent, #2d6a8f)" : "var(--cdl-divider, #d4d4d8)"}
|
|
61
|
-
strokeWidth={active ? 3 : 1.25}
|
|
62
|
-
/>
|
|
63
|
-
|
|
64
|
-
<circle
|
|
65
|
-
cx={canvasCx}
|
|
66
|
-
cy={canvasCy}
|
|
67
|
-
r={maxRadius + 40}
|
|
68
|
-
fill={`url(#${gradientId})`}
|
|
69
|
-
pointerEvents="none"
|
|
70
|
-
/>
|
|
71
|
-
|
|
72
|
-
{layout.edges.map((e, i) => (
|
|
73
|
-
<line
|
|
74
|
-
key={`edge-${i}`}
|
|
75
|
-
data-cdl-role="mind-radial-edge"
|
|
76
|
-
x1={e.x1}
|
|
77
|
-
y1={e.y1}
|
|
78
|
-
x2={e.x2}
|
|
79
|
-
y2={e.y2}
|
|
80
|
-
stroke={toneColor(e.tone)}
|
|
81
|
-
strokeWidth={2.25}
|
|
82
|
-
strokeOpacity={0.7}
|
|
83
|
-
strokeLinecap="round"
|
|
84
|
-
/>
|
|
85
|
-
))}
|
|
86
|
-
|
|
87
|
-
{/* 中心の周りに敷く光の輪。 意図して淡く、 読ませる要素ではない。
|
|
88
|
-
役割を付けるのは、 consumer が色を当てたり検査から外したりできるようにするため
|
|
89
|
-
(役割の無い要素には CSS も検査も届かない)。 */}
|
|
90
|
-
<circle
|
|
91
|
-
data-cdl-role="mind-radial-halo"
|
|
92
|
-
cx={canvasCx}
|
|
93
|
-
cy={canvasCy}
|
|
94
|
-
r={56}
|
|
95
|
-
fill="var(--cdl-tone-accent, #2d6a8f)"
|
|
96
|
-
fillOpacity={0.18}
|
|
97
|
-
/>
|
|
98
|
-
<circle
|
|
99
|
-
cx={canvasCx}
|
|
100
|
-
cy={canvasCy}
|
|
101
|
-
r={42}
|
|
102
|
-
fill="var(--cdl-tone-accent, #2d6a8f)"
|
|
103
|
-
filter={`url(#${shadowId})`}
|
|
104
|
-
/>
|
|
105
|
-
<text
|
|
106
|
-
x={canvasCx}
|
|
107
|
-
y={canvasCy + 5}
|
|
108
|
-
fontSize={14}
|
|
109
|
-
fontWeight={700}
|
|
110
|
-
textAnchor="middle"
|
|
111
|
-
fill="var(--cdl-text-on-tone, #ffffff)"
|
|
112
|
-
>
|
|
113
|
-
{mindData.rootTitle}
|
|
114
|
-
</text>
|
|
115
|
-
|
|
116
|
-
{layout.nodes.filter((n) => !n.isRoot).map((n) => (
|
|
117
|
-
<g key={`n-${n.id}`}>
|
|
118
|
-
<rect
|
|
119
|
-
x={n.x - 62}
|
|
120
|
-
y={n.y - 22}
|
|
121
|
-
width={124}
|
|
122
|
-
height={44}
|
|
123
|
-
rx={12}
|
|
124
|
-
fill="var(--cdl-chip-fill, #f4f6fb)"
|
|
125
|
-
stroke={toneColor(n.tone)}
|
|
126
|
-
strokeWidth={2}
|
|
127
|
-
/>
|
|
128
|
-
<circle
|
|
129
|
-
cx={n.x - 62 + 12}
|
|
130
|
-
cy={n.y}
|
|
131
|
-
r={5}
|
|
132
|
-
fill={toneColor(n.tone)}
|
|
133
|
-
/>
|
|
134
|
-
<text
|
|
135
|
-
x={n.x + 6}
|
|
136
|
-
y={n.y + 5}
|
|
137
|
-
fontSize={13}
|
|
138
|
-
fontWeight={600}
|
|
139
|
-
textAnchor="middle"
|
|
140
|
-
fill="var(--cdl-chip-text, #1a1f2a)"
|
|
141
|
-
>
|
|
142
|
-
{n.title}
|
|
143
|
-
</text>
|
|
144
|
-
</g>
|
|
145
|
-
))}
|
|
146
|
-
</g>
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
type LayoutNode = {
|
|
151
|
-
id: string;
|
|
152
|
-
title: string;
|
|
153
|
-
x: number;
|
|
154
|
-
y: number;
|
|
155
|
-
tone?: Tone;
|
|
156
|
-
isRoot: boolean;
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
type LayoutEdge = {
|
|
160
|
-
x1: number;
|
|
161
|
-
y1: number;
|
|
162
|
-
x2: number;
|
|
163
|
-
y2: number;
|
|
164
|
-
tone?: Tone;
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
function computeRadialLayout(
|
|
168
|
-
mindData: { rootId: string; rootTitle: string; branches: MindBranchNode[] },
|
|
169
|
-
cx: number,
|
|
170
|
-
cy: number,
|
|
171
|
-
maxRadius: number,
|
|
172
|
-
): { nodes: LayoutNode[]; edges: LayoutEdge[] } {
|
|
173
|
-
const nodes: LayoutNode[] = [];
|
|
174
|
-
const edges: LayoutEdge[] = [];
|
|
175
|
-
|
|
176
|
-
nodes.push({ id: mindData.rootId, title: mindData.rootTitle, x: cx, y: cy, isRoot: true });
|
|
177
|
-
|
|
178
|
-
const childrenOf = new Map<string, MindBranchNode[]>();
|
|
179
|
-
for (const br of mindData.branches) {
|
|
180
|
-
if (!childrenOf.has(br.parent)) childrenOf.set(br.parent, []);
|
|
181
|
-
childrenOf.get(br.parent)!.push(br);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const firstLevel = childrenOf.get(mindData.rootId) ?? [];
|
|
185
|
-
const angleStep = (Math.PI * 2) / 8;
|
|
186
|
-
firstLevel.slice(0, 8).forEach((br, idx) => {
|
|
187
|
-
const angle = -Math.PI / 2 + angleStep * idx;
|
|
188
|
-
const tone = br.tone ?? MIND_TONES[idx % MIND_TONES.length]!;
|
|
189
|
-
const r = maxRadius;
|
|
190
|
-
const x = cx + r * Math.cos(angle);
|
|
191
|
-
const y = cy + r * Math.sin(angle);
|
|
192
|
-
nodes.push({ id: br.id, title: br.title, x, y, tone, isRoot: false });
|
|
193
|
-
const rootR = 42;
|
|
194
|
-
const branchR = 62;
|
|
195
|
-
const x1 = cx + rootR * Math.cos(angle);
|
|
196
|
-
const y1 = cy + rootR * Math.sin(angle);
|
|
197
|
-
const x2 = x - branchR * Math.cos(angle);
|
|
198
|
-
const y2 = y - 22 * Math.sin(angle);
|
|
199
|
-
edges.push({ x1, y1, x2, y2, tone });
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
return { nodes, edges };
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
function toneColor(tone: Tone | undefined): string {
|
|
207
|
-
if (!tone) return TONE.accent;
|
|
208
|
-
return TONE[tone];
|
|
209
|
-
}
|