@cardenelabs/cdl 0.13.0 → 0.13.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardenelabs/cdl",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
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,4 +1,5 @@
1
1
  import { textWidth } from "./text-width";
2
+ import { MIN_TEXT_SIZE } from "./compact-title";
2
3
 
3
4
  /**
4
5
  * 箱の中に文字の行を積む (#471)。
@@ -84,6 +85,40 @@ export function 幅で切る(
84
85
  return `${出}${省略}`;
85
86
  }
86
87
 
88
+ /**
89
+ * 字を小さくする下限。
90
+ *
91
+ * `compact-title.ts` の `MIN_TEXT_SIZE` を共有する。 小型の箱の名前が既にこの値まで縮んでおり、
92
+ * 読める下限として使われている。 同じ値を参照すれば、同じ図の中で名前の下限が場所によって
93
+ * 変わらない。
94
+ */
95
+ export const 縮めの下限 = MIN_TEXT_SIZE;
96
+
97
+ /**
98
+ * 幅に入るまで字を小さくし、下限まで縮めても入らなければ切る (#534)。
99
+ *
100
+ * **切るのは最後の手段にする**。 字の大きさを保ったまま切ると、全角 7 文字の名前
101
+ * (`新プロジェクト`) が `新プロジェ…` になって何の箱か読めない。 1 段小さくすれば
102
+ * 入る名前を切っていた。
103
+ *
104
+ * 縮めるのは **入るところまで** で、一律に下限へ落とさない。 落とすと 1 段で足りる名前まで
105
+ * 最小になり、同じ図の中で名前の大きさがばらつく。
106
+ *
107
+ * 元から下限以下の字は縮めない (補足の 9px がここに当たる)。 巻き込むと補足が読めなくなる。
108
+ *
109
+ * 箱の大きさは変えない。 高さの都合は呼出側 (`箱に積む`) が見る。
110
+ */
111
+ export function 幅に収める(
112
+ text: string,
113
+ fontSize: number,
114
+ 使える幅: number,
115
+ 測る: (s: string, size: number) => number = textWidth,
116
+ ): { text: string; fontSize: number } {
117
+ let size = fontSize;
118
+ while (size > 縮めの下限 && 測る(text, size) > 使える幅) size -= 1;
119
+ return { text: 幅で切る(text, size, 使える幅, 測る), fontSize: size };
120
+ }
121
+
87
122
  /**
88
123
  * 見た目の 1 字 (書記素クラスタ) に分ける。
89
124
  *
@@ -115,16 +150,22 @@ export function 箱に積む(行: 積む行[], 箱の幅: number, 箱の高さ:
115
150
  const 使える高さ = 箱の高さ - 上下の余白 * 2;
116
151
  const 高さ = (rs: 積む行[]): number => rs.reduce((a, r) => a + r.fontSize * 行送りの比, 0);
117
152
 
118
- // 入るまで後ろから落とす。 先頭は落とさない
153
+ // 入るまで後ろから落とす。 先頭は落とさない。
154
+ // **判定は縮める前の大きさで行う** (#534)。 縮めた後で決めると、名前が長い箱でだけ
155
+ // 補足が復活する = 同じ高さの箱でも中身の長さで行数が変わり、挙動が読みにくくなる
119
156
  const 残す = [...候補];
120
157
  while (残す.length > 1 && 高さ(残す) > 使える高さ) 残す.pop();
121
158
 
122
159
  const 幅 = 使える幅(箱の幅);
123
- const 総高 = 高さ(残す);
160
+ // **幅に収めるのを先に済ませる** (#534)。 縮めると行の高さも変わるため、後から縮めると
161
+ // `dy` が縮める前の大きさで出て、行の塊が箱の上下の中央からずれる
162
+ const 収めた = 残す.map((r) => ({ ...r, ...幅に収める(r.text, r.fontSize, 幅) }));
163
+
164
+ const 総高 = 高さ(収めた);
124
165
  let 積み = -総高 / 2;
125
- return 残す.map((r) => {
166
+ return 収めた.map((r) => {
126
167
  const dy = 積み + ((行送りの比 + 字の高さの比) / 2) * r.fontSize;
127
168
  積み += r.fontSize * 行送りの比;
128
- return { ...r, text: 幅で切る(r.text, r.fontSize, 幅), dy };
169
+ return { ...r, dy };
129
170
  });
130
171
  }
@@ -54,8 +54,8 @@ export const COMPACT_MAX_H = 100;
54
54
  /** 小型で名前の左右に空ける幅の合計。 `card` の compact と同じ。 */
