@cardenelabs/cdl 0.121.0 → 0.123.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 +51 -1
- package/README.md +137 -0
- package/SPEC.md +1 -1
- package/dist/index.cjs +1513 -667
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -33
- package/dist/index.d.ts +51 -33
- package/dist/index.js +1513 -668
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +1271 -444
- 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 +1271 -444
- package/dist/react.js.map +1 -1
- package/dist/{render-A5AKjNuy.d.cts → render-CVczwfRb.d.cts} +40 -7
- package/dist/{render-A5AKjNuy.d.ts → render-CVczwfRb.d.ts} +40 -7
- package/package.json +9 -12
- package/src/builder.ts +8 -2
- package/src/index.ts +3 -0
- package/src/kinds/box-lines.ts +17 -4
- package/src/kinds/chart-bar.tsx +4 -0
- package/src/kinds/chart-gauge.tsx +20 -9
- package/src/kinds/chart-pie.tsx +67 -17
- package/src/kinds/chart-radial.tsx +7 -2
- package/src/kinds/chart-slope.tsx +26 -7
- package/src/kinds/chart-stacked-bar.tsx +8 -4
- package/src/kinds/chart-stat.tsx +19 -4
- package/src/kinds/chart-waffle.tsx +7 -2
- package/src/kinds/dyn-shape.tsx +22 -9
- package/src/kinds/funnel.tsx +9 -2
- package/src/kinds/generic.tsx +5 -2
- package/src/kinds/mind-map.tsx +26 -10
- package/src/kinds/quadrant.tsx +14 -4
- package/src/kinds/row-align.ts +19 -7
- package/src/kinds/sequence-board-metrics.ts +13 -3
- package/src/kinds/sequence-board.tsx +4 -2
- package/src/kinds/shape-basement.tsx +57 -32
- package/src/kinds/shape-blockchain.tsx +83 -34
- package/src/kinds/shape-commerce.tsx +65 -24
- package/src/kinds/shape-finance.tsx +90 -26
- package/src/kinds/shape-hardware.tsx +48 -17
- package/src/kinds/shape-region.tsx +16 -6
- package/src/kinds/shape-software.tsx +30 -8
- package/src/kinds/station.tsx +29 -0
- package/src/kinds/text-width.ts +17 -3
- package/src/kinds/tree.tsx +25 -7
- package/src/layout/collisions.ts +50 -28
- package/src/layout/curve-path.ts +126 -0
- package/src/layout/edges.ts +271 -58
- package/src/layout/head-label.ts +11 -5
- package/src/layout/lanes.ts +3 -2
- package/src/layout/metro-path.ts +185 -0
- package/src/layout/nodes.ts +14 -6
- package/src/layout/predict-bbox.ts +6 -3
- package/src/layout/self-loop.ts +10 -4
- package/src/layout/spec.ts +36 -7
- package/src/layout/text-width.ts +25 -8
- package/src/layout/tokens.ts +2 -0
- package/src/layout-with-validation.ts +10 -5
- package/src/layout.ts +55 -21
- package/src/presets.ts +69 -43
- package/src/render/edges.tsx +19 -3
- package/src/render/interactive-panel.tsx +34 -7
- package/src/render/nodes.tsx +100 -48
- package/src/render/stage.tsx +3 -0
- package/src/text-metrics.ts +9 -0
- package/src/types.ts +41 -4
- package/src/visual-validate.ts +91 -21
package/src/layout.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CdlDiagram, LaidDiagram } from "./types";
|
|
2
2
|
import { resolveLang } from "./lang";
|
|
3
|
+
import { resolveTextMetrics } from "./text-metrics";
|
|
3
4
|
import {
|
|
4
5
|
CLEARANCE_LABEL_LABEL,
|
|
5
6
|
CLEARANCE_NODE_LABEL,
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
import {
|
|
16
17
|
bowSharedStraightPaths,
|
|
17
18
|
fanIncomingEndpoints,
|
|
19
|
+
followLabelsToFannedTails,
|
|
18
20
|
hopEdgeCrossings,
|
|
19
21
|
layoutEdges,
|
|
20
22
|
pullOutgoingFirstCorner,
|
|
@@ -212,11 +214,13 @@ export function applyRowLevelNodeShifts<
|
|
|
212
214
|
* - node は lane 内 stack 順で縦並び、 各 stack 間 gap 80px、 上端 80px
|
|
213
215
|
* - lane label は lane 上部 30px 高
|
|
214
216
|
* - contain: true の lane は内部 node 全部を 24px padding で囲む boundary
|
|
215
|
-
* - edge routing は orthogonal L 字 (起点 side → 中継 → 終点 side)
|
|
217
|
+
* - edge routing は orthogonal L 字 (起点 side → 中継 → 終点 side)。`curve` は 3 次曲線、
|
|
218
|
+
* `metro` は横線と傾き ±1 の斜線を円弧で結ぶ
|
|
216
219
|
* - edge label は path 中点から path 進行方向の垂直方向に 50-80px、 node bbox を避ける
|
|
217
220
|
* - viewBox は cdl が auto 計算 (右端 +80 / 下端 +80)
|
|
218
221
|
*/
|
|
219
222
|
export function layout(diag: CdlDiagram): LaidDiagram {
|
|
223
|
+
const textMetrics = resolveTextMetrics(diag.textMetrics);
|
|
220
224
|
const lanes = layoutLanes(diag);
|
|
221
225
|
// 下端の名札で絵が箱の上へ出る種別は、 ここで **座標ごと** 下げる (#478)。 以降の全ての
|
|
222
226
|
// 処理 (lane の伸長 / 衝突の解消 / 矢印の経路 / 外接矩形 / 図の下端) が動かした後の位置を
|
|
@@ -224,12 +228,17 @@ export function layout(diag: CdlDiagram): LaidDiagram {
|
|
|
224
228
|
//
|
|
225
229
|
// 描画側で `<g transform>` を掛ける形だった頃は、 箱ごと下へ動いて見えるのに `cy` も
|
|
226
230
|
// `data-cdl-cy` も外接矩形も元のままで、 座標を読む側が実際の位置を知らなかった。
|
|
227
|
-
const rawNodes = applyFooterShapeDrops(lanes, layoutNodes(diag, lanes));
|
|
231
|
+
const rawNodes = applyFooterShapeDrops(lanes, layoutNodes(diag, lanes, textMetrics));
|
|
228
232
|
const expandedLanes = expandLanesForNodes(lanes, rawNodes);
|
|
229
233
|
// CAR-470 SSOT ... 隣接 lane 対を跨ぐ edge label の収納幅を engine 側で先手確保。
|
|
230
234
|
// 拡張後、 後続 lane は cumulative shift で連鎖 shift される。 lane.width は
|
|
231
235
|
// `expandLanesForNodes` 後の値のまま (node 収納 phase と gap 拡張 phase を分離)。
|
|
232
|
-
const gapExpandedLanes = expandLaneGapsForEdgeLabels(
|
|
236
|
+
const gapExpandedLanes = expandLaneGapsForEdgeLabels(
|
|
237
|
+
expandedLanes,
|
|
238
|
+
rawNodes,
|
|
239
|
+
diag.edges,
|
|
240
|
+
textMetrics,
|
|
241
|
+
);
|
|
233
242
|
const nodes = rawNodes.map((n) => {
|
|
234
243
|
// posX / posY を明示した node は「user が固定した絶対座標」 なので lane 中心に引き戻さない。
|
|
235
244
|
//
|
|
@@ -246,16 +255,18 @@ export function layout(diag: CdlDiagram): LaidDiagram {
|
|
|
246
255
|
return { ...n, cx: newLane.x + newLane.width / 2 };
|
|
247
256
|
});
|
|
248
257
|
const containedLanesPre = recomputeContainedLanes(gapExpandedLanes, nodes);
|
|
249
|
-
const edgesPre = layoutEdges(diag, nodes, containedLanesPre);
|
|
258
|
+
const edgesPre = layoutEdges(diag, nodes, containedLanesPre, textMetrics);
|
|
250
259
|
|
|
251
260
|
// 衝突 → 自動再配置 (同 kind=node 同士のみ、 sequence preset の lifeline 等は据え置き)。
|
|
252
261
|
// node の cy を shift した後は lane の contain bbox と edge routing も再計算が必要。
|
|
253
|
-
const bboxesPre = collectBBoxes(containedLanesPre, nodes, edgesPre);
|
|
262
|
+
const bboxesPre = collectBBoxes(containedLanesPre, nodes, edgesPre, textMetrics);
|
|
254
263
|
const collisionsPre = detectCollisions(bboxesPre, edgesPre);
|
|
255
264
|
const nodesResolved = resolveOverlaps(nodes, collisionsPre);
|
|
256
265
|
const moved = nodesResolved !== nodes;
|
|
257
266
|
const containedLanes = moved ? recomputeContainedLanes(gapExpandedLanes, nodesResolved) : containedLanesPre;
|
|
258
|
-
const edgesAfterMove = moved
|
|
267
|
+
const edgesAfterMove = moved
|
|
268
|
+
? layoutEdges(diag, nodesResolved, containedLanes, textMetrics)
|
|
269
|
+
: edgesPre;
|
|
259
270
|
|
|
260
271
|
// post-pass (CAR-430 = SSOT 一本化) ... edges.ts routePath() が返す init 位置を SSOT とし、
|
|
261
272
|
// label 重複が生じた場合のみ chain 伝搬 shift (resolveEdgeLabelOverlapsWithChain) で解消。
|
|
@@ -291,10 +302,13 @@ export function layout(diag: CdlDiagram): LaidDiagram {
|
|
|
291
302
|
} else {
|
|
292
303
|
const propagateResult = resolveEdgeLabelOverlapsWithChainAndPropagate(
|
|
293
304
|
edgesAfterMove,
|
|
294
|
-
collectBBoxes(containedLanes, nodesResolved, []).filter(
|
|
305
|
+
collectBBoxes(containedLanes, nodesResolved, [], textMetrics).filter(
|
|
295
306
|
(b) => b.kind === "node" || b.kind === "lane-label",
|
|
296
307
|
),
|
|
297
308
|
chainShiftClearance,
|
|
309
|
+
undefined,
|
|
310
|
+
undefined,
|
|
311
|
+
textMetrics,
|
|
298
312
|
);
|
|
299
313
|
edges = propagateResult.edges;
|
|
300
314
|
// node / lane-label chain 伝搬 shift を反映 (CAR-482)。
|
|
@@ -333,28 +347,33 @@ export function layout(diag: CdlDiagram): LaidDiagram {
|
|
|
333
347
|
// 浮遊)、 label overlap は engine が最終段階で警告可能。 今後 label shift を再度保存する
|
|
334
348
|
// 必要が生じたら labelX/labelY を propagateResult 側から edgePathShifts と同 map に保存し、
|
|
335
349
|
// 再実行後の edges に merge する二段階 approach を検討する。
|
|
336
|
-
edges = layoutEdges(diag, nodesFinal, containedLanesFinal);
|
|
350
|
+
edges = layoutEdges(diag, nodesFinal, containedLanesFinal, textMetrics);
|
|
337
351
|
}
|
|
338
352
|
}
|
|
339
353
|
|
|
340
354
|
// 同じ 2 点を結ぶ複数の線を弓なりに分ける (dragon #941)。 label と path が確定した後に動かす =
|
|
341
355
|
// 確定前に判定すると、 後から動く label の位置を見て分離を諦める。 座標を明示した edge
|
|
342
|
-
// (canvas pivot)
|
|
356
|
+
// (canvas pivot)、`curve`、`metro` は「指定した形が最終 path」 が公開契約なので対象から外す。
|
|
343
357
|
const positionedEdgeIds = new Set(
|
|
344
358
|
diag.edges
|
|
345
|
-
.filter(
|
|
359
|
+
.filter(
|
|
360
|
+
(e) =>
|
|
361
|
+
e.routing === "curve" ||
|
|
362
|
+
e.routing === "metro" ||
|
|
363
|
+
(e.posX1 !== undefined && e.posY1 !== undefined && e.posX2 !== undefined && e.posY2 !== undefined),
|
|
364
|
+
)
|
|
346
365
|
.map((e) => e.id),
|
|
347
366
|
);
|
|
348
|
-
bowSharedStraightPaths(edges, nodesFinal, positionedEdgeIds);
|
|
367
|
+
bowSharedStraightPaths(edges, nodesFinal, positionedEdgeIds, textMetrics);
|
|
349
368
|
// 弧が確定してから、 同じ節の組を結ぶ辺の label を「兄弟の線を跨がない位置」 へ置き直す (#376)。
|
|
350
369
|
// 順序が逆だと、 直線を前提に置いた label を弧が貫く。 label を先に線から離すと、 今度は弧を
|
|
351
370
|
// 膨らませる判定が label を障害物と見なして弧が消える。
|
|
352
371
|
// 同じ辺から出る線は、 最長の 1 本を除いて最初の直線部を 40 world まで引く (#651)。
|
|
353
372
|
// 経路の引き方を変えず、 確定後の最初の折れだけを動かす。
|
|
354
|
-
pullOutgoingFirstCorner(edges, nodesFinal);
|
|
373
|
+
pullOutgoingFirstCorner(edges, nodesFinal, textMetrics);
|
|
355
374
|
// 同じ辺に入る線の終点を辺に沿って離す (#611)。 経路を引き終えてから当てる = 終点を先に
|
|
356
375
|
// 動かすと引き方の分岐が変わり、 迂回していた線が箱を貫通する (実測で 0 件が 7 件になった)。
|
|
357
|
-
fanIncomingEndpoints(edges, nodesFinal);
|
|
376
|
+
const fannedTailShifts = fanIncomingEndpoints(edges, nodesFinal);
|
|
358
377
|
// 同じ通り道を並んで走る区間をずらす (#610)。 弓の後に当てる = 弓で分かれた線を数え直さない。
|
|
359
378
|
// 弧より先に当てる = ずらした後の形で交点を数えないと、 消えた交点に弧が残る。
|
|
360
379
|
//
|
|
@@ -364,26 +383,39 @@ export function layout(diag: CdlDiagram): LaidDiagram {
|
|
|
364
383
|
// 交わる所で上を通る側に弧を差し込む (#608)。 経路が確定してから当てる = 弓や迂回で
|
|
365
384
|
// 形が変わった後の交点を見る。 札を置く前に当てるので、 札は弧を含んだ経路の上に載る。
|
|
366
385
|
hopEdgeCrossings(edges);
|
|
367
|
-
|
|
386
|
+
// 終点を離す / 共有区間を分ける / 交差へ弧を入れる pass が全て終わった座標で札を見る (#1000)。
|
|
387
|
+
// 先に見ると、 その後に経路が動いて札が線を覆う形が戻る。
|
|
388
|
+
// 追従と最後の逃がし pass は、 `collectBBoxes` が含める下端の名札の箱上へのはみ出しと
|
|
389
|
+
// 縦列の題を同じ矩形で測る。 追従だけ箱本体で測ると、 置いた札を最後の pass が押し直す (#1002)。
|
|
390
|
+
const labelClearanceObstacles = collectBBoxes(
|
|
391
|
+
containedLanesFinal,
|
|
392
|
+
nodesFinal,
|
|
393
|
+
[],
|
|
394
|
+
textMetrics,
|
|
395
|
+
).filter(
|
|
396
|
+
(b) => b.kind === "node" || b.kind === "lane-label",
|
|
397
|
+
);
|
|
398
|
+
followLabelsToFannedTails(edges, labelClearanceObstacles, fannedTailShifts, textMetrics);
|
|
399
|
+
repositionParallelLabels(edges, nodesFinal, textMetrics);
|
|
368
400
|
// 縦にずらすだけでは逃げ場が無い札を、 自分の線に沿って動かす (#601)。 ここまでの
|
|
369
401
|
// 置き直しは全て縦方向で、 線に沿って動かす経路が無かった。 いま被っている札だけを
|
|
370
402
|
// 対象にするので、 落ち着いている図の座標は動かない。
|
|
371
|
-
slideLabelsAlongOwnPath(edges, nodesFinal);
|
|
403
|
+
slideLabelsAlongOwnPath(edges, nodesFinal, textMetrics);
|
|
372
404
|
// 経路を動かす pass が全て終わってから、 札を箱と札から離し直す (#659)。
|
|
373
405
|
//
|
|
374
|
-
// 上の chain 伝搬はここより前に走るため、
|
|
375
|
-
//
|
|
406
|
+
// 上の chain 伝搬はここより前に走るため、 その後の経路 pass が動かした最終経路との隙間を
|
|
407
|
+
// ここで見直す。
|
|
376
408
|
// ここで動かすのは札だけで、 箱も経路も触らない = 触ると直前の pass が確定させた形を崩す。
|
|
377
409
|
if (!isChainEdgeLabelDisabled()) {
|
|
378
410
|
edges = resolveLabelClearanceAfterPathPasses(
|
|
379
411
|
edges,
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
412
|
+
labelClearanceObstacles,
|
|
413
|
+
undefined,
|
|
414
|
+
textMetrics,
|
|
383
415
|
);
|
|
384
416
|
}
|
|
385
417
|
|
|
386
|
-
const bboxes = collectBBoxes(containedLanesFinal, nodesFinal, edges);
|
|
418
|
+
const bboxes = collectBBoxes(containedLanesFinal, nodesFinal, edges, textMetrics);
|
|
387
419
|
const collisions = detectCollisions(bboxes, edges);
|
|
388
420
|
const nearCollisions = detectNearCollisions(bboxes, edges);
|
|
389
421
|
|
|
@@ -426,6 +458,8 @@ export function layout(diag: CdlDiagram): LaidDiagram {
|
|
|
426
458
|
// 描画側の固定の字の言語 (#847)。 既定 (`en`) と型の外の値は持たせない = 配置の結果の形を
|
|
427
459
|
// 変更前と揃え、記法の書き損じで一部の字だけ日本語になる形を作らない
|
|
428
460
|
...(resolveLang(diag.lang) === "ja" ? { lang: "ja" as const } : {}),
|
|
461
|
+
// 既定は持ち越さない。既存の LaidDiagram の形を変えず、等幅の図だけ描画へ伝える
|
|
462
|
+
...(textMetrics === "monospace" ? { textMetrics: "monospace" as const } : {}),
|
|
429
463
|
// 種類も配色と同じく空の目印を出さない。 遮断をここに揃えると手書きの図にも効く
|
|
430
464
|
...(diag.type ? { type: diag.type } : {}),
|
|
431
465
|
bboxes,
|
package/src/presets.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { diagram } from "./builder";
|
|
2
2
|
import type { DiagramBuilder } from "./builder";
|
|
3
|
+
import { resolveTextMetrics } from "./text-metrics";
|
|
3
4
|
import { LANE_PAD_MIN, NODE_SIZE } from "./layout/tokens";
|
|
4
5
|
import { requiredRowsWidth } from "./kinds/row-align";
|
|
5
6
|
import { 木の札の幅 } from "./kinds/tree";
|
|
@@ -12,7 +13,12 @@ import {
|
|
|
12
13
|
type SequenceBoardMessage,
|
|
13
14
|
type SequenceBoardMessageKind,
|
|
14
15
|
} from "./kinds/sequence-board-metrics";
|
|
15
|
-
import type { NodeKind, Tone, EdgeStyle, EdgeHead, EdgeHeadFill, RowMark, JourneyEmotion, QuadrantKey, BoundNumber, EdgeRole } from "./types";
|
|
16
|
+
import type { CdlTextMetrics, NodeKind, Tone, EdgeStyle, EdgeHead, EdgeHeadFill, RowMark, JourneyEmotion, QuadrantKey, BoundNumber, EdgeRole } from "./types";
|
|
17
|
+
|
|
18
|
+
type TextMetricsPreset = {
|
|
19
|
+
/** 図の字幅を測る規則。省略時は `"proportional"` */
|
|
20
|
+
textMetrics?: CdlTextMetrics;
|
|
21
|
+
};
|
|
16
22
|
|
|
17
23
|
/**
|
|
18
24
|
* 状態を読む書き方 (`{名前}`)。
|
|
@@ -175,10 +181,14 @@ function resolveStorageBoxW(width: number | undefined, presetName: string, optio
|
|
|
175
181
|
* 指定値は下限として扱う。 「指定した幅より狭くならない」 は守り、 中身が入らない時だけ広げる。
|
|
176
182
|
* 例外にしない理由は、 著者が行の実寸を事前に計算できないため (font の送り幅に依存する)。
|
|
177
183
|
*/
|
|
178
|
-
function fitRowsWidth(
|
|
184
|
+
function fitRowsWidth(
|
|
185
|
+
boxW: number,
|
|
186
|
+
rows: readonly string[] | undefined,
|
|
187
|
+
textMetrics: CdlTextMetrics = "proportional",
|
|
188
|
+
): number {
|
|
179
189
|
// 呼出は `er` の実体と `classDiagram` のクラスの 2 箇所で、 どちらも `kind: "storage"` を作る。
|
|
180
190
|
// 送り幅と列間は種別で違うため、 どの種別として測るかを渡す
|
|
181
|
-
return Math.max(boxW, requiredRowsWidth(rows ? [...rows] : undefined, "storage"));
|
|
191
|
+
return Math.max(boxW, requiredRowsWidth(rows ? [...rows] : undefined, "storage", textMetrics));
|
|
182
192
|
}
|
|
183
193
|
|
|
184
194
|
/**
|
|
@@ -190,7 +200,7 @@ function fitRowsWidth(boxW: number, rows: readonly string[] | undefined): number
|
|
|
190
200
|
|
|
191
201
|
// ─── swimlane ──────────────────────────────────────────────────
|
|
192
202
|
|
|
193
|
-
export type SwimlanePreset = {
|
|
203
|
+
export type SwimlanePreset = TextMetricsPreset & {
|
|
194
204
|
id: string;
|
|
195
205
|
topic: string;
|
|
196
206
|
/** lane label の配列。 各 lane width は固定 (default 400)、 x は auto-layout */
|
|
@@ -218,7 +228,7 @@ export type SwimlaneResult = DiagramBuilder & {
|
|
|
218
228
|
|
|
219
229
|
export function swimlane(preset: SwimlanePreset): SwimlaneResult {
|
|
220
230
|
const w = preset.laneWidth ?? 400;
|
|
221
|
-
const b = diagram(preset.id, { topic: preset.topic, type: "swimlane" });
|
|
231
|
+
const b = diagram(preset.id, { topic: preset.topic, textMetrics: preset.textMetrics, type: "swimlane" });
|
|
222
232
|
const labelToId = new Map<string, string>();
|
|
223
233
|
const idsInOrder: string[] = [];
|
|
224
234
|
for (const label of preset.lanes) {
|
|
@@ -247,7 +257,7 @@ export type FlowStepInput = {
|
|
|
247
257
|
subtitle?: string;
|
|
248
258
|
};
|
|
249
259
|
|
|
250
|
-
export type FlowPreset = {
|
|
260
|
+
export type FlowPreset = TextMetricsPreset & {
|
|
251
261
|
id: string;
|
|
252
262
|
topic: string;
|
|
253
263
|
/** lane label (1 本のみ、 全 step が縦 stack で並ぶ) */
|
|
@@ -298,7 +308,7 @@ export type FlowBuilder = {
|
|
|
298
308
|
*/
|
|
299
309
|
export function flow(preset: FlowPreset): FlowBuilder {
|
|
300
310
|
const w = preset.laneWidth ?? 400;
|
|
301
|
-
const b = diagram(preset.id, { topic: preset.topic, type: "flow" });
|
|
311
|
+
const b = diagram(preset.id, { topic: preset.topic, textMetrics: preset.textMetrics, type: "flow" });
|
|
302
312
|
b.lane("flow", { width: w, ...(preset.laneLabel ? { label: preset.laneLabel } : {}) });
|
|
303
313
|
const tone: Tone = preset.defaultTone ?? "accent";
|
|
304
314
|
const style: EdgeStyle = preset.defaultStyle ?? "dotted-flow";
|
|
@@ -410,7 +420,7 @@ export type SequenceActor = {
|
|
|
410
420
|
subtitle?: string;
|
|
411
421
|
};
|
|
412
422
|
|
|
413
|
-
export type SequencePreset = {
|
|
423
|
+
export type SequencePreset = TextMetricsPreset & {
|
|
414
424
|
id: string;
|
|
415
425
|
topic: string;
|
|
416
426
|
/** actor label の配列 (lifeline、 column 単位)。 呼び名を添えるなら物で書く (#578) */
|
|
@@ -452,7 +462,7 @@ export type SequenceBuilder = {
|
|
|
452
462
|
*/
|
|
453
463
|
export function sequence(preset: SequencePreset): SequenceBuilder {
|
|
454
464
|
const tone: Tone = preset.defaultTone ?? "accent";
|
|
455
|
-
const b = diagram(preset.id, { topic: preset.topic, type: "sequence" });
|
|
465
|
+
const b = diagram(preset.id, { topic: preset.topic, textMetrics: preset.textMetrics, type: "sequence" });
|
|
456
466
|
|
|
457
467
|
// 名前だけの文字列と、呼び名を添えた物の両方を受ける (#578)
|
|
458
468
|
const 面々: SequenceBoardActor[] = preset.actors.map((a) =>
|
|
@@ -501,7 +511,7 @@ export function sequence(preset: SequencePreset): SequenceBuilder {
|
|
|
501
511
|
.filter((x): x is SequenceBoardBand => x !== null);
|
|
502
512
|
|
|
503
513
|
const data: SequenceBoardData = { actors: 面々, messages: 言づて, bands: 帯 };
|
|
504
|
-
const m = sequenceBoardMetrics(data);
|
|
514
|
+
const m = sequenceBoardMetrics(data, resolveTextMetrics(preset.textMetrics));
|
|
505
515
|
const laneId = "seq";
|
|
506
516
|
b.lane(laneId, { width: m.w + LANE_PAD_MIN * 2 });
|
|
507
517
|
b.node(preset.id, {
|
|
@@ -548,7 +558,7 @@ export type TopologyConnection = {
|
|
|
548
558
|
labelOffsetY?: number;
|
|
549
559
|
};
|
|
550
560
|
|
|
551
|
-
export type TopologyPreset = {
|
|
561
|
+
export type TopologyPreset = TextMetricsPreset & {
|
|
552
562
|
id: string;
|
|
553
563
|
topic: string;
|
|
554
564
|
/** group の lane width (default 360) */
|
|
@@ -592,7 +602,7 @@ export function topology(preset: TopologyPreset): TopologyBuilder {
|
|
|
592
602
|
const groupW = preset.groupWidth ?? 460;
|
|
593
603
|
const tone: Tone = preset.defaultTone ?? "accent";
|
|
594
604
|
const style: EdgeStyle = preset.defaultStyle ?? "solid";
|
|
595
|
-
const b = diagram(preset.id, { topic: preset.topic, type: "topology" });
|
|
605
|
+
const b = diagram(preset.id, { topic: preset.topic, textMetrics: preset.textMetrics, type: "topology" });
|
|
596
606
|
|
|
597
607
|
const groupOrder: string[] = [];
|
|
598
608
|
const groupStackCount = new Map<string, number>();
|
|
@@ -752,7 +762,7 @@ export type ErRelation = {
|
|
|
752
762
|
style?: EdgeStyle;
|
|
753
763
|
};
|
|
754
764
|
|
|
755
|
-
export type ErPreset = {
|
|
765
|
+
export type ErPreset = TextMetricsPreset & {
|
|
756
766
|
id: string;
|
|
757
767
|
topic: string;
|
|
758
768
|
defaultTone?: Tone;
|
|
@@ -804,7 +814,12 @@ export function er(preset: ErPreset): ErBuilder {
|
|
|
804
814
|
const tone: Tone = preset.defaultTone ?? "info";
|
|
805
815
|
const 縞を敷く = preset.rowStripe ?? true;
|
|
806
816
|
// 配色の名前は素通しする。 空文字の遮断は `diagram` が持つ = ここで重ねると到達しない分岐になる
|
|
807
|
-
const b = diagram(preset.id, {
|
|
817
|
+
const b = diagram(preset.id, {
|
|
818
|
+
topic: preset.topic,
|
|
819
|
+
palette: preset.palette,
|
|
820
|
+
textMetrics: preset.textMetrics,
|
|
821
|
+
type: "er",
|
|
822
|
+
});
|
|
808
823
|
|
|
809
824
|
const entities: string[] = [];
|
|
810
825
|
const phaseActivate: string[] = [];
|
|
@@ -834,7 +849,7 @@ export function er(preset: ErPreset): ErBuilder {
|
|
|
834
849
|
const rows = 設計の形 ? 行 : (e.rows ?? []);
|
|
835
850
|
// 帯は実体ごとに決める。 行の長さで箱が広がる実体があるため、 全実体を同じ帯幅で
|
|
836
851
|
// 宣言すると宣言座標と実座標がずれる。
|
|
837
|
-
const boxW = fitRowsWidth(entityW, rows);
|
|
852
|
+
const boxW = fitRowsWidth(entityW, rows, resolveTextMetrics(preset.textMetrics));
|
|
838
853
|
b.lane(laneId, { width: boxLaneW(boxW) });
|
|
839
854
|
b.node(e.id, {
|
|
840
855
|
lane: laneId,
|
|
@@ -986,7 +1001,7 @@ export type FsmTransition = {
|
|
|
986
1001
|
head?: EdgeHead;
|
|
987
1002
|
};
|
|
988
1003
|
|
|
989
|
-
export type StateMachinePreset = {
|
|
1004
|
+
export type StateMachinePreset = TextMetricsPreset & {
|
|
990
1005
|
id: string;
|
|
991
1006
|
topic: string;
|
|
992
1007
|
defaultTone?: Tone;
|
|
@@ -1033,7 +1048,7 @@ export function stateMachine(preset: StateMachinePreset): StateMachineBuilder {
|
|
|
1033
1048
|
const tone: Tone = preset.defaultTone ?? "accent";
|
|
1034
1049
|
// 図の型と最後の段の札は同じ綴り (#859)。 札を別の字にすると、消費側が型の一覧で引けない
|
|
1035
1050
|
const diagramType = "state";
|
|
1036
|
-
const b = diagram(preset.id, { topic: preset.topic, type: diagramType });
|
|
1051
|
+
const b = diagram(preset.id, { topic: preset.topic, textMetrics: preset.textMetrics, type: diagramType });
|
|
1037
1052
|
|
|
1038
1053
|
const phaseActivate: string[] = [];
|
|
1039
1054
|
const stateOrder = new Map<string, number>();
|
|
@@ -1078,7 +1093,9 @@ export function stateMachine(preset: StateMachinePreset): StateMachineBuilder {
|
|
|
1078
1093
|
const 行 = (s.actions ?? []).map((a) => (a.note ? `${a.label}: ${a.note}` : a.label));
|
|
1079
1094
|
const 印 = (s.actions ?? []).map((a) => FSM_ACTION_MARK[a.when]);
|
|
1080
1095
|
const laneId = 格子 && s.col !== undefined ? `col-${s.col}` : `lane-${s.id}`;
|
|
1081
|
-
const boxW = 設計の形
|
|
1096
|
+
const boxW = 設計の形
|
|
1097
|
+
? fitRowsWidth(stateW, 行, resolveTextMetrics(preset.textMetrics))
|
|
1098
|
+
: stateW;
|
|
1082
1099
|
return { s, 行, 印, laneId, boxW, stack: 格子 ? (s.row ?? 0) : 0 };
|
|
1083
1100
|
});
|
|
1084
1101
|
const 節点の中身 = 節点.map((m) => ({
|
|
@@ -1201,7 +1218,7 @@ export type InfraConnection = {
|
|
|
1201
1218
|
labelOffsetY?: number;
|
|
1202
1219
|
};
|
|
1203
1220
|
|
|
1204
|
-
export type InfrastructurePreset = {
|
|
1221
|
+
export type InfrastructurePreset = TextMetricsPreset & {
|
|
1205
1222
|
id: string;
|
|
1206
1223
|
topic: string;
|
|
1207
1224
|
/** col あたりの lane width (default 380) */
|
|
@@ -1239,7 +1256,11 @@ export function infrastructure(preset: InfrastructurePreset): InfrastructureBuil
|
|
|
1239
1256
|
const laneW = preset.laneWidth ?? 380;
|
|
1240
1257
|
const tone: Tone = preset.defaultTone ?? "accent";
|
|
1241
1258
|
const style: EdgeStyle = preset.defaultStyle ?? "solid";
|
|
1242
|
-
const b = diagram(preset.id, {
|
|
1259
|
+
const b = diagram(preset.id, {
|
|
1260
|
+
topic: preset.topic,
|
|
1261
|
+
textMetrics: preset.textMetrics,
|
|
1262
|
+
type: "infrastructure",
|
|
1263
|
+
});
|
|
1243
1264
|
|
|
1244
1265
|
// col → laneId、 col の数で lane を auto 作成
|
|
1245
1266
|
const colLaneIds = new Map<number, string>();
|
|
@@ -1452,7 +1473,7 @@ export type UmlRelation = {
|
|
|
1452
1473
|
style?: EdgeStyle;
|
|
1453
1474
|
};
|
|
1454
1475
|
|
|
1455
|
-
export type ClassDiagramPreset = {
|
|
1476
|
+
export type ClassDiagramPreset = TextMetricsPreset & {
|
|
1456
1477
|
id: string;
|
|
1457
1478
|
topic: string;
|
|
1458
1479
|
defaultTone?: Tone;
|
|
@@ -1503,7 +1524,12 @@ export function classDiagram(preset: ClassDiagramPreset): ClassDiagramBuilder {
|
|
|
1503
1524
|
const 図の既定 = preset.defaultTone;
|
|
1504
1525
|
// 縞と配色は `er()` と同じ扱い (#627)。 箱の作りが同じなので、既定も揃える
|
|
1505
1526
|
const 縞を敷く = preset.rowStripe ?? true;
|
|
1506
|
-
const b = diagram(preset.id, {
|
|
1527
|
+
const b = diagram(preset.id, {
|
|
1528
|
+
topic: preset.topic,
|
|
1529
|
+
palette: preset.palette,
|
|
1530
|
+
textMetrics: preset.textMetrics,
|
|
1531
|
+
type: "class",
|
|
1532
|
+
});
|
|
1507
1533
|
|
|
1508
1534
|
const classes: string[] = [];
|
|
1509
1535
|
const phaseActivate: string[] = [];
|
|
@@ -1537,7 +1563,7 @@ export function classDiagram(preset: ClassDiagramPreset): ClassDiagramBuilder {
|
|
|
1537
1563
|
}
|
|
1538
1564
|
// 帯はクラスごとに決める。 行の長さで箱が広がるクラスがあるため、 全クラスを同じ帯幅で
|
|
1539
1565
|
// 宣言すると宣言座標と実座標がずれる。
|
|
1540
|
-
const boxW = fitRowsWidth(classW, rows);
|
|
1566
|
+
const boxW = fitRowsWidth(classW, rows, resolveTextMetrics(preset.textMetrics));
|
|
1541
1567
|
// 格子に置くかは `col` を書いたかで決める (#578)。 書かない図は従来どおり横 1 列
|
|
1542
1568
|
const laneId = c.col !== undefined ? `col-${c.col}` : `lane-${c.id}`;
|
|
1543
1569
|
return { c, rows, rowMarks, boxW, laneId, stack: c.col !== undefined ? (c.row ?? 0) : 0 };
|
|
@@ -1619,7 +1645,7 @@ export type TreeNode = {
|
|
|
1619
1645
|
subtitle?: string;
|
|
1620
1646
|
};
|
|
1621
1647
|
|
|
1622
|
-
export type TreePreset = {
|
|
1648
|
+
export type TreePreset = TextMetricsPreset & {
|
|
1623
1649
|
id: string;
|
|
1624
1650
|
topic: string;
|
|
1625
1651
|
/** 図の題の上に出す小見出し (#939)。 書かなければ何も出ない */
|
|
@@ -1649,7 +1675,7 @@ export type TreeBuilder = {
|
|
|
1649
1675
|
*/
|
|
1650
1676
|
export function tree(preset: TreePreset): TreeBuilder {
|
|
1651
1677
|
const canvasH = 480;
|
|
1652
|
-
const b = diagram(preset.id, { topic: preset.topic, type: "tree" });
|
|
1678
|
+
const b = diagram(preset.id, { topic: preset.topic, textMetrics: preset.textMetrics, type: "tree" });
|
|
1653
1679
|
const treeNodes: TreeNode[] = [];
|
|
1654
1680
|
|
|
1655
1681
|
const api: TreeBuilder = {
|
|
@@ -1708,7 +1734,7 @@ export type JourneyStep = {
|
|
|
1708
1734
|
opportunity?: string;
|
|
1709
1735
|
};
|
|
1710
1736
|
|
|
1711
|
-
export type UserJourneyPreset = {
|
|
1737
|
+
export type UserJourneyPreset = TextMetricsPreset & {
|
|
1712
1738
|
id: string;
|
|
1713
1739
|
topic: string;
|
|
1714
1740
|
/** 図の題の上に出す小見出し (#939)。 書かなければ何も出ない */
|
|
@@ -1737,7 +1763,7 @@ export type UserJourneyBuilder = {
|
|
|
1737
1763
|
export function userJourney(preset: UserJourneyPreset): UserJourneyBuilder {
|
|
1738
1764
|
const canvasW = preset.stepWidth ? preset.stepWidth * 2 : 720;
|
|
1739
1765
|
const canvasH = 480;
|
|
1740
|
-
const b = diagram(preset.id, { topic: preset.topic, type: "journey" });
|
|
1766
|
+
const b = diagram(preset.id, { topic: preset.topic, textMetrics: preset.textMetrics, type: "journey" });
|
|
1741
1767
|
const steps: JourneyStep[] = [];
|
|
1742
1768
|
|
|
1743
1769
|
const api: UserJourneyBuilder = {
|
|
@@ -1788,7 +1814,7 @@ export type MindBranch = {
|
|
|
1788
1814
|
subtitle?: string;
|
|
1789
1815
|
};
|
|
1790
1816
|
|
|
1791
|
-
export type MindMapPreset = {
|
|
1817
|
+
export type MindMapPreset = TextMetricsPreset & {
|
|
1792
1818
|
id: string;
|
|
1793
1819
|
topic: string;
|
|
1794
1820
|
/** 図の題の上に出す小見出し (#939)。 書かなければ何も出ない */
|
|
@@ -1825,7 +1851,7 @@ export function mindMap(preset: MindMapPreset): MindMapBuilder {
|
|
|
1825
1851
|
// 型は他の組み立て関数と同じく短い名前 (`state` / `class` / `journey` と並ぶ `mind`)。
|
|
1826
1852
|
// 最後の段の札も同じ綴り (#859)
|
|
1827
1853
|
const diagramType = "mind";
|
|
1828
|
-
const b = diagram(preset.id, { topic: preset.topic, type: diagramType });
|
|
1854
|
+
const b = diagram(preset.id, { topic: preset.topic, textMetrics: preset.textMetrics, type: diagramType });
|
|
1829
1855
|
const branches: MindBranch[] = [];
|
|
1830
1856
|
|
|
1831
1857
|
const api: MindMapBuilder = {
|
|
@@ -1880,7 +1906,7 @@ export type FunnelStage = {
|
|
|
1880
1906
|
subtitle?: string;
|
|
1881
1907
|
};
|
|
1882
1908
|
|
|
1883
|
-
export type FunnelPreset = {
|
|
1909
|
+
export type FunnelPreset = TextMetricsPreset & {
|
|
1884
1910
|
id: string;
|
|
1885
1911
|
topic: string;
|
|
1886
1912
|
/** 図の題の上に出す小見出し (#939)。 書かなければ何も出ない */
|
|
@@ -1909,7 +1935,7 @@ export type FunnelBuilder = {
|
|
|
1909
1935
|
export function funnel(preset: FunnelPreset): FunnelBuilder {
|
|
1910
1936
|
const canvasW = toGridSize(preset.stageWidth ?? 560);
|
|
1911
1937
|
const canvasH = 480;
|
|
1912
|
-
const b = diagram(preset.id, { topic: preset.topic, type: "funnel" });
|
|
1938
|
+
const b = diagram(preset.id, { topic: preset.topic, textMetrics: preset.textMetrics, type: "funnel" });
|
|
1913
1939
|
const stages: FunnelStage[] = [];
|
|
1914
1940
|
|
|
1915
1941
|
const api: FunnelBuilder = {
|
|
@@ -1961,7 +1987,7 @@ export type QuadrantItem = {
|
|
|
1961
1987
|
subtitle?: string;
|
|
1962
1988
|
};
|
|
1963
1989
|
|
|
1964
|
-
export type QuadrantPreset = {
|
|
1990
|
+
export type QuadrantPreset = TextMetricsPreset & {
|
|
1965
1991
|
id: string;
|
|
1966
1992
|
topic: string;
|
|
1967
1993
|
/** 図の題の上に出す小見出し (#939)。 書かなければ何も出ない */
|
|
@@ -1998,7 +2024,7 @@ export type QuadrantBuilder = {
|
|
|
1998
2024
|
export function quadrant(preset: QuadrantPreset): QuadrantBuilder {
|
|
1999
2025
|
const canvasW = 640;
|
|
2000
2026
|
const canvasH = 480;
|
|
2001
|
-
const b = diagram(preset.id, { topic: preset.topic, type: "quadrant" });
|
|
2027
|
+
const b = diagram(preset.id, { topic: preset.topic, textMetrics: preset.textMetrics, type: "quadrant" });
|
|
2002
2028
|
const items: QuadrantItem[] = [];
|
|
2003
2029
|
|
|
2004
2030
|
const api: QuadrantBuilder = {
|
|
@@ -2071,7 +2097,7 @@ export type ChartDatum = {
|
|
|
2071
2097
|
tone?: Tone;
|
|
2072
2098
|
};
|
|
2073
2099
|
|
|
2074
|
-
export type ChartPreset = {
|
|
2100
|
+
export type ChartPreset = TextMetricsPreset & {
|
|
2075
2101
|
id: string;
|
|
2076
2102
|
topic: string;
|
|
2077
2103
|
/** 図の題の上に出す小見出し (#939)。 書かなければ何も出ない */
|
|
@@ -2115,7 +2141,7 @@ export function chart(preset: ChartPreset): ChartBuilder {
|
|
|
2115
2141
|
? 320
|
|
2116
2142
|
: 360,
|
|
2117
2143
|
);
|
|
2118
|
-
const b = diagram(preset.id, { topic: preset.topic, type: "chart" });
|
|
2144
|
+
const b = diagram(preset.id, { topic: preset.topic, textMetrics: preset.textMetrics, type: "chart" });
|
|
2119
2145
|
const data: ChartDatum[] = [];
|
|
2120
2146
|
|
|
2121
2147
|
const kindMap = {
|
|
@@ -2186,7 +2212,7 @@ export type GanttTask = {
|
|
|
2186
2212
|
dependsOn?: string;
|
|
2187
2213
|
};
|
|
2188
2214
|
|
|
2189
|
-
export type GanttPreset = {
|
|
2215
|
+
export type GanttPreset = TextMetricsPreset & {
|
|
2190
2216
|
id: string;
|
|
2191
2217
|
topic: string;
|
|
2192
2218
|
/** 図の題の上に出す小見出し (#939)。 書かなければ何も出ない */
|
|
@@ -2229,7 +2255,7 @@ export function gantt(preset: GanttPreset): GanttBuilder {
|
|
|
2229
2255
|
const canvasW = preset.laneWidth ? preset.laneWidth * 2 : 720;
|
|
2230
2256
|
const canvasH = 360;
|
|
2231
2257
|
const tone: Tone = preset.defaultTone ?? "teal";
|
|
2232
|
-
const b = diagram(preset.id, { topic: preset.topic, type: "gantt" });
|
|
2258
|
+
const b = diagram(preset.id, { topic: preset.topic, textMetrics: preset.textMetrics, type: "gantt" });
|
|
2233
2259
|
const tasks: GanttTask[] = [];
|
|
2234
2260
|
|
|
2235
2261
|
const api: GanttBuilder = {
|
|
@@ -2319,7 +2345,7 @@ export type FlowchartEdge = {
|
|
|
2319
2345
|
*/
|
|
2320
2346
|
export type FlowchartDirection = "horizontal" | "vertical";
|
|
2321
2347
|
|
|
2322
|
-
export type FlowchartPreset = {
|
|
2348
|
+
export type FlowchartPreset = TextMetricsPreset & {
|
|
2323
2349
|
id: string;
|
|
2324
2350
|
topic: string;
|
|
2325
2351
|
/** swimlane の lane (role) ラベル */
|
|
@@ -2378,7 +2404,7 @@ export function flowchart(preset: FlowchartPreset): FlowchartBuilder {
|
|
|
2378
2404
|
const laneW = preset.laneWidth ?? 380;
|
|
2379
2405
|
const tone: Tone = preset.defaultTone ?? "accent";
|
|
2380
2406
|
const 縦に積む = (preset.direction ?? "horizontal") === "vertical";
|
|
2381
|
-
const b = diagram(preset.id, { topic: preset.topic, type: "flowchart" });
|
|
2407
|
+
const b = diagram(preset.id, { topic: preset.topic, textMetrics: preset.textMetrics, type: "flowchart" });
|
|
2382
2408
|
|
|
2383
2409
|
const laneToId = new Map<string, string>();
|
|
2384
2410
|
const laneStackCount = new Map<string, number>();
|
|
@@ -2488,7 +2514,7 @@ export type NetworkLink = {
|
|
|
2488
2514
|
tone?: Tone;
|
|
2489
2515
|
};
|
|
2490
2516
|
|
|
2491
|
-
export type NetworkPreset = {
|
|
2517
|
+
export type NetworkPreset = TextMetricsPreset & {
|
|
2492
2518
|
id: string;
|
|
2493
2519
|
topic: string;
|
|
2494
2520
|
defaultTone?: Tone;
|
|
@@ -2534,7 +2560,7 @@ const NET_KIND_LABEL: Record<NetworkDeviceKind, string> = {
|
|
|
2534
2560
|
export function network(preset: NetworkPreset): NetworkBuilder {
|
|
2535
2561
|
const laneW = preset.laneWidth ?? 320;
|
|
2536
2562
|
const tone: Tone = preset.defaultTone ?? "info";
|
|
2537
|
-
const b = diagram(preset.id, { topic: preset.topic, type: "network" });
|
|
2563
|
+
const b = diagram(preset.id, { topic: preset.topic, textMetrics: preset.textMetrics, type: "network" });
|
|
2538
2564
|
|
|
2539
2565
|
const colLaneIds = new Map<number, string>();
|
|
2540
2566
|
const phaseActivate: string[] = [];
|
|
@@ -2609,7 +2635,7 @@ export type FsmTransitionExt = {
|
|
|
2609
2635
|
tone?: Tone;
|
|
2610
2636
|
};
|
|
2611
2637
|
|
|
2612
|
-
export type StateMachine2Preset = {
|
|
2638
|
+
export type StateMachine2Preset = TextMetricsPreset & {
|
|
2613
2639
|
id: string;
|
|
2614
2640
|
topic: string;
|
|
2615
2641
|
defaultTone?: Tone;
|
|
@@ -2653,7 +2679,7 @@ export function stateMachine2(preset: StateMachine2Preset): StateMachine2Builder
|
|
|
2653
2679
|
const tone: Tone = preset.defaultTone ?? "accent";
|
|
2654
2680
|
// 入れ子を持つ状態の図も型は `state`。 最後の段の札も同じ綴りで、1 つ目の図との見分けは題が持つ (#859)
|
|
2655
2681
|
const diagramType = "state";
|
|
2656
|
-
const b = diagram(preset.id, { topic: preset.topic, type: diagramType });
|
|
2682
|
+
const b = diagram(preset.id, { topic: preset.topic, textMetrics: preset.textMetrics, type: diagramType });
|
|
2657
2683
|
|
|
2658
2684
|
const stateOrder = new Map<string, number>();
|
|
2659
2685
|
const phaseActivate: string[] = [];
|
package/src/render/edges.tsx
CHANGED
|
@@ -2,7 +2,13 @@ import { 等幅の字 } from "./mono-text";
|
|
|
2
2
|
import type { JSX } from "react";
|
|
3
3
|
import { computeLabelBBoxWorld, hasRenderedLabel, LABEL_PILL_PAD_X } from "../layout/spec";
|
|
4
4
|
import { 端の字の大きさ, 端の字を置く, type 線の端 } from "../layout/head-label";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
EDGE_HEAD_MARKER_SIZE,
|
|
7
|
+
type CdlTextMetrics,
|
|
8
|
+
type EdgeRole,
|
|
9
|
+
type LaidEdge,
|
|
10
|
+
type Tone,
|
|
11
|
+
} from "../types";
|
|
6
12
|
import { edgeHeadMarkerName } from "./edge-head";
|
|
7
13
|
import { toneColor } from "./tone";
|
|
8
14
|
|
|
@@ -119,6 +125,7 @@ export function CdlEdgeView({
|
|
|
119
125
|
return (
|
|
120
126
|
<g
|
|
121
127
|
data-cdl-edge={edge.id}
|
|
128
|
+
data-cdl-routing={edge.routing === "metro" ? "metro" : undefined}
|
|
122
129
|
data-cdl-from={edge.from}
|
|
123
130
|
data-cdl-to={edge.to}
|
|
124
131
|
data-cdl-active={active ? "true" : "false"}
|
|
@@ -130,6 +137,7 @@ export function CdlEdgeView({
|
|
|
130
137
|
data-cdl-edge-label={edge.label}
|
|
131
138
|
data-cdl-edge-sub={edge.sub ?? ""}
|
|
132
139
|
data-cdl-tone={edge.tone}
|
|
140
|
+
data-cdl-edge-role={edge.role}
|
|
133
141
|
>
|
|
134
142
|
{isDotted ? (
|
|
135
143
|
<g>
|
|
@@ -348,9 +356,11 @@ export function CdlEdgeGlowView({
|
|
|
348
356
|
export function CdlEdgeLabelView({
|
|
349
357
|
edge,
|
|
350
358
|
active,
|
|
359
|
+
textMetrics = "proportional",
|
|
351
360
|
}: {
|
|
352
361
|
edge: LaidEdge;
|
|
353
362
|
active: boolean;
|
|
363
|
+
textMetrics?: CdlTextMetrics;
|
|
354
364
|
}): JSX.Element | null {
|
|
355
365
|
// routing v9 = 空 label / 空 sub の edge は DOM 描画自体を skip、
|
|
356
366
|
// collisions.ts 側 (bbox 生成 skip、 CAR-430 で SSOT 統一) と対称に render 層でも noop。
|
|
@@ -362,7 +372,7 @@ export function CdlEdgeLabelView({
|
|
|
362
372
|
// bbox 計算 drift を解消)。 pill h / w / padding / boxLeftX / boxTopY は本 helper 経由で取得、
|
|
363
373
|
// <g transform="translate(labelX, labelY)"> 内 rect は相対座標なので world x/y を helper から
|
|
364
374
|
// 引き算して rect x/y に渡す。
|
|
365
|
-
const bbox = computeLabelBBoxWorld(edge);
|
|
375
|
+
const bbox = computeLabelBBoxWorld(edge, textMetrics);
|
|
366
376
|
const boxW = bbox.w;
|
|
367
377
|
const boxH = bbox.h;
|
|
368
378
|
const boxLeftX = bbox.x - edge.labelX;
|
|
@@ -382,6 +392,8 @@ export function CdlEdgeLabelView({
|
|
|
382
392
|
data-cdl-edge-label-for={edge.id}
|
|
383
393
|
data-cdl-edge-label-text={edge.label}
|
|
384
394
|
data-cdl-edge-sub-text={edge.sub ?? ""}
|
|
395
|
+
data-cdl-tone={edge.tone}
|
|
396
|
+
data-cdl-edge-role={edge.role}
|
|
385
397
|
>
|
|
386
398
|
{/* 下地は既定で敷く。 丸い下地は箱と同じ形なので、 罫の細い図では外せるようにした (#618) */}
|
|
387
399
|
{edge.labelPlate !== false && (
|
|
@@ -440,12 +452,14 @@ export function CdlEdgeEndLabelView({
|
|
|
440
452
|
edge,
|
|
441
453
|
active,
|
|
442
454
|
端,
|
|
455
|
+
textMetrics = "proportional",
|
|
443
456
|
}: {
|
|
444
457
|
edge: LaidEdge;
|
|
445
458
|
active: boolean;
|
|
446
459
|
端: 線の端;
|
|
460
|
+
textMetrics?: CdlTextMetrics;
|
|
447
461
|
}): JSX.Element | null {
|
|
448
|
-
const 置き場所 = 端の字を置く(edge,
|
|
462
|
+
const 置き場所 = 端の字を置く(edge, 端, textMetrics);
|
|
449
463
|
if (!置き場所) return null;
|
|
450
464
|
const color = edgeColor(edge);
|
|
451
465
|
return (
|
|
@@ -453,6 +467,8 @@ export function CdlEdgeEndLabelView({
|
|
|
453
467
|
data-cdl-role="edge-end-label"
|
|
454
468
|
data-cdl-edge-end-label-for={edge.id}
|
|
455
469
|
data-cdl-edge-end={端}
|
|
470
|
+
data-cdl-tone={edge.tone}
|
|
471
|
+
data-cdl-edge-role={edge.role}
|
|
456
472
|
x={置き場所.x}
|
|
457
473
|
y={置き場所.y}
|
|
458
474
|
fontSize={端の字の大きさ}
|