@cardenelabs/cdl 0.72.0 → 0.74.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 +68 -1
- package/dist/index.cjs +77 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +77 -23
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +137 -83
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +137 -83
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/src/kinds/shape-blockchain.tsx +58 -12
- package/src/kinds/shape-region.tsx +113 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cardenelabs/cdl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.74.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",
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import type { JSX } from "react";
|
|
2
2
|
import type { LaidNode } from "../types";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
fitArt,
|
|
5
|
+
labelPlan,
|
|
6
|
+
shapeRegion,
|
|
7
|
+
textWidth,
|
|
8
|
+
打ち消しの倍率,
|
|
9
|
+
絵の中の字の幅,
|
|
10
|
+
type ArtExtent,
|
|
11
|
+
type 打ち消す字,
|
|
12
|
+
} from "./shape-region";
|
|
4
13
|
import { 幅で切る } from "./box-lines";
|
|
5
|
-
import { MONO_ADVANCE_RATIO, displayWidth } from "./row-align";
|
|
6
14
|
import { 字引きを作る, type CdlLang, type CdlTextKey } from "../lang";
|
|
7
15
|
|
|
8
16
|
/**
|
|
@@ -29,6 +37,13 @@ const SMART_CONTRACT_H = 220;
|
|
|
29
37
|
|
|
30
38
|
const BLOCK_W = 240;
|
|
31
39
|
const BLOCK_H = 220;
|
|
40
|
+
/** `shape-blockchain-block` の字の指定 (#871 で打ち消しの計算に使うため定数へ出した) */
|
|
41
|
+
const 見出しの字 = 13;
|
|
42
|
+
const 名の字 = 10.5;
|
|
43
|
+
const 値の字 = 11;
|
|
44
|
+
/** 5 行を並べる間隔と、見出しが乗る帯の高さ。 打ち消しの上限をここから出す (#871) */
|
|
45
|
+
const 行の間隔 = 22;
|
|
46
|
+
const 見出しの帯 = 32;
|
|
32
47
|
const BLOCK_SHADOW = 6;
|
|
33
48
|
|
|
34
49
|
const NFT_W = 220;
|
|
@@ -252,8 +267,7 @@ export function ShapeSmartContractNode({ node, active, lang }: ShapeProps): JSX.
|
|
|
252
267
|
* どちらの書体で描かれても実物以上に出る側へ倒す = 下回ると「収まっていない物を収まっていると
|
|
253
268
|
* 判定」 する向きに外れる (`row-align.ts` の `rowColumnWidth` と同じ考え方)。
|
|
254
269
|
*/
|
|
255
|
-
const 字の幅 = (text: string, size: number): number =>
|
|
256
|
-
Math.max(displayWidth(text) * size * MONO_ADVANCE_RATIO, textWidth(text, size));
|
|
270
|
+
const 字の幅 = (text: string, size: number): number => 絵の中の字の幅(text, size);
|
|
257
271
|
|
|
258
272
|
/**
|
|
259
273
|
* 絵の中の 5 行の既定値。 書き手が渡さなかった欄はこれで描く (#469)。
|
|
@@ -321,27 +335,58 @@ export function ShapeBlockchainBlockNode({ node, active, lang }: ShapeProps): JS
|
|
|
321
335
|
|
|
322
336
|
// 見出しは右上の丸 (半径 5.5、 中心が bw - 16) に掛からないところまで
|
|
323
337
|
const 見出しの幅 = bw - 14 - 30;
|
|
324
|
-
|
|
338
|
+
/*
|
|
339
|
+
* 絵を **倍率を受け取ってから** 組む (#871)。 縮んだ分を字の指定で打ち消す。
|
|
340
|
+
*
|
|
341
|
+
* **幅の予算も打ち消した後の大きさで計算する** = 字だけ大きくして切る位置を元の大きさの
|
|
342
|
+
* まま置くと、切った字が枠から出る。
|
|
343
|
+
*/
|
|
344
|
+
/*
|
|
345
|
+
* 打ち消しの上限を出す帯 (#886)。 **絵の定数から渡す**。
|
|
346
|
+
*
|
|
347
|
+
* 見出しは高さ 32 の帯に乗り、5 行は 22 ずつ空けて並ぶ。 幅は行の内側 (`bw - 28`) で、
|
|
348
|
+
* 値は名前の分を引いた残り = 名前と値が同じ行に並ぶため。
|
|
349
|
+
*
|
|
350
|
+
* 値の幅は **打ち消す前の名前の幅** で引く。 打ち消した後の幅で引くと、上限を出すのに
|
|
351
|
+
* 上限が要る形になる。 名前は打ち消しで広がるので、その分は帯に対する上限 (8 割) が吸う。
|
|
352
|
+
*/
|
|
353
|
+
const 打ち消す字ら: 打ち消す字[] = [
|
|
354
|
+
{ text: 見出し, size: 見出しの字, 帯: { 幅: 見出しの幅, 高さ: 見出しの帯 } },
|
|
355
|
+
...fields.flatMap((f): 打ち消す字[] => [
|
|
356
|
+
{ text: f.label, size: 名の字, 帯: { 幅: bw - 28, 高さ: 行の間隔 } },
|
|
357
|
+
{
|
|
358
|
+
text: f.value,
|
|
359
|
+
size: 値の字,
|
|
360
|
+
帯: { 幅: bw - 28 - 字の幅(f.label, 名の字) - 8, 高さ: 行の間隔 },
|
|
361
|
+
},
|
|
362
|
+
]),
|
|
363
|
+
];
|
|
364
|
+
const art = (倍率: number): JSX.Element => {
|
|
365
|
+
const 倍 = 打ち消しの倍率(倍率, 打ち消す字ら);
|
|
366
|
+
const 題の大きさ = 見出しの字 * 倍;
|
|
367
|
+
const 名の大きさ = 名の字 * 倍;
|
|
368
|
+
const 値の大きさ = 値の字 * 倍;
|
|
369
|
+
return (
|
|
325
370
|
<g>
|
|
326
371
|
{/* 3D shadow */}
|
|
327
372
|
<rect x={x + 6} y={y + 6} width={bw} height={bh} rx={8} fill={frame.stroke} opacity={0.2} stroke="none" />
|
|
328
373
|
<rect data-cdl-role="node-body" x={x} y={y} width={bw} height={bh} rx={8} {...frame} />
|
|
329
374
|
{/* header bar */}
|
|
330
375
|
<path d={`M ${x} ${y + 32} L ${x} ${y + 8} Q ${x} ${y}, ${x + 8} ${y} L ${x + bw - 8} ${y} Q ${x + bw} ${y}, ${x + bw} ${y + 8} L ${x + bw} ${y + 32} Z`} fill="var(--cdl-tone-accent, #2d6a8f)" fillOpacity={0.9} stroke="none" />
|
|
331
|
-
<text data-cdl-role="blockchain-block-title" x={x + 14} y={y + 22} fontSize={
|
|
332
|
-
{幅で切る(見出し,
|
|
376
|
+
<text data-cdl-role="blockchain-block-title" data-cdl-counter-scaled="1" x={x + 14} y={y + 22} fontSize={題の大きさ} fontWeight={700} fontFamily="ui-monospace, SFMono-Regular, Menlo, monospace" fill="#ffffff">
|
|
377
|
+
{幅で切る(見出し, 題の大きさ, 見出しの幅, 字の幅)}
|
|
333
378
|
</text>
|
|
334
379
|
<circle cx={x + bw - 16} cy={y + 16} r={5.5} fill="#7cd88f" stroke="#ffffff" strokeWidth={1.5} />
|
|
335
380
|
{/* fields */}
|
|
336
381
|
{fields.map((f, i) => (
|
|
337
382
|
<g key={i}>
|
|
338
|
-
<text x={x + 14} y={y + 54 + i *
|
|
383
|
+
<text data-cdl-counter-scaled="1" x={x + 14} y={y + 54 + i * 行の間隔} fontSize={名の大きさ} fontFamily="ui-monospace, SFMono-Regular, Menlo, monospace" fill="var(--cdl-text-dim, #5a6270)">
|
|
339
384
|
{f.label}
|
|
340
385
|
</text>
|
|
341
|
-
<text data-cdl-role={`blockchain-block-${f.key}`} x={x + bw - 14} y={y + 54 + i *
|
|
342
|
-
{幅で切る(f.value,
|
|
386
|
+
<text data-cdl-role={`blockchain-block-${f.key}`} data-cdl-counter-scaled="1" x={x + bw - 14} y={y + 54 + i * 行の間隔} fontSize={値の大きさ} fontFamily="ui-monospace, SFMono-Regular, Menlo, monospace" fontWeight={600} textAnchor="end" fill="var(--cdl-text, #1a1f2a)">
|
|
387
|
+
{幅で切る(f.value, 値の大きさ, bw - 28 - 字の幅(f.label, 名の大きさ) - 8, 字の幅)}
|
|
343
388
|
</text>
|
|
344
|
-
<line x1={x + 14} y1={y + 60 + i *
|
|
389
|
+
<line x1={x + 14} y1={y + 60 + i * 行の間隔} x2={x + bw - 14} y2={y + 60 + i * 行の間隔} stroke="var(--cdl-divider, #d4d4d8)" strokeWidth={0.5} opacity={0.5} />
|
|
345
390
|
</g>
|
|
346
391
|
))}
|
|
347
392
|
{/* chain link icon (bottom center) */}
|
|
@@ -351,7 +396,8 @@ export function ShapeBlockchainBlockNode({ node, active, lang }: ShapeProps): JS
|
|
|
351
396
|
<rect x={42} y={0} width={22} height={18} rx={3} fill="var(--cdl-tone-accent, #2d6a8f)" fillOpacity={0.5} stroke={frame.stroke} strokeWidth={1.5} />
|
|
352
397
|
</g>
|
|
353
398
|
</g>
|
|
354
|
-
|
|
399
|
+
);
|
|
400
|
+
};
|
|
355
401
|
return <g>{fitArt(r, blockchainBlockExtent(frame.strokeWidth), art)}</g>;
|
|
356
402
|
}
|
|
357
403
|
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import type { JSX } from "react";
|
|
23
23
|
import type { LaidNode, NodeKind } from "../types";
|
|
24
|
+
import { MONO_ADVANCE_RATIO, displayWidth } from "./row-align";
|
|
25
|
+
import { textWidth } from "./text-width";
|
|
24
26
|
|
|
25
27
|
// 既存の呼出 (検査を含む) がここから取っているため、 そのまま出し直す。
|
|
26
28
|
export { textWidth } from "./text-width";
|
|
@@ -450,13 +452,27 @@ export type ArtExtent = { 上: number; 下: number; 左: number; 右: number };
|
|
|
450
452
|
export function fitArt(
|
|
451
453
|
region: { cx: number; cy: number; availW: number; availH: number },
|
|
452
454
|
extent: ArtExtent,
|
|
453
|
-
art: JSX.Element,
|
|
455
|
+
art: JSX.Element | ((倍率: number) => JSX.Element),
|
|
454
456
|
): JSX.Element {
|
|
457
|
+
/*
|
|
458
|
+
* 絵を **倍率を知った上で** 組めるようにする (#871)。
|
|
459
|
+
*
|
|
460
|
+
* 縮める時は絵を丸ごと `scale(k)` で包むため、中の文字も一緒に縮む。 指定 10.5 の字が
|
|
461
|
+
* `k = 0.719` の図で 7.55 になり、読める下限 (画面で 8px) を割っていた。
|
|
462
|
+
*
|
|
463
|
+
* **打ち消せるのは絵の側だけ**。 ここで文字の指定を機械的に `1/k` 倍すると、絵が自分で
|
|
464
|
+
* 計算している「その字が収まる幅」 が指定の大きさのまま残り、字だけが大きくなって
|
|
465
|
+
* 枠から出る (`shape-blockchain-block` は `幅で切る` に指定 11 を渡して切る位置を決めている)。
|
|
466
|
+
*
|
|
467
|
+
* そこで **倍率を絵に渡し、打ち消すかどうかを絵に決めさせる**。 関数を渡さない絵は
|
|
468
|
+
* 今までどおり縮むだけで、1 つも描き方が変わらない。
|
|
469
|
+
*/
|
|
470
|
+
const 組む = (k: number): JSX.Element => (typeof art === "function" ? art(k) : art);
|
|
455
471
|
const 縦 = extent.上 + extent.下;
|
|
456
472
|
const 横 = extent.左 + extent.右;
|
|
457
473
|
// 広がりが無限なら倍率が 0 になるので後段の `s` の防御でも止まる。 ここで先に落とすのは
|
|
458
474
|
// 契約 (有限で正の値だけを通す) を入口で 1 つに揃えるため
|
|
459
|
-
if (!Number.isFinite(縦) || !Number.isFinite(横) || 縦 <= 0 || 横 <= 0) return
|
|
475
|
+
if (!Number.isFinite(縦) || !Number.isFinite(横) || 縦 <= 0 || 横 <= 0) return 組む(1);
|
|
460
476
|
|
|
461
477
|
// **ここは後段の `s` の防御では代替できない**。 無限の軸は倍率の候補が `Infinity` になって
|
|
462
478
|
// `Math.min` から落ちるだけなので、 残りの候補 (上限 1 と反対の軸) で倍率が決まり、 通常の
|
|
@@ -467,11 +483,11 @@ export function fitArt(
|
|
|
467
483
|
region.availH <= 0 ||
|
|
468
484
|
region.availW <= 0
|
|
469
485
|
) {
|
|
470
|
-
return
|
|
486
|
+
return 組む(1);
|
|
471
487
|
}
|
|
472
488
|
|
|
473
489
|
const s = Math.min(1, region.availH / 縦, region.availW / 横);
|
|
474
|
-
if (!Number.isFinite(s) || s <= 0) return
|
|
490
|
+
if (!Number.isFinite(s) || s <= 0) return 組む(1);
|
|
475
491
|
|
|
476
492
|
// 絵の外接矩形の中心を領域の中心に合わせる。 上下 (左右) の広がりが違うので、 中心を
|
|
477
493
|
// そろえないと片側だけはみ出す
|
|
@@ -488,14 +504,104 @@ export function fitArt(
|
|
|
488
504
|
// 食い込む。 「収まるか」 を合計だけで見て位置を見ていなかった。
|
|
489
505
|
//
|
|
490
506
|
// 倍率が 1 で中心も合っている時だけ包まない = 対称な絵に余計な `<g>` を挟まない。
|
|
491
|
-
if (s >= 1 && dx === 0 && dy === 0) return
|
|
507
|
+
if (s >= 1 && dx === 0 && dy === 0) return 組む(1);
|
|
492
508
|
|
|
493
509
|
// **包む時は必ず `translate` と `scale` を両方書く**。 倍率 1 の時に `translate` だけを
|
|
494
510
|
// 書くと変換の形が 2 通りになり、 読む側が両方を扱う必要が出る (実測 = 検査の変換 parser が
|
|
495
511
|
// `translate(0 1)` を読めず 8 件落ちた)。 `scale(1)` は無害なので形を 1 つに保つ
|
|
496
512
|
return (
|
|
497
|
-
|
|
498
|
-
|
|
513
|
+
/*
|
|
514
|
+
* 包みに印を付ける (#886)。 **`scale(` を持つ変換は他にもある** = `event` は目立たせる
|
|
515
|
+
* ための脈動を `scale(0.78)` から `scale(1)` で描き、絵を箱に収める縮小ではない。
|
|
516
|
+
* 祖先の変換を正規表現で見る形だと、その脈動まで「絵の縮小」 として数える。
|
|
517
|
+
*/
|
|
518
|
+
<g data-cdl-art-fit="1" transform={`translate(${dx} ${dy}) scale(${s})`}>
|
|
519
|
+
{組む(s)}
|
|
499
520
|
</g>
|
|
500
521
|
);
|
|
501
522
|
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* 打ち消しを掛ける字 1 つ (#886)。
|
|
526
|
+
*
|
|
527
|
+
* `帯` はその字が座る場所の広さ (絵の座標)。 行なら行の間隔と行の内側の幅、
|
|
528
|
+
* 見出しなら見出しの帯の高さと、見出しが使える幅。
|
|
529
|
+
*
|
|
530
|
+
* **絵が自分の定数から渡す**。 表に書き写すと、絵の寸法を変えた日に片方だけ古くなる
|
|
531
|
+
* (`ArtExtent` と同じ考え方)。
|
|
532
|
+
*/
|
|
533
|
+
export type 打ち消す字 = {
|
|
534
|
+
/** 描く字。 幅の上限を出すのに使う */
|
|
535
|
+
text: string;
|
|
536
|
+
/** 打ち消す前の指定 */
|
|
537
|
+
size: number;
|
|
538
|
+
/** 字間。 渡さない呼び方は 0 */
|
|
539
|
+
letterSpacing?: number;
|
|
540
|
+
/** その字が座る場所の広さ */
|
|
541
|
+
帯: { 幅: number; 高さ: number };
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* 打ち消した字が帯の高さのどこまでを占めてよいか。 超えると隣の行や帯の縁に触れる。
|
|
546
|
+
*
|
|
547
|
+
* **高さにだけ掛ける**。 帯の高さは行の間隔 = 次の行までの距離なので、字がそのまま使うと
|
|
548
|
+
* 上下が触れる。 帯の幅は絵が余白を引いた内側の幅を渡すので、そのまま使ってよい
|
|
549
|
+
* (8 割で止めると、字を切る予算より狭い所で打ち消しが止まって 2 割を捨てる)。
|
|
550
|
+
*/
|
|
551
|
+
const 帯の高さに対する上限 = 0.8;
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* 絵の中の字の幅。 **実物以上に出る側へ倒す**。
|
|
555
|
+
*
|
|
556
|
+
* 等幅の字送りで数えた値と、字形から数えた値の大きい方を採る = 下回ると
|
|
557
|
+
* 「収まっていない物を収まっていると判定」 する向きに外れる (`row-align.ts` の
|
|
558
|
+
* `rowColumnWidth` と同じ考え方)。
|
|
559
|
+
*/
|
|
560
|
+
export function 絵の中の字の幅(text: string, size: number, letterSpacing = 0): number {
|
|
561
|
+
return Math.max(
|
|
562
|
+
displayWidth(text) * size * MONO_ADVANCE_RATIO,
|
|
563
|
+
textWidth(text, size, letterSpacing),
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* 縮んだ分を字の指定で打ち消す倍率 (#871 で連鎖の箱に入れ、#886 で共通化した)。
|
|
569
|
+
*
|
|
570
|
+
* 入れ子の絵は決まった大きさで組んでから `fitArt` が箱に収まる倍率で丸ごと縮めるため、
|
|
571
|
+
* 字も一緒に縮む (実測 = 指定 10.5 が倍率 0.719 の図で 7.55)。 字の指定にこの倍率を掛けると
|
|
572
|
+
* 画面に出る大きさが指定へ戻る。
|
|
573
|
+
*
|
|
574
|
+
* ## 上限は絵の中の帯から出す
|
|
575
|
+
*
|
|
576
|
+
* 固定値にすると、絵の寸法を変えた日に効き方だけが変わる。
|
|
577
|
+
* **幅と高さの両方から出して小さい方を採る** (#886) = 高さだけで止めると、中央揃えの看板の
|
|
578
|
+
* ように幅が字数に比例して伸びる字が横へ出る。
|
|
579
|
+
*
|
|
580
|
+
* ## 帯に収まっていない字では打ち消さない
|
|
581
|
+
*
|
|
582
|
+
* 上限が 1 を下回る字がある絵では 1 を返す (実測 = 判子の中の `印` は半径 8 の丸に収まる
|
|
583
|
+
* 四角の 1 辺 11.3 を指定 10 の 1 字でほぼ使い切っており、幅から出る上限は 1 を下回る)。
|
|
584
|
+
* 打ち消す側に倒すと、その字が確実に入れ物から出る。
|
|
585
|
+
*
|
|
586
|
+
* ## 測れる字が 1 つも無い時も打ち消さない
|
|
587
|
+
*
|
|
588
|
+
* 帯の値が有限の正の値でない絵は、収まるかの判定ができない。
|
|
589
|
+
* `fitArt` が判定できない入力で手を触れないのと同じ向きに倒す。
|
|
590
|
+
*/
|
|
591
|
+
export function 打ち消しの倍率(倍率: number, 字ら: readonly 打ち消す字[]): number {
|
|
592
|
+
const 戻し = Number.isFinite(倍率) && 倍率 > 0 ? 1 / 倍率 : 1;
|
|
593
|
+
let 上限 = Number.POSITIVE_INFINITY;
|
|
594
|
+
for (const 字 of 字ら) {
|
|
595
|
+
if (!(Number.isFinite(字.size) && 字.size > 0)) continue;
|
|
596
|
+
const { 幅, 高さ } = 字.帯;
|
|
597
|
+
if (Number.isFinite(高さ) && 高さ > 0) {
|
|
598
|
+
上限 = Math.min(上限, (高さ * 帯の高さに対する上限) / 字.size);
|
|
599
|
+
}
|
|
600
|
+
const 実幅 = 絵の中の字の幅(字.text, 字.size, 字.letterSpacing ?? 0);
|
|
601
|
+
if (Number.isFinite(幅) && 幅 > 0 && 実幅 > 0) {
|
|
602
|
+
上限 = Math.min(上限, 幅 / 実幅);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
if (!Number.isFinite(上限)) return 1;
|
|
606
|
+
return Math.max(1, Math.min(戻し, 上限));
|
|
607
|
+
}
|