@cardenelabs/cdl 0.51.0 → 0.52.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 +15 -1
- package/dist/index.cjs +44 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +44 -30
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +41 -30
- 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 +41 -30
- package/dist/react.js.map +1 -1
- package/dist/{render-BoBEyCu-.d.cts → render-C0PJ2weJ.d.cts} +6 -0
- package/dist/{render-BoBEyCu-.d.ts → render-C0PJ2weJ.d.ts} +6 -0
- package/package.json +1 -1
- package/src/builder.ts +6 -0
- package/src/layout/head-label.ts +56 -36
- package/src/presets.ts +3 -0
- package/src/render/edges.tsx +8 -5
- package/src/render/stage.tsx +13 -10
- package/src/types.ts +6 -0
|
@@ -693,6 +693,12 @@ type CdlEdge = {
|
|
|
693
693
|
* 置き場所は線の最後の区間から決まる (`layout/head-label.ts`)。
|
|
694
694
|
*/
|
|
695
695
|
headLabel?: string;
|
|
696
|
+
/**
|
|
697
|
+
* 出どころの端に添える短い字 (#825)。 `headLabel` と対。 クラス図の出どころ側の多重度に使う。
|
|
698
|
+
*
|
|
699
|
+
* UML は関係の両端に多重度を書く。 置き場所の計算は `headLabel` と同じ (`layout/head-label.ts`)。
|
|
700
|
+
*/
|
|
701
|
+
tailLabel?: string;
|
|
696
702
|
/** label の x 方向 fine offset (default 0)、 著者が細かく位置調整するための逃げ道 */
|
|
697
703
|
labelOffsetX?: number;
|
|
698
704
|
/** label の y 方向 fine offset (default 0、 + で下、 - で上) */
|
|
@@ -693,6 +693,12 @@ type CdlEdge = {
|
|
|
693
693
|
* 置き場所は線の最後の区間から決まる (`layout/head-label.ts`)。
|
|
694
694
|
*/
|
|
695
695
|
headLabel?: string;
|
|
696
|
+
/**
|
|
697
|
+
* 出どころの端に添える短い字 (#825)。 `headLabel` と対。 クラス図の出どころ側の多重度に使う。
|
|
698
|
+
*
|
|
699
|
+
* UML は関係の両端に多重度を書く。 置き場所の計算は `headLabel` と同じ (`layout/head-label.ts`)。
|
|
700
|
+
*/
|
|
701
|
+
tailLabel?: string;
|
|
696
702
|
/** label の x 方向 fine offset (default 0)、 著者が細かく位置調整するための逃げ道 */
|
|
697
703
|
labelOffsetX?: number;
|
|
698
704
|
/** label の y 方向 fine offset (default 0、 + で下、 - で上) */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cardenelabs/cdl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.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",
|
package/src/builder.ts
CHANGED
|
@@ -1454,6 +1454,8 @@ export type DiagramBuilder = {
|
|
|
1454
1454
|
cardinality?: string;
|
|
1455
1455
|
/** 行き先の端に添える字 (#821)。 クラス図の多重度に使う */
|
|
1456
1456
|
headLabel?: string;
|
|
1457
|
+
/** 出どころの端に添える字 (#825)。 クラス図の出どころ側の多重度に使う */
|
|
1458
|
+
tailLabel?: string;
|
|
1457
1459
|
labelOffsetX?: number;
|
|
1458
1460
|
labelOffsetY?: number;
|
|
1459
1461
|
routing?: "default" | "back-detour";
|
|
@@ -1564,6 +1566,8 @@ export type DiagramBuilder = {
|
|
|
1564
1566
|
cardinality?: string;
|
|
1565
1567
|
/** 行き先の端に添える字 (#821)。 クラス図の多重度に使う */
|
|
1566
1568
|
headLabel?: string;
|
|
1569
|
+
/** 出どころの端に添える字 (#825)。 クラス図の出どころ側の多重度に使う */
|
|
1570
|
+
tailLabel?: string;
|
|
1567
1571
|
labelOffsetX?: number;
|
|
1568
1572
|
labelOffsetY?: number;
|
|
1569
1573
|
routing?: "default" | "back-detour";
|
|
@@ -1781,6 +1785,7 @@ export function diagram(
|
|
|
1781
1785
|
guard: opts.guard,
|
|
1782
1786
|
cardinality: opts.cardinality,
|
|
1783
1787
|
headLabel: opts.headLabel,
|
|
1788
|
+
tailLabel: opts.tailLabel,
|
|
1784
1789
|
labelOffsetX: opts.labelOffsetX,
|
|
1785
1790
|
labelOffsetY: opts.labelOffsetY,
|
|
1786
1791
|
routing: opts.routing,
|
|
@@ -1916,6 +1921,7 @@ export function diagram(
|
|
|
1916
1921
|
guard: d.guard,
|
|
1917
1922
|
cardinality: d.cardinality,
|
|
1918
1923
|
headLabel: d.headLabel,
|
|
1924
|
+
tailLabel: d.tailLabel,
|
|
1919
1925
|
labelOffsetX: d.labelOffsetX,
|
|
1920
1926
|
labelOffsetY: d.labelOffsetY,
|
|
1921
1927
|
routing: d.routing,
|
package/src/layout/head-label.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 線の端に添える字 (`headLabel` / `tailLabel`) の置き場所 (#821 / #825)。
|
|
3
3
|
*
|
|
4
4
|
* 描く側 (`render/edges.tsx`) と検査が同じ値を使うため、計算をここに 1 つだけ置く。
|
|
5
|
+
* 行き先の端 (`head`) と出どころの端 (`tail`) は、端での線の向きの取り方だけが違う。
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* 隙間は変わらない。
|
|
7
|
+
* 端での線の向きで決める。 端から `端の字を戻す距離` だけ線を戻った点を基準に、線の脇へ置く。
|
|
8
|
+
* 字は端に近い側の縁を基準点に揃える = 字の長さが変わっても、端から字までの隙間は変わらない。
|
|
9
9
|
*
|
|
10
10
|
* **線の札の反対側に置く**。 札と同じ側に置くと、札の語と多重度が続けて読まれる (「持つ」 の
|
|
11
11
|
* 隣に `1..*`、 英字の頃は「HAS1」)。 実測で、札と同じ側 (横向きなら上、縦向きなら右) に
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* 札を持たない線は、横向きなら下、縦向きなら左に置く (engine が札を置く既定の側の反対)。
|
|
14
14
|
*
|
|
15
15
|
* 斜めの線と曲線は、向きの大きいほうの軸で横向き / 縦向きに振り分ける。 曲線の向きは
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* 端の接線 (行き先の端なら最後の制御点から終点へ、出どころの端なら最初の制御点から始点へ) を
|
|
17
|
+
* 使う。 弦の向きを使うと、大きく膨らむ曲線で線が端へ入ってくる向きと別の向きが出る。
|
|
18
18
|
*/
|
|
19
19
|
import { EDGE_LABEL_TEXT } from "../label-text";
|
|
20
20
|
import { extractPathSegments } from "./collisions";
|
|
@@ -25,7 +25,7 @@ import { measureTextWidth } from "./text-width";
|
|
|
25
25
|
* 端から字までの、線に沿った距離 (world)。
|
|
26
26
|
*
|
|
27
27
|
* 端の印の長さの最大 (光っている時の 14、 `render/stage.tsx` の marker の大きさ) に隙間 8 を足す。
|
|
28
|
-
* 印を持たない端 (菱が出どころ側にある「持つ」 「抱える」) でも同じ距離にする = 端ごとに
|
|
28
|
+
* 印を持たない端 (菱が出どころ側にある「持つ」 「抱える」 の行き先) でも同じ距離にする = 端ごとに
|
|
29
29
|
* 距離が変わると、図の中で多重度の位置が揃わない。
|
|
30
30
|
*/
|
|
31
31
|
export const 端の字を戻す距離 = 22;
|
|
@@ -42,6 +42,9 @@ const 箱の高さ = Math.round(端の字の大きさ * 1.2);
|
|
|
42
42
|
/** 字の大きさに対する baseline の深さ。 等幅の字の ascent が字の大きさの 8 割ほど */
|
|
43
43
|
const BASELINE_RATIO = 0.8;
|
|
44
44
|
|
|
45
|
+
/** どちらの端か。 `head` = 行き先 (`to`)、 `tail` = 出どころ (`from`) */
|
|
46
|
+
export type 線の端 = "head" | "tail";
|
|
47
|
+
|
|
45
48
|
export interface 端の字の置き場所 {
|
|
46
49
|
/** `text` の x。 どちらの縁かは `anchor` が決める */
|
|
47
50
|
x: number;
|
|
@@ -52,43 +55,60 @@ export interface 端の字の置き場所 {
|
|
|
52
55
|
box: { x: number; y: number; w: number; h: number };
|
|
53
56
|
}
|
|
54
57
|
|
|
58
|
+
type 点 = { x: number; y: number };
|
|
59
|
+
|
|
60
|
+
/** 端の点と、線がその端へ入ってくる向き (単位ベクトル)。 取れない時は `null` */
|
|
61
|
+
function 向きを取る(end: 点, 候補: ReadonlyArray<点 | undefined>): { end: 点; ux: number; uy: number } | null {
|
|
62
|
+
for (const p of 候補) {
|
|
63
|
+
if (!p) continue;
|
|
64
|
+
const dx = end.x - p.x;
|
|
65
|
+
const dy = end.y - p.y;
|
|
66
|
+
const len = Math.hypot(dx, dy);
|
|
67
|
+
if (len > 1e-6) return { end, ux: dx / len, uy: dy / len };
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
55
72
|
/**
|
|
56
|
-
*
|
|
73
|
+
* 端の点と、線がその端へ入ってくる向き。
|
|
57
74
|
*
|
|
58
|
-
*
|
|
59
|
-
* 時は `null`。
|
|
75
|
+
* 行き先の端は最後の区間から、出どころの端は最初の区間を逆にたどって取る。 端の区間の長さが
|
|
76
|
+
* 0 の時は隣の区間へ進む。 向きが取れない (区間が無い / 全て長さ 0) 時は `null`。
|
|
60
77
|
*/
|
|
61
|
-
function
|
|
78
|
+
function 端での向き(d: string, 端: 線の端): { end: 点; ux: number; uy: number } | null {
|
|
62
79
|
const segs = extractPathSegments(d);
|
|
63
|
-
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const dy = end.y - p.y;
|
|
73
|
-
const len = Math.hypot(dx, dy);
|
|
74
|
-
if (len > 1e-6) return { end, ux: dx / len, uy: dy / len };
|
|
75
|
-
}
|
|
80
|
+
if (segs.length === 0) return null;
|
|
81
|
+
if (端 === "head") {
|
|
82
|
+
const last = segs[segs.length - 1]!;
|
|
83
|
+
const end = { x: last.x2, y: last.y2 };
|
|
84
|
+
const 候補 = segs
|
|
85
|
+
.slice()
|
|
86
|
+
.reverse()
|
|
87
|
+
.flatMap((s) => [s.cmd === "Q" ? s.ctrl?.[0] : s.cmd === "C" ? s.ctrl?.[1] : undefined, { x: s.x1, y: s.y1 }]);
|
|
88
|
+
return 向きを取る(end, 候補);
|
|
76
89
|
}
|
|
77
|
-
|
|
90
|
+
const first = segs[0]!;
|
|
91
|
+
const end = { x: first.x1, y: first.y1 };
|
|
92
|
+
const 候補 = segs.flatMap((s) => [...(s.ctrl ?? []), { x: s.x2, y: s.y2 }]);
|
|
93
|
+
return 向きを取る(end, 候補);
|
|
78
94
|
}
|
|
79
95
|
|
|
80
|
-
/**
|
|
81
|
-
export function 端の字を置く(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
96
|
+
/** 端の字を描く位置。 字が空か、線の向きが取れない時は `null` (描かない) */
|
|
97
|
+
export function 端の字を置く(
|
|
98
|
+
edge: {
|
|
99
|
+
d: string;
|
|
100
|
+
headLabel?: string;
|
|
101
|
+
tailLabel?: string;
|
|
102
|
+
label?: string;
|
|
103
|
+
sub?: string;
|
|
104
|
+
labelX?: number;
|
|
105
|
+
labelY?: number;
|
|
106
|
+
},
|
|
107
|
+
端: 線の端 = "head",
|
|
108
|
+
): 端の字の置き場所 | null {
|
|
109
|
+
const text = (端 === "head" ? edge.headLabel : edge.tailLabel)?.trim();
|
|
90
110
|
if (!text) return null;
|
|
91
|
-
const 向き =
|
|
111
|
+
const 向き = 端での向き(edge.d, 端);
|
|
92
112
|
if (!向き) return null;
|
|
93
113
|
const { end, ux, uy } = 向き;
|
|
94
114
|
const bx = end.x - ux * 端の字を戻す距離;
|
package/src/presets.ts
CHANGED
|
@@ -1432,6 +1432,8 @@ export type UmlRelation = {
|
|
|
1432
1432
|
label?: string;
|
|
1433
1433
|
/** 多重度 (例 "1..*" / "0..1")。 行き先 (`to`) の端に添える (#821) */
|
|
1434
1434
|
cardinality?: string;
|
|
1435
|
+
/** 出どころ (`from`) 側の多重度 (例 "1")。 出どころの端に添える (#825) */
|
|
1436
|
+
tailCardinality?: string;
|
|
1435
1437
|
tone?: Tone;
|
|
1436
1438
|
/**
|
|
1437
1439
|
* 矢印の先の形 (cdl#574)。 書かなければ種類から決まる (`CLASS_RELATION_LOOK`)。
|
|
@@ -1576,6 +1578,7 @@ export function classDiagram(preset: ClassDiagramPreset): ClassDiagramBuilder {
|
|
|
1576
1578
|
// 多重度は行き先の端に添える (#821)。 札の下の行に入れると語と続けて読まれ
|
|
1577
1579
|
// (英字の頃は「HAS 1..*」 が「HAS1」)、 どちらの端の数かも読めない
|
|
1578
1580
|
...(r.cardinality ? { headLabel: r.cardinality } : {}),
|
|
1581
|
+
...(r.tailCardinality ? { tailLabel: r.tailCardinality } : {}),
|
|
1579
1582
|
tone: r.tone ?? 図の既定 ?? CLASS_RELATION_TONE[r.type],
|
|
1580
1583
|
// 関係の種類から線と端の形を決める (cdl#574 / #578)。 書いた値が勝つ
|
|
1581
1584
|
style: r.style ?? 見た目.style,
|
package/src/render/edges.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { 等幅の字 } from "./mono-text";
|
|
2
2
|
import type { JSX } from "react";
|
|
3
3
|
import { computeLabelBBoxWorld, hasRenderedLabel, LABEL_PILL_PAD_X } from "../layout/spec";
|
|
4
|
-
import { 端の字の大きさ,
|
|
4
|
+
import { 端の字の大きさ, 端の字を置く, type 線の端 } from "../layout/head-label";
|
|
5
5
|
import type { EdgeRole, LaidEdge, Tone } from "../types";
|
|
6
6
|
import { edgeHeadMarkerName } from "./edge-head";
|
|
7
7
|
import { toneColor } from "./tone";
|
|
@@ -397,25 +397,28 @@ export function CdlEdgeLabelView({
|
|
|
397
397
|
}
|
|
398
398
|
|
|
399
399
|
/**
|
|
400
|
-
*
|
|
400
|
+
* 線の端に添える字 (#821 / #825)。 札と同じく最前面の pass で描く。 `端` で行き先か出どころかを選ぶ。
|
|
401
401
|
*
|
|
402
402
|
* 下地の四角は敷かず、字の周りに下地色の縁取りを付ける (`paint-order: stroke`)。 字は線の脇、
|
|
403
403
|
* 箱の角の近くに置くので、四角い下地だと箱の角や線に被って小さな箱に見える。
|
|
404
404
|
*/
|
|
405
|
-
export function
|
|
405
|
+
export function CdlEdgeEndLabelView({
|
|
406
406
|
edge,
|
|
407
407
|
active,
|
|
408
|
+
端,
|
|
408
409
|
}: {
|
|
409
410
|
edge: LaidEdge;
|
|
410
411
|
active: boolean;
|
|
412
|
+
端: 線の端;
|
|
411
413
|
}): JSX.Element | null {
|
|
412
|
-
const 置き場所 = 端の字を置く(edge);
|
|
414
|
+
const 置き場所 = 端の字を置く(edge, 端);
|
|
413
415
|
if (!置き場所) return null;
|
|
414
416
|
const color = edgeColor(edge);
|
|
415
417
|
return (
|
|
416
418
|
<text
|
|
417
419
|
data-cdl-role="edge-end-label"
|
|
418
420
|
data-cdl-edge-end-label-for={edge.id}
|
|
421
|
+
data-cdl-edge-end={端}
|
|
419
422
|
x={置き場所.x}
|
|
420
423
|
y={置き場所.y}
|
|
421
424
|
fontSize={端の字の大きさ}
|
|
@@ -428,7 +431,7 @@ export function CdlEdgeHeadLabelView({
|
|
|
428
431
|
paintOrder="stroke"
|
|
429
432
|
style={{ fontFamily: fontFamilyAttr("sub") }}
|
|
430
433
|
>
|
|
431
|
-
{等幅の字(edge.headLabel, 端の字の大きさ)}
|
|
434
|
+
{等幅の字(端 === "head" ? edge.headLabel : edge.tailLabel, 端の字の大きさ)}
|
|
432
435
|
</text>
|
|
433
436
|
);
|
|
434
437
|
}
|
package/src/render/stage.tsx
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
edgeHeadMarkerName,
|
|
21
21
|
edgeHeadPaint,
|
|
22
22
|
} from "./edge-head";
|
|
23
|
-
import { CdlEdgeView, CdlEdgeGlowView,
|
|
23
|
+
import { CdlEdgeView, CdlEdgeGlowView, CdlEdgeEndLabelView, CdlEdgeLabelView } from "./edges";
|
|
24
24
|
import { CdlNodeView } from "./nodes";
|
|
25
25
|
import { resolveChartData } from "./payload-binding";
|
|
26
26
|
import { relatedFocusSet } from "./relation-focus";
|
|
@@ -453,16 +453,19 @@ export function CdlStage({
|
|
|
453
453
|
/>
|
|
454
454
|
))}
|
|
455
455
|
|
|
456
|
-
{/*
|
|
456
|
+
{/* 線の端に添える字 (#821 / #825)。 札と同じ最前面の pass */}
|
|
457
457
|
{laid.edges
|
|
458
|
-
.filter((edge) => edge.headLabel && 矢印の出番.出るか(edge.id))
|
|
459
|
-
.
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
458
|
+
.filter((edge) => (edge.headLabel || edge.tailLabel) && 矢印の出番.出るか(edge.id))
|
|
459
|
+
.flatMap((edge) =>
|
|
460
|
+
(["head", "tail"] as const).map((端) => (
|
|
461
|
+
<CdlEdgeEndLabelView
|
|
462
|
+
key={`${端}-label-${edge.id}`}
|
|
463
|
+
edge={edge}
|
|
464
|
+
active={activeSet.has(edge.id)}
|
|
465
|
+
端={端}
|
|
466
|
+
/>
|
|
467
|
+
)),
|
|
468
|
+
)}
|
|
466
469
|
|
|
467
470
|
{/* debug overlay = engine が把握している bbox + collision を半透明で重ね描き */}
|
|
468
471
|
{debug && (
|
package/src/types.ts
CHANGED
|
@@ -765,6 +765,12 @@ export type CdlEdge = {
|
|
|
765
765
|
* 置き場所は線の最後の区間から決まる (`layout/head-label.ts`)。
|
|
766
766
|
*/
|
|
767
767
|
headLabel?: string;
|
|
768
|
+
/**
|
|
769
|
+
* 出どころの端に添える短い字 (#825)。 `headLabel` と対。 クラス図の出どころ側の多重度に使う。
|
|
770
|
+
*
|
|
771
|
+
* UML は関係の両端に多重度を書く。 置き場所の計算は `headLabel` と同じ (`layout/head-label.ts`)。
|
|
772
|
+
*/
|
|
773
|
+
tailLabel?: string;
|
|
768
774
|
/** label の x 方向 fine offset (default 0)、 著者が細かく位置調整するための逃げ道 */
|
|
769
775
|
labelOffsetX?: number;
|
|
770
776
|
/** label の y 方向 fine offset (default 0、 + で下、 - で上) */
|