@cardenelabs/cdl 0.124.0 → 0.125.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/CHANGELOG.md +43 -1
- package/README.md +8 -0
- package/dist/index.cjs +873 -142
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +873 -143
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +858 -142
- 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 +858 -142
- package/dist/react.js.map +1 -1
- package/dist/{render-U6omSNH2.d.cts → render-7yEHIUK1.d.cts} +57 -3
- package/dist/{render-U6omSNH2.d.ts → render-7yEHIUK1.d.ts} +57 -3
- package/package.json +12 -9
- package/src/builder.ts +6 -0
- package/src/index.ts +4 -0
- package/src/kinds/card.tsx +12 -4
- package/src/kinds/generic.tsx +19 -9
- package/src/kinds/station.tsx +105 -19
- package/src/kinds/terminal-mark.tsx +36 -9
- package/src/layout/edges.ts +77 -24
- package/src/layout/lanes.ts +69 -5
- package/src/layout/legend.ts +68 -0
- package/src/layout/metro-path.ts +123 -30
- package/src/layout/nodes.ts +13 -2
- package/src/layout/spec.ts +17 -1
- package/src/layout/tokens.ts +8 -1
- package/src/layout.ts +20 -2
- package/src/render/edge-head.ts +6 -0
- package/src/render/edges.tsx +27 -6
- package/src/render/legend.tsx +120 -0
- package/src/render/nodes.tsx +21 -5
- package/src/render/stage.tsx +255 -10
- package/src/render/template-fields.ts +2 -0
- package/src/types.ts +106 -5
- package/src/validate.ts +5 -0
- package/src/visual-validate.ts +7 -7
|
@@ -125,8 +125,16 @@ type Tone = (typeof TONES)[number];
|
|
|
125
125
|
* 食い違うため、図単位で測り方を揃えられるようにする。
|
|
126
126
|
*/
|
|
127
127
|
type CdlTextMetrics = "proportional" | "monospace";
|
|
128
|
+
/** 凡例に描ける印。 配置・描画・検査はこの 1 つの列を出所にする。 */
|
|
129
|
+
declare const LEGEND_MARKS: readonly ["diamond", "filled-circle", "double-circle", "dotted-line", "solid-line", "curved-line", "rounded-label", "numbered-circle", "station", "arrow"];
|
|
130
|
+
type LegendMark = (typeof LEGEND_MARKS)[number];
|
|
131
|
+
/** 図の下へ置く凡例の 1 項目。 */
|
|
132
|
+
type CdlLegendItem = {
|
|
133
|
+
mark: LegendMark;
|
|
134
|
+
text: string;
|
|
135
|
+
};
|
|
128
136
|
/** 描画できる箱の種類。 検査・記法一覧・型はこの 1 つの列を出所にする。 */
|
|
129
|
-
declare const NODE_KINDS: readonly ["dyn-rect", "dyn-circle", "dyn-arc", "dyn-wave", "dyn-polygon", "actor", "function", "storage", "event", "card", "person", "user-group", "admin", "developer", "external-user", "database", "cache", "queue", "message-bus", "cloud", "cdn", "service", "api", "frontend", "backend", "webhook", "microservice", "signer", "oracle", "merkle-tree", "decision", "chart-pie", "chart-line", "chart-bar", "chart-gauge", "chart-radial", "chart-stat", "chart-waffle", "chart-stacked-bar", "chart-slope", "gantt-timeline", "mind-map", "funnel-stages", "quadrant-matrix", "tree-hierarchy", "journey-map", "shape-file", "shape-folder", "shape-cloud", "shape-cylinder", "shape-hexagon", "shape-diamond", "shape-stack", "shape-person", "shape-window", "shape-terminal", "shape-code-block", "shape-kanban-card", "shape-message-bubble", "shape-gear", "shape-server-rack", "shape-network-node", "shape-mobile-device", "shape-iot-sensor", "shape-robot-arm", "shape-satellite", "shape-smart-contract", "shape-blockchain-block", "shape-rpc-node", "shape-wallet", "shape-nft", "shape-token", "shape-blockchain", "shape-bitcoin-chain", "shape-ethereum-chain", "shape-blockchain-node", "shape-bank", "shape-trust-bank", "shape-payment-provider", "shape-credit-card", "shape-brokerage", "shape-exchange", "shape-atm", "shape-website", "shape-storefront", "shape-warehouse", "shape-online-shop", "shape-cdn-edge", "shape-api-gateway", "shape-auditor", "shape-regulator", "shape-notary", "shape-lawyer", "shape-trader", "shape-customer-service", "mark-start", "mark-end", "station", "sequence-board"];
|
|
137
|
+
declare const NODE_KINDS: readonly ["dyn-rect", "dyn-circle", "dyn-arc", "dyn-wave", "dyn-polygon", "actor", "function", "storage", "event", "card", "person", "user-group", "admin", "developer", "external-user", "database", "cache", "queue", "message-bus", "cloud", "cdn", "service", "api", "frontend", "backend", "webhook", "microservice", "signer", "oracle", "merkle-tree", "decision", "chart-pie", "chart-line", "chart-bar", "chart-gauge", "chart-radial", "chart-stat", "chart-waffle", "chart-stacked-bar", "chart-slope", "gantt-timeline", "mind-map", "funnel-stages", "quadrant-matrix", "tree-hierarchy", "journey-map", "shape-file", "shape-folder", "shape-cloud", "shape-cylinder", "shape-hexagon", "shape-diamond", "shape-stack", "shape-person", "shape-window", "shape-terminal", "shape-code-block", "shape-kanban-card", "shape-message-bubble", "shape-gear", "shape-server-rack", "shape-network-node", "shape-mobile-device", "shape-iot-sensor", "shape-robot-arm", "shape-satellite", "shape-smart-contract", "shape-blockchain-block", "shape-rpc-node", "shape-wallet", "shape-nft", "shape-token", "shape-blockchain", "shape-bitcoin-chain", "shape-ethereum-chain", "shape-blockchain-node", "shape-bank", "shape-trust-bank", "shape-payment-provider", "shape-credit-card", "shape-brokerage", "shape-exchange", "shape-atm", "shape-website", "shape-storefront", "shape-warehouse", "shape-online-shop", "shape-cdn-edge", "shape-api-gateway", "shape-auditor", "shape-regulator", "shape-notary", "shape-lawyer", "shape-trader", "shape-customer-service", "mark-start", "mark-end", "station", "timeline-number", "sequence-board"];
|
|
130
138
|
type NodeKind = (typeof NODE_KINDS)[number];
|
|
131
139
|
type Side = "top" | "right" | "bottom" | "left";
|
|
132
140
|
/**
|
|
@@ -148,7 +156,9 @@ type EdgeStyle = "solid" | "dotted-flow" | "dashed";
|
|
|
148
156
|
* - `"back-detour"` ... 箱の上を回って前の箱へ戻り、途中の箱と交わる時は外側を回る
|
|
149
157
|
* - `"curve"` ... 始点から水平に出て終点へ水平に入る 3 次曲線。 途中の箱は避けず、
|
|
150
158
|
* 2 点が同じ高さなら直線になる。 終点の矢印は水平を向く
|
|
151
|
-
* - `"metro"` ...
|
|
159
|
+
* - `"metro"` ... 通常は横線と傾き ±1 の斜線で結び、角を円弧で丸める。
|
|
160
|
+
* `decision` からの枝は、行き先が同じ横位置なら上下の角から真っ直ぐ、右にあれば右の角から出て曲がる。
|
|
161
|
+
* 左へ戻る `dashed` は行き先側へ横に出して駅の上下へ入る。
|
|
152
162
|
* 途中の箱は避けず、2 点が同じ高さなら横 1 本になる
|
|
153
163
|
*/
|
|
154
164
|
type EdgeRouting = "default" | "back-detour" | "curve" | "metro";
|
|
@@ -291,7 +301,29 @@ type CdlLane = {
|
|
|
291
301
|
*/
|
|
292
302
|
width: number;
|
|
293
303
|
label?: string;
|
|
304
|
+
/**
|
|
305
|
+
* 路線図の担当として描く lane。 `label` を名札の名前、`subtitle` をその補足に使う。
|
|
306
|
+
*
|
|
307
|
+
* 書いた lane だけが名札と案内線を出す。書かない lane の出力は変わらない。
|
|
308
|
+
*/
|
|
309
|
+
metro?: {
|
|
310
|
+
subtitle?: string;
|
|
311
|
+
};
|
|
294
312
|
contain?: boolean;
|
|
313
|
+
/**
|
|
314
|
+
* lane を同じ上端・高さの段階列として描く時の見出し。
|
|
315
|
+
*
|
|
316
|
+
* `number` と `name` は別の SVG text に出るため、利用側は `stage-number` と `stage-name` を
|
|
317
|
+
* 個別に選べる。この lane にある `card` node の `subtitle` は、札の右側へ `stage-note` として
|
|
318
|
+
* 描き、通常の card の補足位置には出さない。
|
|
319
|
+
* `leaderTo` を持つ `card` も、段階列の内外にかかわらず同じ `stage-note` を使う。
|
|
320
|
+
*
|
|
321
|
+
* 書かない lane は既存の縦列のままで、`lane-container` / `lane-label` の入力と出力は変わらない。
|
|
322
|
+
*/
|
|
323
|
+
stage?: {
|
|
324
|
+
number: string;
|
|
325
|
+
name: string;
|
|
326
|
+
};
|
|
295
327
|
/**
|
|
296
328
|
* この lane を何のために置いているか (既定 = `"column"`)。
|
|
297
329
|
*
|
|
@@ -311,6 +343,12 @@ type CdlLane = {
|
|
|
311
343
|
role?: "column" | "overlay";
|
|
312
344
|
/** UML sequence diagram の lifeline (= lane の中央 x に縦の点線を描画) */
|
|
313
345
|
lifeline?: boolean;
|
|
346
|
+
/**
|
|
347
|
+
* lane の中央 x に時間軸の実線を描く。線はこの lane にある描画対象 node の中心間を結ぶ。
|
|
348
|
+
*
|
|
349
|
+
* `lifeline` の点線とは独立しており、両方を書いた lane では両方を描く。
|
|
350
|
+
*/
|
|
351
|
+
timelineAxis?: boolean;
|
|
314
352
|
/**
|
|
315
353
|
* 明示指定した座標。 `posX` と `posY` の **両方**が揃った lane は「置いた位置に置く」
|
|
316
354
|
* 扱いになる。 片方だけでは何も変わらない。
|
|
@@ -336,6 +374,11 @@ type CdlNode = {
|
|
|
336
374
|
title: string;
|
|
337
375
|
eyebrow?: string;
|
|
338
376
|
subtitle?: string;
|
|
377
|
+
/**
|
|
378
|
+
* この箱から指定した node へ矢印なしの補助線を引く。edge ではないため `laid.edges` には入らない。
|
|
379
|
+
* `card` の `subtitle` は札の右側へ `stage-note` として描き、通常の補足位置には出さない。
|
|
380
|
+
*/
|
|
381
|
+
leaderTo?: string;
|
|
339
382
|
/** mono 数値表示用、 `{stateId}` を含めれば state 値で動的置換 */
|
|
340
383
|
value?: string;
|
|
341
384
|
/** kind=storage 時の行 (各 mono、 state 参照可) */
|
|
@@ -2651,6 +2694,8 @@ type CdlDiagram = {
|
|
|
2651
2694
|
* 種類を書いていない図にも当たる。
|
|
2652
2695
|
*/
|
|
2653
2696
|
type?: string;
|
|
2697
|
+
/** SVG 内の図の下へ、指定順の横一列で置く凡例。 */
|
|
2698
|
+
legend?: readonly CdlLegendItem[];
|
|
2654
2699
|
/**
|
|
2655
2700
|
* 矢印をいつ出すか (既定 = `"phase"`、 #582)。
|
|
2656
2701
|
*
|
|
@@ -2836,6 +2881,13 @@ type LaidEdge = CdlEdge & {
|
|
|
2836
2881
|
labelX: number;
|
|
2837
2882
|
labelY: number;
|
|
2838
2883
|
labelAnchor: "start" | "middle" | "end";
|
|
2884
|
+
/** 前の station へ戻る metro 線だけが持つ、専用の点線と矢印を描く目印。 */
|
|
2885
|
+
metroReturn?: true;
|
|
2886
|
+
};
|
|
2887
|
+
type LaidLegendItem = CdlLegendItem & {
|
|
2888
|
+
x: number;
|
|
2889
|
+
/** 文字の baseline。 印はこの値を基準に同じ行へ揃える。 */
|
|
2890
|
+
y: number;
|
|
2839
2891
|
};
|
|
2840
2892
|
/**
|
|
2841
2893
|
* AABB (axis-aligned bounding box)。
|
|
@@ -2869,6 +2921,8 @@ type LaidDiagram = {
|
|
|
2869
2921
|
textMetrics?: "monospace";
|
|
2870
2922
|
/** どの種類の図か (#645)。 `CdlDiagram.type` をそのまま持ち越す */
|
|
2871
2923
|
type?: string;
|
|
2924
|
+
/** 指定がある図だけが持つ、配置済みの凡例。 */
|
|
2925
|
+
legend?: readonly LaidLegendItem[];
|
|
2872
2926
|
viewBox: {
|
|
2873
2927
|
x: number;
|
|
2874
2928
|
y: number;
|
|
@@ -3069,4 +3123,4 @@ type CdlDiagramViewProps = {
|
|
|
3069
3123
|
};
|
|
3070
3124
|
declare function CdlDiagramView({ diagram, laid: laidProp, hideHeader, hideMiniPhaseIndicator, focusPhaseId, debug, emitGeometryWarn, headingLevel, interactiveHandlers }: CdlDiagramViewProps): JSX.Element;
|
|
3071
3125
|
|
|
3072
|
-
export {
|
|
3126
|
+
export { NODE_KINDS as $, type CdlPhase as A, type BBox as B, type CdlDiagram as C, type ChartPieForm as D, type EdgeStyle as E, type ChartSlopeForm as F, EDGE_HEADS as G, EDGE_HEAD_DEFAULT as H, EDGE_HEAD_FILLS as I, type JourneyEmotion as J, EDGE_HEAD_FILL_DEFAULT as K, type LaidDiagram as L, EDGE_REVEALS as M, type NodeKind as N, EDGE_REVEAL_DEFAULT as O, EDGE_STYLES as P, type QuadrantKey as Q, type RowMark as R, type Signal as S, type Tone as T, type EdgeReveal as U, type InteractiveHandlerMap as V, type InteractiveSignalsAccessor as W, LEGEND_MARKS as X, type LaidLane as Y, type LaidLegendItem as Z, type LegendMark as _, type CdlInput as a, RELATION_FOCUSES as a0, RELATION_FOCUS_DEFAULT as a1, type RelationFocus as a2, type ScrollProgressHandle as a3, TONES as a4, computeEventBindingKey as a5, computeFormulaKey as a6, computeInputSignalKey as a7, computeScrollTriggerKey as a8, computed as a9, createInteractiveSignalsAccessor as aa, createScrollProgressHandle as ab, createScrollProgressSignals as ac, hasInteractivePrimitives as ad, resolveLang as ae, sequenceBoardMetrics as af, sequenceStepId as ag, signal as ah, type CdlLane as b, type CdlNode as c, type Side as d, type EdgeRole as e, type EdgeHead as f, type EdgeHeadFill as g, type EdgeRouting as h, type CdlState as i, type CdlInputOption as j, type CdlEventTarget as k, type CdlLang as l, type CdlTextMetrics as m, type Computed as n, type CdlDerivedValue as o, type CdlScrollTrigger as p, type CdlDiagramViewProps as q, type LaidEdge as r, type LaidNode as s, type CdlEdge as t, type TreeNodePayload as u, CdlDiagramView as v, type CdlEventBinding as w, type CdlEventKind as x, type CdlFormula as y, type CdlLegendItem as z };
|
|
@@ -125,8 +125,16 @@ type Tone = (typeof TONES)[number];
|
|
|
125
125
|
* 食い違うため、図単位で測り方を揃えられるようにする。
|
|
126
126
|
*/
|
|
127
127
|
type CdlTextMetrics = "proportional" | "monospace";
|
|
128
|
+
/** 凡例に描ける印。 配置・描画・検査はこの 1 つの列を出所にする。 */
|
|
129
|
+
declare const LEGEND_MARKS: readonly ["diamond", "filled-circle", "double-circle", "dotted-line", "solid-line", "curved-line", "rounded-label", "numbered-circle", "station", "arrow"];
|
|
130
|
+
type LegendMark = (typeof LEGEND_MARKS)[number];
|
|
131
|
+
/** 図の下へ置く凡例の 1 項目。 */
|
|
132
|
+
type CdlLegendItem = {
|
|
133
|
+
mark: LegendMark;
|
|
134
|
+
text: string;
|
|
135
|
+
};
|
|
128
136
|
/** 描画できる箱の種類。 検査・記法一覧・型はこの 1 つの列を出所にする。 */
|
|
129
|
-
declare const NODE_KINDS: readonly ["dyn-rect", "dyn-circle", "dyn-arc", "dyn-wave", "dyn-polygon", "actor", "function", "storage", "event", "card", "person", "user-group", "admin", "developer", "external-user", "database", "cache", "queue", "message-bus", "cloud", "cdn", "service", "api", "frontend", "backend", "webhook", "microservice", "signer", "oracle", "merkle-tree", "decision", "chart-pie", "chart-line", "chart-bar", "chart-gauge", "chart-radial", "chart-stat", "chart-waffle", "chart-stacked-bar", "chart-slope", "gantt-timeline", "mind-map", "funnel-stages", "quadrant-matrix", "tree-hierarchy", "journey-map", "shape-file", "shape-folder", "shape-cloud", "shape-cylinder", "shape-hexagon", "shape-diamond", "shape-stack", "shape-person", "shape-window", "shape-terminal", "shape-code-block", "shape-kanban-card", "shape-message-bubble", "shape-gear", "shape-server-rack", "shape-network-node", "shape-mobile-device", "shape-iot-sensor", "shape-robot-arm", "shape-satellite", "shape-smart-contract", "shape-blockchain-block", "shape-rpc-node", "shape-wallet", "shape-nft", "shape-token", "shape-blockchain", "shape-bitcoin-chain", "shape-ethereum-chain", "shape-blockchain-node", "shape-bank", "shape-trust-bank", "shape-payment-provider", "shape-credit-card", "shape-brokerage", "shape-exchange", "shape-atm", "shape-website", "shape-storefront", "shape-warehouse", "shape-online-shop", "shape-cdn-edge", "shape-api-gateway", "shape-auditor", "shape-regulator", "shape-notary", "shape-lawyer", "shape-trader", "shape-customer-service", "mark-start", "mark-end", "station", "sequence-board"];
|
|
137
|
+
declare const NODE_KINDS: readonly ["dyn-rect", "dyn-circle", "dyn-arc", "dyn-wave", "dyn-polygon", "actor", "function", "storage", "event", "card", "person", "user-group", "admin", "developer", "external-user", "database", "cache", "queue", "message-bus", "cloud", "cdn", "service", "api", "frontend", "backend", "webhook", "microservice", "signer", "oracle", "merkle-tree", "decision", "chart-pie", "chart-line", "chart-bar", "chart-gauge", "chart-radial", "chart-stat", "chart-waffle", "chart-stacked-bar", "chart-slope", "gantt-timeline", "mind-map", "funnel-stages", "quadrant-matrix", "tree-hierarchy", "journey-map", "shape-file", "shape-folder", "shape-cloud", "shape-cylinder", "shape-hexagon", "shape-diamond", "shape-stack", "shape-person", "shape-window", "shape-terminal", "shape-code-block", "shape-kanban-card", "shape-message-bubble", "shape-gear", "shape-server-rack", "shape-network-node", "shape-mobile-device", "shape-iot-sensor", "shape-robot-arm", "shape-satellite", "shape-smart-contract", "shape-blockchain-block", "shape-rpc-node", "shape-wallet", "shape-nft", "shape-token", "shape-blockchain", "shape-bitcoin-chain", "shape-ethereum-chain", "shape-blockchain-node", "shape-bank", "shape-trust-bank", "shape-payment-provider", "shape-credit-card", "shape-brokerage", "shape-exchange", "shape-atm", "shape-website", "shape-storefront", "shape-warehouse", "shape-online-shop", "shape-cdn-edge", "shape-api-gateway", "shape-auditor", "shape-regulator", "shape-notary", "shape-lawyer", "shape-trader", "shape-customer-service", "mark-start", "mark-end", "station", "timeline-number", "sequence-board"];
|
|
130
138
|
type NodeKind = (typeof NODE_KINDS)[number];
|
|
131
139
|
type Side = "top" | "right" | "bottom" | "left";
|
|
132
140
|
/**
|
|
@@ -148,7 +156,9 @@ type EdgeStyle = "solid" | "dotted-flow" | "dashed";
|
|
|
148
156
|
* - `"back-detour"` ... 箱の上を回って前の箱へ戻り、途中の箱と交わる時は外側を回る
|
|
149
157
|
* - `"curve"` ... 始点から水平に出て終点へ水平に入る 3 次曲線。 途中の箱は避けず、
|
|
150
158
|
* 2 点が同じ高さなら直線になる。 終点の矢印は水平を向く
|
|
151
|
-
* - `"metro"` ...
|
|
159
|
+
* - `"metro"` ... 通常は横線と傾き ±1 の斜線で結び、角を円弧で丸める。
|
|
160
|
+
* `decision` からの枝は、行き先が同じ横位置なら上下の角から真っ直ぐ、右にあれば右の角から出て曲がる。
|
|
161
|
+
* 左へ戻る `dashed` は行き先側へ横に出して駅の上下へ入る。
|
|
152
162
|
* 途中の箱は避けず、2 点が同じ高さなら横 1 本になる
|
|
153
163
|
*/
|
|
154
164
|
type EdgeRouting = "default" | "back-detour" | "curve" | "metro";
|
|
@@ -291,7 +301,29 @@ type CdlLane = {
|
|
|
291
301
|
*/
|
|
292
302
|
width: number;
|
|
293
303
|
label?: string;
|
|
304
|
+
/**
|
|
305
|
+
* 路線図の担当として描く lane。 `label` を名札の名前、`subtitle` をその補足に使う。
|
|
306
|
+
*
|
|
307
|
+
* 書いた lane だけが名札と案内線を出す。書かない lane の出力は変わらない。
|
|
308
|
+
*/
|
|
309
|
+
metro?: {
|
|
310
|
+
subtitle?: string;
|
|
311
|
+
};
|
|
294
312
|
contain?: boolean;
|
|
313
|
+
/**
|
|
314
|
+
* lane を同じ上端・高さの段階列として描く時の見出し。
|
|
315
|
+
*
|
|
316
|
+
* `number` と `name` は別の SVG text に出るため、利用側は `stage-number` と `stage-name` を
|
|
317
|
+
* 個別に選べる。この lane にある `card` node の `subtitle` は、札の右側へ `stage-note` として
|
|
318
|
+
* 描き、通常の card の補足位置には出さない。
|
|
319
|
+
* `leaderTo` を持つ `card` も、段階列の内外にかかわらず同じ `stage-note` を使う。
|
|
320
|
+
*
|
|
321
|
+
* 書かない lane は既存の縦列のままで、`lane-container` / `lane-label` の入力と出力は変わらない。
|
|
322
|
+
*/
|
|
323
|
+
stage?: {
|
|
324
|
+
number: string;
|
|
325
|
+
name: string;
|
|
326
|
+
};
|
|
295
327
|
/**
|
|
296
328
|
* この lane を何のために置いているか (既定 = `"column"`)。
|
|
297
329
|
*
|
|
@@ -311,6 +343,12 @@ type CdlLane = {
|
|
|
311
343
|
role?: "column" | "overlay";
|
|
312
344
|
/** UML sequence diagram の lifeline (= lane の中央 x に縦の点線を描画) */
|
|
313
345
|
lifeline?: boolean;
|
|
346
|
+
/**
|
|
347
|
+
* lane の中央 x に時間軸の実線を描く。線はこの lane にある描画対象 node の中心間を結ぶ。
|
|
348
|
+
*
|
|
349
|
+
* `lifeline` の点線とは独立しており、両方を書いた lane では両方を描く。
|
|
350
|
+
*/
|
|
351
|
+
timelineAxis?: boolean;
|
|
314
352
|
/**
|
|
315
353
|
* 明示指定した座標。 `posX` と `posY` の **両方**が揃った lane は「置いた位置に置く」
|
|
316
354
|
* 扱いになる。 片方だけでは何も変わらない。
|
|
@@ -336,6 +374,11 @@ type CdlNode = {
|
|
|
336
374
|
title: string;
|
|
337
375
|
eyebrow?: string;
|
|
338
376
|
subtitle?: string;
|
|
377
|
+
/**
|
|
378
|
+
* この箱から指定した node へ矢印なしの補助線を引く。edge ではないため `laid.edges` には入らない。
|
|
379
|
+
* `card` の `subtitle` は札の右側へ `stage-note` として描き、通常の補足位置には出さない。
|
|
380
|
+
*/
|
|
381
|
+
leaderTo?: string;
|
|
339
382
|
/** mono 数値表示用、 `{stateId}` を含めれば state 値で動的置換 */
|
|
340
383
|
value?: string;
|
|
341
384
|
/** kind=storage 時の行 (各 mono、 state 参照可) */
|
|
@@ -2651,6 +2694,8 @@ type CdlDiagram = {
|
|
|
2651
2694
|
* 種類を書いていない図にも当たる。
|
|
2652
2695
|
*/
|
|
2653
2696
|
type?: string;
|
|
2697
|
+
/** SVG 内の図の下へ、指定順の横一列で置く凡例。 */
|
|
2698
|
+
legend?: readonly CdlLegendItem[];
|
|
2654
2699
|
/**
|
|
2655
2700
|
* 矢印をいつ出すか (既定 = `"phase"`、 #582)。
|
|
2656
2701
|
*
|
|
@@ -2836,6 +2881,13 @@ type LaidEdge = CdlEdge & {
|
|
|
2836
2881
|
labelX: number;
|
|
2837
2882
|
labelY: number;
|
|
2838
2883
|
labelAnchor: "start" | "middle" | "end";
|
|
2884
|
+
/** 前の station へ戻る metro 線だけが持つ、専用の点線と矢印を描く目印。 */
|
|
2885
|
+
metroReturn?: true;
|
|
2886
|
+
};
|
|
2887
|
+
type LaidLegendItem = CdlLegendItem & {
|
|
2888
|
+
x: number;
|
|
2889
|
+
/** 文字の baseline。 印はこの値を基準に同じ行へ揃える。 */
|
|
2890
|
+
y: number;
|
|
2839
2891
|
};
|
|
2840
2892
|
/**
|
|
2841
2893
|
* AABB (axis-aligned bounding box)。
|
|
@@ -2869,6 +2921,8 @@ type LaidDiagram = {
|
|
|
2869
2921
|
textMetrics?: "monospace";
|
|
2870
2922
|
/** どの種類の図か (#645)。 `CdlDiagram.type` をそのまま持ち越す */
|
|
2871
2923
|
type?: string;
|
|
2924
|
+
/** 指定がある図だけが持つ、配置済みの凡例。 */
|
|
2925
|
+
legend?: readonly LaidLegendItem[];
|
|
2872
2926
|
viewBox: {
|
|
2873
2927
|
x: number;
|
|
2874
2928
|
y: number;
|
|
@@ -3069,4 +3123,4 @@ type CdlDiagramViewProps = {
|
|
|
3069
3123
|
};
|
|
3070
3124
|
declare function CdlDiagramView({ diagram, laid: laidProp, hideHeader, hideMiniPhaseIndicator, focusPhaseId, debug, emitGeometryWarn, headingLevel, interactiveHandlers }: CdlDiagramViewProps): JSX.Element;
|
|
3071
3125
|
|
|
3072
|
-
export {
|
|
3126
|
+
export { NODE_KINDS as $, type CdlPhase as A, type BBox as B, type CdlDiagram as C, type ChartPieForm as D, type EdgeStyle as E, type ChartSlopeForm as F, EDGE_HEADS as G, EDGE_HEAD_DEFAULT as H, EDGE_HEAD_FILLS as I, type JourneyEmotion as J, EDGE_HEAD_FILL_DEFAULT as K, type LaidDiagram as L, EDGE_REVEALS as M, type NodeKind as N, EDGE_REVEAL_DEFAULT as O, EDGE_STYLES as P, type QuadrantKey as Q, type RowMark as R, type Signal as S, type Tone as T, type EdgeReveal as U, type InteractiveHandlerMap as V, type InteractiveSignalsAccessor as W, LEGEND_MARKS as X, type LaidLane as Y, type LaidLegendItem as Z, type LegendMark as _, type CdlInput as a, RELATION_FOCUSES as a0, RELATION_FOCUS_DEFAULT as a1, type RelationFocus as a2, type ScrollProgressHandle as a3, TONES as a4, computeEventBindingKey as a5, computeFormulaKey as a6, computeInputSignalKey as a7, computeScrollTriggerKey as a8, computed as a9, createInteractiveSignalsAccessor as aa, createScrollProgressHandle as ab, createScrollProgressSignals as ac, hasInteractivePrimitives as ad, resolveLang as ae, sequenceBoardMetrics as af, sequenceStepId as ag, signal as ah, type CdlLane as b, type CdlNode as c, type Side as d, type EdgeRole as e, type EdgeHead as f, type EdgeHeadFill as g, type EdgeRouting as h, type CdlState as i, type CdlInputOption as j, type CdlEventTarget as k, type CdlLang as l, type CdlTextMetrics as m, type Computed as n, type CdlDerivedValue as o, type CdlScrollTrigger as p, type CdlDiagramViewProps as q, type LaidEdge as r, type LaidNode as s, type CdlEdge as t, type TreeNodePayload as u, CdlDiagramView as v, type CdlEventBinding as w, type CdlEventKind as x, type CdlFormula as y, type CdlLegendItem as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cardenelabs/cdl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.125.0",
|
|
4
4
|
"description": "CDL (Chainome Diagram Language). Mermaid-like declarative DSL that compiles to animated SVG diagrams. Built for blockchain / Solidity flows, generic enough for sequence / flow / state / ER / topology diagrams.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "cardene777",
|
|
@@ -57,6 +57,16 @@
|
|
|
57
57
|
"node": ">=18"
|
|
58
58
|
},
|
|
59
59
|
"sideEffects": false,
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "tsup",
|
|
62
|
+
"dev": "tsup --watch",
|
|
63
|
+
"typecheck": "tsc --noEmit",
|
|
64
|
+
"test": "cd ../.. && vitest run packages/cdl",
|
|
65
|
+
"lint": "tsc --noEmit",
|
|
66
|
+
"prepublishOnly": "pnpm run typecheck && pnpm run build",
|
|
67
|
+
"prepack": "node scripts/prepack-check.mjs",
|
|
68
|
+
"postpack": "node scripts/prepack-check.mjs"
|
|
69
|
+
},
|
|
60
70
|
"peerDependencies": {
|
|
61
71
|
"react": "^19.0.0",
|
|
62
72
|
"react-dom": "^19.0.0"
|
|
@@ -68,12 +78,5 @@
|
|
|
68
78
|
"typedoc": "^0.28.19",
|
|
69
79
|
"typedoc-plugin-markdown": "^4.12.0",
|
|
70
80
|
"typescript": "^5.8.0"
|
|
71
|
-
},
|
|
72
|
-
"scripts": {
|
|
73
|
-
"build": "tsup",
|
|
74
|
-
"dev": "tsup --watch",
|
|
75
|
-
"typecheck": "tsc --noEmit",
|
|
76
|
-
"test": "cd ../.. && vitest run packages/cdl",
|
|
77
|
-
"lint": "tsc --noEmit"
|
|
78
81
|
}
|
|
79
|
-
}
|
|
82
|
+
}
|
package/src/builder.ts
CHANGED
|
@@ -1670,6 +1670,8 @@ export function diagram(
|
|
|
1670
1670
|
* この図がどの種類か (#645)。 詳しくは `CdlDiagram.type` の説明を参照。
|
|
1671
1671
|
*/
|
|
1672
1672
|
type?: string;
|
|
1673
|
+
/** SVG 内の図の下へ置く共通凡例。 */
|
|
1674
|
+
legend?: CdlDiagram["legend"];
|
|
1673
1675
|
},
|
|
1674
1676
|
): DiagramBuilder {
|
|
1675
1677
|
const lanes: CdlLane[] = [];
|
|
@@ -3825,6 +3827,10 @@ export function diagram(
|
|
|
3825
3827
|
...(options.textMetrics !== undefined ? { textMetrics: options.textMetrics } : {}),
|
|
3826
3828
|
// 種類の名前も同じ経路で渡し、空文字の扱いを `layout` の 1 箇所に揃える
|
|
3827
3829
|
...(options.type !== undefined ? { type: options.type } : {}),
|
|
3830
|
+
// 空の並びは書かなかった図と同じ形にする。 凡例なしの JSON と配置を変えない
|
|
3831
|
+
...(options.legend && options.legend.length > 0
|
|
3832
|
+
? { legend: options.legend.map((item) => ({ ...item })) }
|
|
3833
|
+
: {}),
|
|
3828
3834
|
// inputs / formulas / scrollTriggers / eventBindings は空なら省略 (static diagram の JSON 形状維持)
|
|
3829
3835
|
...(inputs.length > 0 ? { inputs: [...inputs] } : {}),
|
|
3830
3836
|
...(formulas.length > 0 ? { formulas: [...formulas] } : {}),
|
package/src/index.ts
CHANGED
|
@@ -303,6 +303,7 @@ export type {
|
|
|
303
303
|
} from "./presets";
|
|
304
304
|
export type {
|
|
305
305
|
CdlDiagram,
|
|
306
|
+
CdlLegendItem,
|
|
306
307
|
CdlEdge,
|
|
307
308
|
ChartPieForm,
|
|
308
309
|
ChartSlopeForm,
|
|
@@ -321,10 +322,12 @@ export type {
|
|
|
321
322
|
LaidDiagram,
|
|
322
323
|
LaidEdge,
|
|
323
324
|
LaidLane,
|
|
325
|
+
LaidLegendItem,
|
|
324
326
|
LaidNode,
|
|
325
327
|
NodeKind,
|
|
326
328
|
Side,
|
|
327
329
|
Tone,
|
|
330
|
+
LegendMark,
|
|
328
331
|
} from "./types";
|
|
329
332
|
// 値としての tone 一覧。 downstream (dragon の parser 等) が受理できる色名の集合を
|
|
330
333
|
// 手書きせずに済ませるため公開する。
|
|
@@ -342,6 +345,7 @@ export {
|
|
|
342
345
|
EDGE_HEAD_FILL_DEFAULT,
|
|
343
346
|
EDGE_REVEALS,
|
|
344
347
|
EDGE_REVEAL_DEFAULT,
|
|
348
|
+
LEGEND_MARKS,
|
|
345
349
|
EDGE_STYLES,
|
|
346
350
|
RELATION_FOCUSES,
|
|
347
351
|
RELATION_FOCUS_DEFAULT,
|
package/src/kinds/card.tsx
CHANGED
|
@@ -6,7 +6,15 @@ import { SUBTITLE_SIZE, 行送りの倍率, 説明文を割り付ける } from "
|
|
|
6
6
|
import { 余りを分ける, 目印なしの繰り上げ } from "../layout/spec";
|
|
7
7
|
import { NODE_SIZE } from "../layout/tokens";
|
|
8
8
|
|
|
9
|
-
export function CardNode({
|
|
9
|
+
export function CardNode({
|
|
10
|
+
node,
|
|
11
|
+
active,
|
|
12
|
+
compactTitleInset,
|
|
13
|
+
}: {
|
|
14
|
+
node: LaidNode;
|
|
15
|
+
active: boolean;
|
|
16
|
+
compactTitleInset?: number;
|
|
17
|
+
}): JSX.Element {
|
|
10
18
|
const accent = nodeAccent(node, "var(--cdl-tone-accent, #2d6a8f)");
|
|
11
19
|
const x = node.cx - node.w / 2;
|
|
12
20
|
const y = node.cy - node.h / 2;
|
|
@@ -32,12 +40,12 @@ export function CardNode({ node, active }: { node: LaidNode; active: boolean }):
|
|
|
32
40
|
});
|
|
33
41
|
const ずらし = 繰り上げ - 余り;
|
|
34
42
|
const titleY = compact ? (小型に呼び名 ? node.h / 2 - 2 : node.h / 2 + 8) : 86 - ずらし;
|
|
35
|
-
const titleX = compact ? node.w / 2 : 26;
|
|
36
|
-
const paddingX = compact ?
|
|
43
|
+
const titleX = compact ? compactTitleInset ?? node.w / 2 : 26;
|
|
44
|
+
const paddingX = compact ? (compactTitleInset ?? 8) * 2 : 52;
|
|
37
45
|
const availableW = node.w - paddingX;
|
|
38
46
|
const initialTitleSize = compact ? 20 : 24;
|
|
39
47
|
const titleSize = fitTextSize(node.title, availableW, initialTitleSize, 11);
|
|
40
|
-
const textAnchor = compact ? ("middle" as const) : ("start" as const);
|
|
48
|
+
const textAnchor = compact && compactTitleInset === undefined ? ("middle" as const) : ("start" as const);
|
|
41
49
|
const subtitleY = 124 - ずらし;
|
|
42
50
|
// 2 行を置けるのは、題名の下端に触れず箱の下端も越えない時だけ (#843)。
|
|
43
51
|
// 判定は最大の大きさ (21) で行う = 実際はこれ以下に収まるので、入ると判定した箱には必ず入る
|
package/src/kinds/generic.tsx
CHANGED
|
@@ -238,6 +238,8 @@ export function GenericNode({
|
|
|
238
238
|
stateValues,
|
|
239
239
|
値の名前,
|
|
240
240
|
textMetrics = "proportional",
|
|
241
|
+
showKindIcon = true,
|
|
242
|
+
metroDecision = false,
|
|
241
243
|
}: {
|
|
242
244
|
node: LaidNode;
|
|
243
245
|
active: boolean;
|
|
@@ -246,6 +248,10 @@ export function GenericNode({
|
|
|
246
248
|
/** 入力欄の値ごとの名前 (#855)。 値と行の差し込みに使う */
|
|
247
249
|
値の名前?: 値の名前の表;
|
|
248
250
|
textMetrics?: CdlTextMetrics;
|
|
251
|
+
/** 路線図の分かれ道のように、箱の形自体が種類を示す時は種類の印を重ねない。 */
|
|
252
|
+
showKindIcon?: boolean;
|
|
253
|
+
/** `routing: "metro"` の線が出入りする分かれ道だけに見本の寸法を使う。 */
|
|
254
|
+
metroDecision?: boolean;
|
|
249
255
|
}): JSX.Element {
|
|
250
256
|
const base = STYLES[node.kind] ?? FALLBACK;
|
|
251
257
|
// `tone` 指定があれば kind の既定色を置き換える。 色を決める式は `node-tone.ts` に集約。
|
|
@@ -281,13 +287,13 @@ export function GenericNode({
|
|
|
281
287
|
const cy = h / 2;
|
|
282
288
|
// diamond の上下頂点を内側に 8px padding して stroke clip を回避。
|
|
283
289
|
// 旧実装は M cx 0 で上頂点が container 上端と完全一致、 strokeWidth=3 で半分が out of bounds に。
|
|
284
|
-
const pad = 8;
|
|
290
|
+
const pad = metroDecision ? 0 : 8;
|
|
285
291
|
return (
|
|
286
292
|
<path
|
|
287
293
|
d={`M ${cx} ${pad} L ${w - pad} ${cy} L ${cx} ${h - pad} L ${pad} ${cy} Z`}
|
|
288
294
|
fill="var(--cdl-node-fill, #ffffff)"
|
|
289
295
|
stroke={active ? style.accent : "var(--cdl-divider, #d4d4d8)"}
|
|
290
|
-
strokeWidth={active ? 3 : 1.25}
|
|
296
|
+
strokeWidth={metroDecision ? 1.5 : active ? 3 : 1.25}
|
|
291
297
|
/>
|
|
292
298
|
);
|
|
293
299
|
}
|
|
@@ -401,13 +407,15 @@ export function GenericNode({
|
|
|
401
407
|
*/
|
|
402
408
|
const 絵の芯 = centered && STYLES[node.kind]?.shape === "cloud" ? h * CLOUD_ART_CENTER_RISE : 0;
|
|
403
409
|
const eyebrowY = centered ? h / 2 - 26 - 絵の芯 : 32 + cylinderShift + 余り;
|
|
404
|
-
const titleY =
|
|
410
|
+
const titleY = metroDecision
|
|
411
|
+
? h / 2 + 9
|
|
412
|
+
: genericTitleBaselineY(node.kind, h, hasRows) - ずらし - 絵の芯;
|
|
405
413
|
const subtitleY = centered ? h / 2 + 28 - 絵の芯 : 96 + cylinderShift - ずらし;
|
|
406
414
|
// icon 位置 (diamond / cloud は title の真右の少し上、 中心配置に合わせる)
|
|
407
415
|
const iconX = centered ? w / 2 + 40 : w - 48;
|
|
408
416
|
// 目印も字と同じ芯に合わせる (#588)。 字だけ動かすと目印との間隔が変わる
|
|
409
417
|
const iconY = centered ? h / 2 - 44 - 絵の芯 : 18 + cylinderShift;
|
|
410
|
-
const dominantBaseline = centered ? ("middle" as const) : undefined;
|
|
418
|
+
const dominantBaseline = centered && !metroDecision ? ("middle" as const) : undefined;
|
|
411
419
|
|
|
412
420
|
return (
|
|
413
421
|
<g
|
|
@@ -420,16 +428,18 @@ export function GenericNode({
|
|
|
420
428
|
>
|
|
421
429
|
{shapeEl}
|
|
422
430
|
{/* icon */}
|
|
423
|
-
|
|
424
|
-
<
|
|
425
|
-
|
|
426
|
-
|
|
431
|
+
{showKindIcon && (
|
|
432
|
+
<g transform={`translate(${iconX} ${iconY}) scale(1)`} data-cdl-role="node-kind-icon">
|
|
433
|
+
<rect width={24} height={24} fill="transparent" />
|
|
434
|
+
<path d={style.icon} fill={style.eyebrowColor} opacity={0.7} transform="scale(1)" />
|
|
435
|
+
</g>
|
|
436
|
+
)}
|
|
427
437
|
{node.eyebrow && (
|
|
428
438
|
<text x={textX} y={eyebrowY} fontSize={17} fontWeight={700} letterSpacing={1.5} fill={paint.onSolid ? "var(--cdl-node-fill, #ffffff)" : style.eyebrowColor} textAnchor={textAnchor} dominantBaseline={dominantBaseline}>
|
|
429
439
|
{node.eyebrow}
|
|
430
440
|
</text>
|
|
431
441
|
)}
|
|
432
|
-
<text data-cdl-role="node-label" x={textX} y={titleY} fontSize={20} fontWeight={700} fill={paint.onSolid ? "var(--cdl-node-fill, #ffffff)" : "var(--cdl-text, #1a1f2a)"} textAnchor={textAnchor} dominantBaseline={dominantBaseline} style={node.kind === "function" || node.kind === "api" ? { fontFamily: "'JetBrains Mono', monospace" } : undefined}>
|
|
442
|
+
<text data-cdl-role="node-label" x={textX} y={titleY} fontSize={metroDecision ? 26 : 20} fontWeight={700} fill={paint.onSolid ? "var(--cdl-node-fill, #ffffff)" : "var(--cdl-text, #1a1f2a)"} textAnchor={textAnchor} dominantBaseline={dominantBaseline} style={node.kind === "function" || node.kind === "api" ? { fontFamily: "'JetBrains Mono', monospace" } : undefined}>
|
|
433
443
|
{node.kind === "function" || node.kind === "api" ? 等幅の字(node.title, 20) : node.title}
|
|
434
444
|
</text>
|
|
435
445
|
{node.subtitle && (
|
package/src/kinds/station.tsx
CHANGED
|
@@ -1,29 +1,115 @@
|
|
|
1
1
|
import type { JSX } from "react";
|
|
2
2
|
|
|
3
|
+
import { TIMELINE_NUMBER_STROKE, titleUsableWidth } from "../layout/spec";
|
|
3
4
|
import type { LaidNode } from "../types";
|
|
4
|
-
import {
|
|
5
|
+
import { fitTextSize, MIN_TEXT_SIZE } from "./compact-title";
|
|
5
6
|
import { nodeAccent } from "./node-tone";
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
-
const
|
|
8
|
+
export const METRO_STATION_RADIUS = 16;
|
|
9
|
+
export const METRO_STATION_STROKE_WIDTH = 6;
|
|
10
|
+
const METRO_STATION_RADIUS_CSS = `var(--cdl-metro-station-radius, ${METRO_STATION_RADIUS}px)`;
|
|
11
|
+
const METRO_STATION_STROKE_WIDTH_CSS = `var(--cdl-metro-station-stroke-width, ${METRO_STATION_STROKE_WIDTH}px)`;
|
|
9
12
|
const STATION_DEFAULT = "var(--cdl-tone-accent, #2d6a8f)";
|
|
13
|
+
const STATION_NAME_FONT_SIZE = 25;
|
|
14
|
+
const STATION_NAME_GAP = 18;
|
|
15
|
+
const STATION_NAME_RIGHT_GAP = 32;
|
|
16
|
+
const STATION_NAME_RIGHT_BASELINE = 9;
|
|
17
|
+
const STATION_SUBTITLE_FONT_SIZE = 18;
|
|
18
|
+
const STATION_SUBTITLE_GAP = 22;
|
|
10
19
|
|
|
11
20
|
/** 路線図の線路上に置く駅。名前と説明は丸を隠さないよう外側に置く。 */
|
|
12
|
-
export function StationNode({
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
export function StationNode({
|
|
22
|
+
node,
|
|
23
|
+
nameRight = false,
|
|
24
|
+
}: {
|
|
25
|
+
node: LaidNode;
|
|
26
|
+
active: boolean;
|
|
27
|
+
/** 線路が駅へ真上から入る時は、枝の札と重ならないよう名前を右へ置く。 */
|
|
28
|
+
nameRight?: boolean;
|
|
29
|
+
}): JSX.Element {
|
|
30
|
+
const name = node.title.trim();
|
|
31
|
+
const subtitle = node.subtitle?.trim() ?? "";
|
|
32
|
+
return (
|
|
33
|
+
<g>
|
|
34
|
+
<circle
|
|
35
|
+
data-cdl-role="node-body"
|
|
36
|
+
data-cdl-mark="station"
|
|
37
|
+
cx={node.cx}
|
|
38
|
+
cy={node.cy}
|
|
39
|
+
r={METRO_STATION_RADIUS}
|
|
40
|
+
fill="var(--cdl-node-fill, #ffffff)"
|
|
41
|
+
stroke={nodeAccent(node, STATION_DEFAULT)}
|
|
42
|
+
strokeWidth={METRO_STATION_STROKE_WIDTH}
|
|
43
|
+
style={{ r: METRO_STATION_RADIUS_CSS, strokeWidth: METRO_STATION_STROKE_WIDTH_CSS }}
|
|
44
|
+
/>
|
|
45
|
+
{name !== "" && (
|
|
46
|
+
<text
|
|
47
|
+
data-cdl-role="shape-name"
|
|
48
|
+
x={nameRight ? node.cx + STATION_NAME_RIGHT_GAP : node.cx}
|
|
49
|
+
y={nameRight
|
|
50
|
+
? node.cy + STATION_NAME_RIGHT_BASELINE
|
|
51
|
+
: node.cy - METRO_STATION_RADIUS - STATION_NAME_GAP}
|
|
52
|
+
textAnchor={nameRight ? "start" : "middle"}
|
|
53
|
+
fontSize={STATION_NAME_FONT_SIZE}
|
|
54
|
+
fontWeight={700}
|
|
55
|
+
fill="var(--cdl-text, #23201b)"
|
|
56
|
+
stroke="var(--cdl-node-fill, #ffffff)"
|
|
57
|
+
strokeWidth={8}
|
|
58
|
+
strokeLinejoin="round"
|
|
59
|
+
paintOrder="stroke"
|
|
60
|
+
>
|
|
61
|
+
{name}
|
|
62
|
+
</text>
|
|
63
|
+
)}
|
|
64
|
+
{subtitle !== "" && (
|
|
65
|
+
<text
|
|
66
|
+
data-cdl-role="shape-value"
|
|
67
|
+
x={node.cx}
|
|
68
|
+
y={node.cy + METRO_STATION_RADIUS + STATION_SUBTITLE_GAP}
|
|
69
|
+
textAnchor="middle"
|
|
70
|
+
fontSize={STATION_SUBTITLE_FONT_SIZE}
|
|
71
|
+
fill="var(--cdl-text-dim, #5a6270)"
|
|
72
|
+
>
|
|
73
|
+
{subtitle}
|
|
74
|
+
</text>
|
|
75
|
+
)}
|
|
76
|
+
</g>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** 時間軸の実線上に置き、番号を中央に収める丸。路線図の駅の意味は兼ねない。 */
|
|
81
|
+
export function TimelineNumberNode({ node }: { node: LaidNode }): JSX.Element {
|
|
82
|
+
const radius = Math.max(0, Math.min(node.w, node.h) / 2 - TIMELINE_NUMBER_STROKE / 2);
|
|
83
|
+
const fontSize = fitTextSize(
|
|
84
|
+
node.title,
|
|
85
|
+
titleUsableWidth(node.kind, node.w, node.h),
|
|
86
|
+
18,
|
|
87
|
+
MIN_TEXT_SIZE,
|
|
88
|
+
);
|
|
89
|
+
return (
|
|
90
|
+
<>
|
|
91
|
+
<circle
|
|
92
|
+
data-cdl-role="node-body"
|
|
93
|
+
data-cdl-mark="timeline-number"
|
|
94
|
+
cx={node.cx}
|
|
95
|
+
cy={node.cy}
|
|
96
|
+
r={radius}
|
|
97
|
+
fill="var(--cdl-node-fill, #ffffff)"
|
|
98
|
+
stroke={nodeAccent(node, STATION_DEFAULT)}
|
|
99
|
+
strokeWidth={TIMELINE_NUMBER_STROKE}
|
|
100
|
+
/>
|
|
101
|
+
<text
|
|
102
|
+
data-cdl-role="node-label"
|
|
103
|
+
x={node.cx}
|
|
104
|
+
y={node.cy}
|
|
105
|
+
textAnchor="middle"
|
|
106
|
+
dominantBaseline="central"
|
|
107
|
+
fontSize={fontSize}
|
|
108
|
+
fontWeight={700}
|
|
109
|
+
fill="var(--cdl-text, #1a1f2a)"
|
|
110
|
+
>
|
|
111
|
+
{node.title}
|
|
112
|
+
</text>
|
|
113
|
+
</>
|
|
27
114
|
);
|
|
28
|
-
return renderShapeWithOutsideLabels(形, node, "middle");
|
|
29
115
|
}
|