55
55
  const COMPACT_PADDING_X = 16;
56
56
 
57
- /** 小型で名前を縮める下限。 `card` と同じ。 */
58
- const MIN_SIZE = 11;
57
+ /** 小型で名前を縮める共通の下限。 `card` tree / mind-map で同じ値を使う。 */
58
+ export const MIN_TEXT_SIZE = 11;
59
59
 
60
60
  /**
61
61
  * 字が baseline から上下へどれだけ張り出すかの見積り。
@@ -69,10 +69,10 @@ const DESCENT = 0.25;
69
69
  /**
70
70
  * 名前が収まる最小の帯の高さ。 これより低い帯では **収めきれない**。
71
71
  *
72
- * 読める下限 (`MIN_SIZE`) を割ってまで縮めない。 その場合の張り出しは上下へ均等に出す =
72
+ * 読める下限 (`MIN_TEXT_SIZE`) を割ってまで縮めない。 その場合の張り出しは上下へ均等に出す =
73
73
  * 下だけに落ちる形にはしない。
74
74
  */
75
- export const MIN_BAND_H = MIN_SIZE * (ASCENT + DESCENT);
75
+ export const MIN_BAND_H = MIN_TEXT_SIZE * (ASCENT + DESCENT);
76
76
 
77
77
  /** 目印 (`eyebrow`) の baseline と大きさ。 4 種で同じ値を使っている。 */
78
78
  const EYEBROW_BASELINE = 38;
@@ -144,10 +144,10 @@ export function titlePlacement(
144
144
  node.title,
145
145
  Math.max(0, node.w - COMPACT_PADDING_X),
146
146
  通常.size,
147
- MIN_SIZE,
147
+ MIN_TEXT_SIZE,
148
148
  );
149
149
  const 帯で = Math.floor(帯 / (ASCENT + DESCENT));
150
- const size = Math.max(MIN_SIZE, Math.min(幅で, 帯で));
150
+ const size = Math.max(MIN_TEXT_SIZE, Math.min(幅で, 帯で));
151
151
  const 字高 = size * (ASCENT + DESCENT);
152
152
 
153
153
  // 収まるなら帯の中央。 収まらない時の逃がし方は目印の有無で変える
@@ -1,5 +1,5 @@
1
1
  import type { JSX } from "react";
2
- import { 使える幅, 幅で切る, 箱に積む } from "./box-lines";
2
+ import { 使える幅, 幅に収める, 箱に積む } from "./box-lines";
3
3
  import type { LaidNode, Tone, MindBranchNode, MindBranchPayload } from "../types";
4
4
  import { TONE } from "../render/tone";
5
5
  import { resolveMindData } from "../render/payload-binding";
