@cardenelabs/cdl 0.122.0 → 0.124.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 +28 -1
- package/README.md +70 -1
- package/dist/index.cjs +1125 -618
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -26
- package/dist/index.d.ts +38 -26
- package/dist/index.js +1125 -619
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +894 -404
- 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 +894 -404
- package/dist/react.js.map +1 -1
- package/dist/{render-U_pC-Ksh.d.cts → render-U6omSNH2.d.cts} +23 -4
- package/dist/{render-U_pC-Ksh.d.ts → render-U6omSNH2.d.ts} +23 -4
- package/package.json +9 -12
- package/src/builder.ts +5 -0
- package/src/index.ts +2 -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/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/text-width.ts +17 -3
- package/src/kinds/tree.tsx +25 -7
- package/src/layout/collisions.ts +21 -8
- package/src/layout/curve-path.ts +3 -2
- package/src/layout/edges.ts +173 -29
- package/src/layout/head-label.ts +3 -1
- package/src/layout/lanes.ts +3 -2
- package/src/layout/metro-path.ts +3 -2
- 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 +20 -6
- package/src/layout/text-width.ts +25 -8
- package/src/layout.ts +39 -15
- package/src/presets.ts +69 -43
- package/src/render/edges.tsx +18 -3
- package/src/render/interactive-panel.tsx +34 -7
- package/src/render/nodes.tsx +97 -48
- package/src/render/stage.tsx +3 -0
- package/src/text-metrics.ts +9 -0
- package/src/types.ts +22 -2
- package/src/visual-validate.ts +67 -17
package/src/visual-validate.ts
CHANGED
|
@@ -77,7 +77,14 @@
|
|
|
77
77
|
* test が無いと、 次に種別を足した時に同じ状態へ戻る。
|
|
78
78
|
* ───────────────────────────────────────────────────────────
|
|
79
79
|
*/
|
|
80
|
-
import {
|
|
80
|
+
import {
|
|
81
|
+
EDGE_HEADS,
|
|
82
|
+
type CdlDiagram,
|
|
83
|
+
type CdlTextMetrics,
|
|
84
|
+
type LaidDiagram,
|
|
85
|
+
type LaidNode,
|
|
86
|
+
type NodeKind,
|
|
87
|
+
} from "./types";
|
|
81
88
|
import { CLEARANCE_LANE_LABEL, DIST_LABEL_PATH_MAX } from "./layout/clearance-constants";
|
|
82
89
|
import { layout } from "./layout";
|
|
83
90
|
import {
|
|
@@ -92,7 +99,7 @@ import {
|
|
|
92
99
|
} from "./layout/collisions";
|
|
93
100
|
import { measureTextWidth } from "./layout/text-width";
|
|
94
101
|
import { droppedLineIsHidden, labelPlan } from "./kinds/shape-region";
|
|
95
|
-
import { 箱に積む, type 積む行 } from "./kinds/box-lines";
|
|
102
|
+
import { 箱に積む, 箱の字を測る, type 積む行 } from "./kinds/box-lines";
|
|
96
103
|
import { 木の中身, 木の行 } from "./kinds/tree";
|
|
97
104
|
import { 放射の中身, 放射の行 } from "./kinds/mind-map";
|
|
98
105
|
import { templateRefIds } from "./render/utils";
|
|
@@ -587,7 +594,12 @@ function emptyCounts(): Record<VisualAxis, number> {
|
|
|
587
594
|
}
|
|
588
595
|
|
|
589
596
|
/** 箱に行を積む種別の、 中の 1 箱。 */
|
|
590
|
-
type 積む箱 = {
|
|
597
|
+
type 積む箱 = {
|
|
598
|
+
行: 積む行[];
|
|
599
|
+
w: number;
|
|
600
|
+
h: number;
|
|
601
|
+
測る: (text: string, fontSize: number) => number;
|
|
602
|
+
};
|
|
591
603
|
|
|
592
604
|
/**
|
|
593
605
|
* 木と放射の中の箱を、 **描く時と同じ手順で** 集める (`#538`)。
|
|
@@ -601,10 +613,23 @@ type 積む箱 = { 行: 積む行[]; w: number; h: number };
|
|
|
601
613
|
*
|
|
602
614
|
* 行を積まない種別では空を返す。
|
|
603
615
|
*/
|
|
604
|
-
function 積む箱を集める(
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
616
|
+
function 積む箱を集める(
|
|
617
|
+
n: LaidNode,
|
|
618
|
+
textMetrics: CdlTextMetrics = "proportional",
|
|
619
|
+
): 積む箱[] {
|
|
620
|
+
const 箱 = (
|
|
621
|
+
b: { w: number; h: number },
|
|
622
|
+
行: 積む行[],
|
|
623
|
+
測る: 積む箱["測る"],
|
|
624
|
+
): 積む箱 => ({ 行, w: b.w, h: b.h, 測る });
|
|
625
|
+
if (n.kind === "tree-hierarchy") {
|
|
626
|
+
const 測る = 箱の字を測る(textMetrics);
|
|
627
|
+
return 木の中身(n, {}, textMetrics).nodes.map((b) => 箱(b, 木の行(b), 測る));
|
|
628
|
+
}
|
|
629
|
+
if (n.kind === "mind-map") {
|
|
630
|
+
const 測る = 箱の字を測る(textMetrics);
|
|
631
|
+
return (放射の中身(n, {}, textMetrics)?.nodes ?? []).map((b) => 箱(b, 放射の行(b), 測る));
|
|
632
|
+
}
|
|
608
633
|
return [];
|
|
609
634
|
}
|
|
610
635
|
|
|
@@ -1972,6 +1997,7 @@ function runAxes(
|
|
|
1972
1997
|
/** 軸 23 が基準にする器。 入口で確かめ済 (`器を確かめる`)。 高さ `undefined` は上限なし。 */
|
|
1973
1998
|
器: { 幅: number; 高さ: number | undefined },
|
|
1974
1999
|
): VisualValidationReport {
|
|
2000
|
+
const textMetrics = laid.textMetrics ?? "proportional";
|
|
1975
2001
|
// ───────────────────────────────────────────────────────────
|
|
1976
2002
|
// Axis 1: node-visibility (bbox 面積 / 最小寸法)
|
|
1977
2003
|
//
|
|
@@ -1994,7 +2020,7 @@ function runAxes(
|
|
|
1994
2020
|
// ───────────────────────────────────────────────────────────
|
|
1995
2021
|
// Axis 2: edge-label-overlap (edge label ↔ node の AABB collision)
|
|
1996
2022
|
// ───────────────────────────────────────────────────────────
|
|
1997
|
-
const bboxes = collectBBoxes(laid.lanes, laid.nodes, laid.edges);
|
|
2023
|
+
const bboxes = collectBBoxes(laid.lanes, laid.nodes, laid.edges, textMetrics);
|
|
1998
2024
|
const collisions = detectCollisions(bboxes, laid.edges);
|
|
1999
2025
|
for (const c of collisions) {
|
|
2000
2026
|
const involvesEdgeLabel = c.a.kind === "edge-label" || c.b.kind === "edge-label";
|
|
@@ -2041,7 +2067,11 @@ function runAxes(
|
|
|
2041
2067
|
// 名前と図表の題) は内側の余白に縛られないので、足すと収まっている題まで挙がる。
|
|
2042
2068
|
const 余白 = drawsTitleOutsideBox(n.kind) ? 0 : TITLE_BOX_PADDING;
|
|
2043
2069
|
const 使える幅 = n.w - 余白;
|
|
2044
|
-
const 実幅 = measureTextWidth(titleTrimmed, {
|
|
2070
|
+
const 実幅 = measureTextWidth(titleTrimmed, {
|
|
2071
|
+
fontSize: titleFontSize(n.kind),
|
|
2072
|
+
bound: "drawn",
|
|
2073
|
+
textMetrics,
|
|
2074
|
+
});
|
|
2045
2075
|
if (titleScalesToFit(n.kind, n.h)) {
|
|
2046
2076
|
// 収まるまで縮める種別は、題がはみ出すことが起きない。 代わりに字が読めない大きさまで
|
|
2047
2077
|
// 落ちるので、**読める下限を割るか** を見る (#951)。 下限まで縮めても収まらない時
|
|
@@ -2157,7 +2187,15 @@ function runAxes(
|
|
|
2157
2187
|
: null;
|
|
2158
2188
|
const 相手 = 動かす === n.a ? n.b : n.a;
|
|
2159
2189
|
const reason =
|
|
2160
|
-
動かす !== null
|
|
2190
|
+
動かす !== null
|
|
2191
|
+
? (札が動けない理由(
|
|
2192
|
+
動かす.id,
|
|
2193
|
+
相手,
|
|
2194
|
+
laid.edges,
|
|
2195
|
+
札の障害物,
|
|
2196
|
+
textMetrics,
|
|
2197
|
+
) ?? undefined)
|
|
2198
|
+
: undefined;
|
|
2161
2199
|
push(
|
|
2162
2200
|
"clearance",
|
|
2163
2201
|
`${n.a.kind}:${n.a.id} ↔ ${n.b.kind}:${n.b.id} gap=${n.gap.toFixed(1)}px (need ${n.required}px)`,
|
|
@@ -2194,7 +2232,10 @@ function runAxes(
|
|
|
2194
2232
|
if (!hasRenderedLabel(e)) continue;
|
|
2195
2233
|
const segs = extractPathSegments(e.d);
|
|
2196
2234
|
if (segs.length === 0) continue;
|
|
2197
|
-
const minDist = measurePathLabelGapSegs(
|
|
2235
|
+
const minDist = measurePathLabelGapSegs(
|
|
2236
|
+
flattenPathSegments(segs),
|
|
2237
|
+
computeLabelBBoxWorld(e, textMetrics),
|
|
2238
|
+
);
|
|
2198
2239
|
if (!Number.isFinite(minDist)) continue;
|
|
2199
2240
|
if (minDist > PROXIMITY_ERROR_THRESHOLD) {
|
|
2200
2241
|
push(
|
|
@@ -2264,9 +2305,16 @@ function runAxes(
|
|
|
2264
2305
|
// ───────────────────────────────────────────────────────────
|
|
2265
2306
|
for (const e of laid.edges) {
|
|
2266
2307
|
if (!hasRenderedLabel(e)) continue;
|
|
2267
|
-
const mainW = measureTextWidth(e.label ?? "", {
|
|
2308
|
+
const mainW = measureTextWidth(e.label ?? "", {
|
|
2309
|
+
fontSize: EDGE_LABEL_TEXT.main.fontSize,
|
|
2310
|
+
textMetrics,
|
|
2311
|
+
});
|
|
2268
2312
|
const subW = e.sub
|
|
2269
|
-
? measureTextWidth(e.sub, {
|
|
2313
|
+
? measureTextWidth(e.sub, {
|
|
2314
|
+
fontSize: EDGE_LABEL_TEXT.sub.fontSize,
|
|
2315
|
+
fontFamily: "mono",
|
|
2316
|
+
textMetrics,
|
|
2317
|
+
})
|
|
2270
2318
|
: 0;
|
|
2271
2319
|
const expectedBoxW = Math.max(mainW, subW) + LABEL_PILL_PAD_X * 2;
|
|
2272
2320
|
const bbox = bboxes.find((b) => b.kind === "edge-label" && b.id === e.id);
|
|
@@ -2499,7 +2547,9 @@ function runAxes(
|
|
|
2499
2547
|
// 共有 plan が行を落とさない高さでは、 固有 layout がその行を描かない種別でも報告されない。
|
|
2500
2548
|
// 本軸は箱が狭くて落ちた行だけを扱い、 種別が元から対応しない欄は別の検査範囲とする。
|
|
2501
2549
|
for (const n of laid.nodes) {
|
|
2502
|
-
const 落とした = labelPlan(n).落とした.filter((line) =>
|
|
2550
|
+
const 落とした = labelPlan(n).落とした.filter((line) =>
|
|
2551
|
+
droppedLineIsHidden(n.kind, line),
|
|
2552
|
+
);
|
|
2503
2553
|
if (落とした.length === 0) continue;
|
|
2504
2554
|
push(
|
|
2505
2555
|
"shape-label-dropped",
|
|
@@ -2531,7 +2581,7 @@ function runAxes(
|
|
|
2531
2581
|
// なので、 対象から外すと変えた時に落ちても見えない。
|
|
2532
2582
|
// ───────────────────────────────────────────────────────────
|
|
2533
2583
|
for (const n of laid.nodes) {
|
|
2534
|
-
const 箱 = 積む箱を集める(n);
|
|
2584
|
+
const 箱 = 積む箱を集める(n, textMetrics);
|
|
2535
2585
|
if (箱.length === 0) continue;
|
|
2536
2586
|
/** 補足を書いた箱の数。 書いていない箱は元から描かれないので数えない。 */
|
|
2537
2587
|
let 対象 = 0;
|
|
@@ -2541,7 +2591,7 @@ function runAxes(
|
|
|
2541
2591
|
const 宣言 = b.行.slice(1).filter((r) => r.text.trim() !== "");
|
|
2542
2592
|
if (宣言.length === 0) continue;
|
|
2543
2593
|
対象 += 1;
|
|
2544
|
-
const 残った = new Set(箱に積む(b.行, b.w, b.h).map((r) => r.役割));
|
|
2594
|
+
const 残った = new Set(箱に積む(b.行, b.w, b.h, b.測る).map((r) => r.役割));
|
|
2545
2595
|
if (宣言.every((r) => 残った.has(r.役割))) continue;
|
|
2546
2596
|
落ちた.push({ title: b.行[0]?.text ?? "", h: b.h });
|
|
2547
2597
|
}
|
|
@@ -4402,7 +4452,7 @@ function runAxes(
|
|
|
4402
4452
|
const allowedLanes = new Set<string>();
|
|
4403
4453
|
if (fromNode) allowedLanes.add(fromNode.lane);
|
|
4404
4454
|
if (toNode) allowedLanes.add(toNode.lane);
|
|
4405
|
-
const box = computeLabelBBoxWorld(e);
|
|
4455
|
+
const box = computeLabelBBoxWorld(e, textMetrics);
|
|
4406
4456
|
for (const lane of laid.lanes) {
|
|
4407
4457
|
if (allowedLanes.has(lane.id)) continue;
|
|
4408
4458
|
const side = rectCrossesLaneBorder(box, lane, rangeByLane.get(lane.id));
|