@@ -115,6 +115,10 @@ export function MindMapNode({
115
115
  );
116
116
  // 判定は行数でなく「補足を渡したか」 で行う (理由は tree.tsx と同じ、 #471 Round 1)
117
117
  const 補足あり = (n.subtitle ?? "").trim() !== "";
118
+ // 補足ありでは `箱に積む` が既に同じ処理を済ませているため、1 行の経路だけ計算する
119
+ const 収めた名前 = 補足あり
120
+ ? undefined
121
+ : 幅に収める(n.title, n.isRoot ? 15 : 13, 使える幅(n.w));
118
122
  const 文字の色 = n.isRoot
119
123
  ? "var(--cdl-text-on-tone, #ffffff)"
120
124
  : "var(--cdl-chip-text, #1a1f2a)";
@@ -130,7 +134,7 @@ export function MindMapNode({
130
134
  stroke={n.isRoot ? toneColor(n.tone ?? "accent") : toneColor(n.tone)}
131
135
  strokeWidth={n.isRoot ? 2 : 1.75}
132
136
  />
133
- {補足あり ? (
137
+ {収めた名前 === undefined ? (
134
138
  行.map((r) => (
135
139
  <text
136
140
  key={r.役割}
@@ -149,16 +153,17 @@ export function MindMapNode({
149
153
  ) : (
150
154
  // 補足が無くても **幅に収める** (#526)。 `箱に積む` を通らないこの経路だけが
151
155
  // 幅を見ておらず、長い名前がそのまま箱の外へ出て枝の線と重なっていた
152
- // (実測 = 中心の箱で文字 163px に対し箱 120px)
156
+ // (実測 = 中心の箱で文字 163px に対し箱 120px)
157
+ // 収める手段は縮めるのが先で、切るのは下限まで縮めても入らない時だけ (#534)
153
158
  <text
154
159
  x={n.x}
155
160
  y={n.y + 5}
156
- fontSize={n.isRoot ? 15 : 13}
161
+ fontSize={収めた名前.fontSize}
157
162
  fontWeight={n.isRoot ? 700 : 600}
158
163
  textAnchor="middle"
159
164
  fill={文字の色}
160
165
  >
161
- {幅で切る(n.title, n.isRoot ? 15 : 13, 使える幅(n.w))}
166
+ {収めた名前.text}
162
167
  </text>
163
168
  )}
164
169
  </g>
@@ -2,7 +2,7 @@ import type { JSX } from "react";
2
2
  import type { LaidNode, TreeNodePayload } from "../types";
3
3
  import { TONE } from "../render/tone";
4
4
  import { resolveTreeData } from "../render/payload-binding";
5
- import { 使える幅, 幅で切る, 箱に積む, type 積んだ行 } from "../kinds/box-lines";
5
+ import { 使える幅, 幅に収める, 箱に積む, type 積んだ行 } from "../kinds/box-lines";
6
6
  import { 進みを畳む, 伸ばすdash, 段の伸び } from "./draw-progress";
7
7
 
8
8
  /**
@@ -164,6 +164,11 @@ export function TreeNode({
164
164
  // **判定は行数でなく「補足を渡したか」 で行う**。 行数で見ると、 箱が低くて補足が落ちた時に
165
165
  // 従来の 1 行の経路へ戻り、 幅で切っていない名前が箱から横にはみ出す (#471 Round 1)
166
166
  const 補足あり = (n.subtitle ?? "").trim() !== "";
167
+ // 補足ありでは `箱に積む` が既に同じ処理を済ませているため、1 行の経路だけ計算する。
168
+ // 収める手段は縮めるのが先で、切るのは下限まで縮めても入らない時だけ (#534)
169
+ const 収めた名前 = 補足あり
170
+ ? undefined
171
+ : 幅に収める(n.title, isRoot ? 13 : 12, 使える幅(n.w));
167
172
  return (
168
173
  <g
169
174
  key={`n-${n.id}`}
@@ -181,7 +186,7 @@ export function TreeNode({
181
186
  rx={12}
182
187
  fill={`url(#${gradientId})`}
183
188
  />
184
- {補足あり ? (
189
+ {収めた名前 === undefined ? (
185
190
  箱の文字({
186
191
  行,
187
192
  x: n.w / 2,
@@ -195,12 +200,12 @@ export function TreeNode({
195
200
  <text
196
201
  x={n.w / 2}
197
202
  y={n.h / 2 + 5}
198
- fontSize={13}
203
+ fontSize={収めた名前.fontSize}
199
204
  fontWeight={700}
200
205
  textAnchor="middle"
201
206
  fill="var(--cdl-text-on-tone, #ffffff)"
202
207
  >
203
- {幅で切る(n.title, 13, 使える幅(n.w))}
208
+ {収めた名前.text}
204
209
  </text>
205
210
  )}
206
211
  </>
@@ -218,7 +223,7 @@ export function TreeNode({
218
223
  strokeWidth={1.5}
219
224
  />
220
225
  <rect x={0} y={0} width={4} height={n.h} rx={2} fill={accent} />
221
- {補足あり ? (
226
+ {収めた名前 === undefined ? (
222
227
  箱の文字({
223
228
  行,
224
229
  x: n.w / 2 + 2,
@@ -230,12 +235,12 @@ export function TreeNode({
230
235
  <text
231
236
  x={n.w / 2 + 2}
232
237
  y={n.h / 2 + 4}
233
- fontSize={12}
238
+ fontSize={収めた名前.fontSize}
234
239
  fontWeight={600}
235
240
  textAnchor="middle"
236
241
  fill="var(--cdl-chip-text, #1a1f2a)"
237
242
  >
238
- {幅で切る(n.title, 12, 使える幅(n.w))}
243
+ {収めた名前.text}
239
244
  </text>
240
245
  )}
241
246
  </>