@opendata-ai/openchart-core 8.0.0-rc.8 → 8.0.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/dist/index.d.ts +461 -56
- package/dist/index.js +62 -24
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +1 -2
- package/schema/chart.schema.json +373 -67
- package/schema/table.schema.json +35 -1
- package/schema/vizspec.schema.json +950 -152
- package/src/accessibility/__tests__/alt-text.test.ts +0 -110
- package/src/accessibility/__tests__/aria.test.ts +0 -125
- package/src/accessibility/alt-text.ts +0 -155
- package/src/accessibility/aria.ts +0 -75
- package/src/accessibility/index.ts +0 -6
- package/src/colors/__tests__/colorblind.test.ts +0 -75
- package/src/colors/__tests__/contrast.test.ts +0 -119
- package/src/colors/__tests__/palettes.test.ts +0 -74
- package/src/colors/colorblind.ts +0 -131
- package/src/colors/contrast.ts +0 -143
- package/src/colors/index.ts +0 -36
- package/src/colors/palettes.ts +0 -206
- package/src/helpers/__tests__/deep-merge.test.ts +0 -54
- package/src/helpers/__tests__/spec-builders.test.ts +0 -338
- package/src/helpers/deep-merge.ts +0 -29
- package/src/helpers/spec-builders.ts +0 -479
- package/src/index.ts +0 -215
- package/src/labels/__tests__/collision.test.ts +0 -310
- package/src/labels/collision.ts +0 -249
- package/src/labels/index.ts +0 -13
- package/src/layout/__tests__/chrome.test.ts +0 -208
- package/src/layout/__tests__/text-measure.test.ts +0 -57
- package/src/layout/chrome.ts +0 -470
- package/src/layout/index.ts +0 -18
- package/src/layout/text-measure.ts +0 -404
- package/src/layout/text-wrap.ts +0 -93
- package/src/locale/__tests__/format.test.ts +0 -606
- package/src/locale/format.ts +0 -459
- package/src/locale/index.ts +0 -21
- package/src/responsive/__tests__/breakpoints.test.ts +0 -146
- package/src/responsive/__tests__/metrics.test.ts +0 -52
- package/src/responsive/breakpoints.ts +0 -172
- package/src/responsive/index.ts +0 -53
- package/src/responsive/metrics.ts +0 -299
- package/src/schema/__tests__/generated-artifacts.test.ts +0 -42
- package/src/schema/__tests__/schema.test.ts +0 -179
- package/src/styles/__tests__/reduced-motion-selectors.test.ts +0 -28
- package/src/styles/animation.css +0 -208
- package/src/styles/base.css +0 -102
- package/src/styles/chrome.css +0 -174
- package/src/styles/dark.css +0 -44
- package/src/styles/graph.css +0 -135
- package/src/styles/index.css +0 -28
- package/src/styles/keyframes.css +0 -151
- package/src/styles/legend.css +0 -18
- package/src/styles/reduced-motion.css +0 -67
- package/src/styles/series-search.css +0 -153
- package/src/styles/sparkline.css +0 -16
- package/src/styles/table-animation.css +0 -93
- package/src/styles/table.css +0 -398
- package/src/styles/token-definitions.ts +0 -199
- package/src/styles/tokens.css +0 -156
- package/src/styles/tooltip.css +0 -126
- package/src/styles/you-draw-it.css +0 -126
- package/src/theme/__tests__/dark-mode.test.ts +0 -232
- package/src/theme/__tests__/defaults.test.ts +0 -73
- package/src/theme/__tests__/resolve.test.ts +0 -277
- package/src/theme/dark-mode.ts +0 -258
- package/src/theme/defaults.ts +0 -110
- package/src/theme/index.ts +0 -8
- package/src/theme/presets.ts +0 -64
- package/src/theme/resolve.ts +0 -312
- package/src/types/__tests__/encoding.test.ts +0 -368
- package/src/types/__tests__/gradient.test.ts +0 -95
- package/src/types/__tests__/spec.test.ts +0 -499
- package/src/types/encoding.ts +0 -280
- package/src/types/events.ts +0 -154
- package/src/types/index.ts +0 -289
- package/src/types/layout.ts +0 -2001
- package/src/types/schema-roots.ts +0 -32
- package/src/types/spec.ts +0 -2906
- package/src/types/table.ts +0 -108
- package/src/types/theme.ts +0 -209
|
@@ -1,404 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Text measurement for environments without a DOM.
|
|
3
|
-
*
|
|
4
|
-
* Uses per-character advance-width tables measured from Inter (the default
|
|
5
|
-
* chart font) for ~2-3% accuracy on Latin text at weights 400/700.
|
|
6
|
-
* Other fonts set via theme.fonts.family still get a better-than-flat-ratio
|
|
7
|
-
* approximation but no accuracy guarantee.
|
|
8
|
-
*
|
|
9
|
-
* Tables generated by scripts/generate-char-widths.mjs — regenerate, don't
|
|
10
|
-
* hand-edit.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import type { MeasureTextFn } from '../types/layout';
|
|
14
|
-
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
// Per-character advance widths for Inter, normalized to fontSize = 1.
|
|
17
|
-
// Generated by scripts/generate-char-widths.mjs from Chromium canvas metrics.
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
|
|
20
|
-
const CHAR_WIDTHS_400: Record<string, number> = {
|
|
21
|
-
' ': 0.278,
|
|
22
|
-
'!': 0.278,
|
|
23
|
-
'"': 0.355,
|
|
24
|
-
'#': 0.556,
|
|
25
|
-
$: 0.556,
|
|
26
|
-
'%': 0.889,
|
|
27
|
-
'&': 0.667,
|
|
28
|
-
"'": 0.191,
|
|
29
|
-
'(': 0.333,
|
|
30
|
-
')': 0.333,
|
|
31
|
-
'*': 0.389,
|
|
32
|
-
'+': 0.584,
|
|
33
|
-
',': 0.278,
|
|
34
|
-
'-': 0.333,
|
|
35
|
-
'.': 0.278,
|
|
36
|
-
'/': 0.278,
|
|
37
|
-
'0': 0.556,
|
|
38
|
-
'1': 0.556,
|
|
39
|
-
'2': 0.556,
|
|
40
|
-
'3': 0.556,
|
|
41
|
-
'4': 0.556,
|
|
42
|
-
'5': 0.556,
|
|
43
|
-
'6': 0.556,
|
|
44
|
-
'7': 0.556,
|
|
45
|
-
'8': 0.556,
|
|
46
|
-
'9': 0.556,
|
|
47
|
-
':': 0.278,
|
|
48
|
-
';': 0.278,
|
|
49
|
-
'<': 0.584,
|
|
50
|
-
'=': 0.584,
|
|
51
|
-
'>': 0.584,
|
|
52
|
-
'?': 0.556,
|
|
53
|
-
'@': 1.015,
|
|
54
|
-
A: 0.667,
|
|
55
|
-
B: 0.667,
|
|
56
|
-
C: 0.722,
|
|
57
|
-
D: 0.722,
|
|
58
|
-
E: 0.667,
|
|
59
|
-
F: 0.611,
|
|
60
|
-
G: 0.778,
|
|
61
|
-
H: 0.722,
|
|
62
|
-
I: 0.278,
|
|
63
|
-
J: 0.5,
|
|
64
|
-
K: 0.667,
|
|
65
|
-
L: 0.556,
|
|
66
|
-
M: 0.833,
|
|
67
|
-
N: 0.722,
|
|
68
|
-
O: 0.778,
|
|
69
|
-
P: 0.667,
|
|
70
|
-
Q: 0.778,
|
|
71
|
-
R: 0.722,
|
|
72
|
-
S: 0.667,
|
|
73
|
-
T: 0.611,
|
|
74
|
-
U: 0.722,
|
|
75
|
-
V: 0.667,
|
|
76
|
-
W: 0.944,
|
|
77
|
-
X: 0.667,
|
|
78
|
-
Y: 0.667,
|
|
79
|
-
Z: 0.611,
|
|
80
|
-
'[': 0.278,
|
|
81
|
-
'\\': 0.278,
|
|
82
|
-
']': 0.278,
|
|
83
|
-
'^': 0.469,
|
|
84
|
-
_: 0.556,
|
|
85
|
-
'`': 0.333,
|
|
86
|
-
a: 0.556,
|
|
87
|
-
b: 0.556,
|
|
88
|
-
c: 0.5,
|
|
89
|
-
d: 0.556,
|
|
90
|
-
e: 0.556,
|
|
91
|
-
f: 0.278,
|
|
92
|
-
g: 0.556,
|
|
93
|
-
h: 0.556,
|
|
94
|
-
i: 0.222,
|
|
95
|
-
j: 0.222,
|
|
96
|
-
k: 0.5,
|
|
97
|
-
l: 0.222,
|
|
98
|
-
m: 0.833,
|
|
99
|
-
n: 0.556,
|
|
100
|
-
o: 0.556,
|
|
101
|
-
p: 0.556,
|
|
102
|
-
q: 0.556,
|
|
103
|
-
r: 0.333,
|
|
104
|
-
s: 0.5,
|
|
105
|
-
t: 0.278,
|
|
106
|
-
u: 0.556,
|
|
107
|
-
v: 0.5,
|
|
108
|
-
w: 0.722,
|
|
109
|
-
x: 0.5,
|
|
110
|
-
y: 0.5,
|
|
111
|
-
z: 0.5,
|
|
112
|
-
'{': 0.334,
|
|
113
|
-
'|': 0.26,
|
|
114
|
-
'}': 0.334,
|
|
115
|
-
'~': 0.584,
|
|
116
|
-
'₹': 0.52,
|
|
117
|
-
'₩': 1.022,
|
|
118
|
-
'¥': 0.556,
|
|
119
|
-
'¥': 1,
|
|
120
|
-
'€': 0.744,
|
|
121
|
-
'£': 0.556,
|
|
122
|
-
'¢': 0.556,
|
|
123
|
-
'§': 0.556,
|
|
124
|
-
'©': 0.737,
|
|
125
|
-
'®': 0.737,
|
|
126
|
-
'™': 1,
|
|
127
|
-
'°': 0.4,
|
|
128
|
-
'–': 0.556,
|
|
129
|
-
'—': 1,
|
|
130
|
-
'‘': 0.222,
|
|
131
|
-
'’': 0.222,
|
|
132
|
-
'“': 0.333,
|
|
133
|
-
'”': 0.333,
|
|
134
|
-
'…': 1,
|
|
135
|
-
'±': 0.549,
|
|
136
|
-
'×': 0.584,
|
|
137
|
-
'÷': 0.549,
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
const AVG_CHAR_WIDTH_400 = 0.53;
|
|
141
|
-
|
|
142
|
-
const CHAR_WIDTHS_700: Record<string, number> = {
|
|
143
|
-
' ': 0.278,
|
|
144
|
-
'!': 0.333,
|
|
145
|
-
'"': 0.474,
|
|
146
|
-
'#': 0.556,
|
|
147
|
-
$: 0.556,
|
|
148
|
-
'%': 0.889,
|
|
149
|
-
'&': 0.722,
|
|
150
|
-
"'": 0.238,
|
|
151
|
-
'(': 0.333,
|
|
152
|
-
')': 0.333,
|
|
153
|
-
'*': 0.389,
|
|
154
|
-
'+': 0.584,
|
|
155
|
-
',': 0.278,
|
|
156
|
-
'-': 0.333,
|
|
157
|
-
'.': 0.278,
|
|
158
|
-
'/': 0.278,
|
|
159
|
-
'0': 0.556,
|
|
160
|
-
'1': 0.556,
|
|
161
|
-
'2': 0.556,
|
|
162
|
-
'3': 0.556,
|
|
163
|
-
'4': 0.556,
|
|
164
|
-
'5': 0.556,
|
|
165
|
-
'6': 0.556,
|
|
166
|
-
'7': 0.556,
|
|
167
|
-
'8': 0.556,
|
|
168
|
-
'9': 0.556,
|
|
169
|
-
':': 0.333,
|
|
170
|
-
';': 0.333,
|
|
171
|
-
'<': 0.584,
|
|
172
|
-
'=': 0.584,
|
|
173
|
-
'>': 0.584,
|
|
174
|
-
'?': 0.611,
|
|
175
|
-
'@': 0.975,
|
|
176
|
-
A: 0.722,
|
|
177
|
-
B: 0.722,
|
|
178
|
-
C: 0.722,
|
|
179
|
-
D: 0.722,
|
|
180
|
-
E: 0.667,
|
|
181
|
-
F: 0.611,
|
|
182
|
-
G: 0.778,
|
|
183
|
-
H: 0.722,
|
|
184
|
-
I: 0.278,
|
|
185
|
-
J: 0.556,
|
|
186
|
-
K: 0.722,
|
|
187
|
-
L: 0.611,
|
|
188
|
-
M: 0.833,
|
|
189
|
-
N: 0.722,
|
|
190
|
-
O: 0.778,
|
|
191
|
-
P: 0.667,
|
|
192
|
-
Q: 0.778,
|
|
193
|
-
R: 0.722,
|
|
194
|
-
S: 0.667,
|
|
195
|
-
T: 0.611,
|
|
196
|
-
U: 0.722,
|
|
197
|
-
V: 0.667,
|
|
198
|
-
W: 0.944,
|
|
199
|
-
X: 0.667,
|
|
200
|
-
Y: 0.667,
|
|
201
|
-
Z: 0.611,
|
|
202
|
-
'[': 0.333,
|
|
203
|
-
'\\': 0.278,
|
|
204
|
-
']': 0.333,
|
|
205
|
-
'^': 0.584,
|
|
206
|
-
_: 0.556,
|
|
207
|
-
'`': 0.333,
|
|
208
|
-
a: 0.556,
|
|
209
|
-
b: 0.611,
|
|
210
|
-
c: 0.556,
|
|
211
|
-
d: 0.611,
|
|
212
|
-
e: 0.556,
|
|
213
|
-
f: 0.333,
|
|
214
|
-
g: 0.611,
|
|
215
|
-
h: 0.611,
|
|
216
|
-
i: 0.278,
|
|
217
|
-
j: 0.278,
|
|
218
|
-
k: 0.556,
|
|
219
|
-
l: 0.278,
|
|
220
|
-
m: 0.889,
|
|
221
|
-
n: 0.611,
|
|
222
|
-
o: 0.611,
|
|
223
|
-
p: 0.611,
|
|
224
|
-
q: 0.611,
|
|
225
|
-
r: 0.389,
|
|
226
|
-
s: 0.556,
|
|
227
|
-
t: 0.333,
|
|
228
|
-
u: 0.611,
|
|
229
|
-
v: 0.556,
|
|
230
|
-
w: 0.778,
|
|
231
|
-
x: 0.556,
|
|
232
|
-
y: 0.556,
|
|
233
|
-
z: 0.5,
|
|
234
|
-
'{': 0.389,
|
|
235
|
-
'|': 0.28,
|
|
236
|
-
'}': 0.389,
|
|
237
|
-
'~': 0.584,
|
|
238
|
-
'₹': 0.553,
|
|
239
|
-
'₩': 1.024,
|
|
240
|
-
'¥': 0.556,
|
|
241
|
-
'¥': 1,
|
|
242
|
-
'€': 0.744,
|
|
243
|
-
'£': 0.556,
|
|
244
|
-
'¢': 0.556,
|
|
245
|
-
'§': 0.556,
|
|
246
|
-
'©': 0.737,
|
|
247
|
-
'®': 0.737,
|
|
248
|
-
'™': 1,
|
|
249
|
-
'°': 0.4,
|
|
250
|
-
'–': 0.556,
|
|
251
|
-
'—': 1,
|
|
252
|
-
'‘': 0.278,
|
|
253
|
-
'’': 0.278,
|
|
254
|
-
'“': 0.5,
|
|
255
|
-
'”': 0.5,
|
|
256
|
-
'…': 1,
|
|
257
|
-
'±': 0.549,
|
|
258
|
-
'×': 0.584,
|
|
259
|
-
'÷': 0.549,
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
const AVG_CHAR_WIDTH_700 = 0.554;
|
|
263
|
-
|
|
264
|
-
// Weights < 650 scale the CHAR_WIDTHS_400 table; weights >= 650 scale
|
|
265
|
-
// CHAR_WIDTHS_700. The factor is 1.0 at each table's native weight.
|
|
266
|
-
const WEIGHT_ADJUSTMENT: Record<number, number> = {
|
|
267
|
-
100: 0.9,
|
|
268
|
-
200: 0.92,
|
|
269
|
-
300: 0.95,
|
|
270
|
-
400: 1.0,
|
|
271
|
-
500: 1.02,
|
|
272
|
-
600: 1.05,
|
|
273
|
-
700: 1.0, // uses CHAR_WIDTHS_700 directly
|
|
274
|
-
800: 1.1,
|
|
275
|
-
900: 1.12,
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
function resolveTable(fontWeight: number): {
|
|
279
|
-
table: Record<string, number>;
|
|
280
|
-
avg: number;
|
|
281
|
-
scale: number;
|
|
282
|
-
} {
|
|
283
|
-
if (fontWeight >= 650) {
|
|
284
|
-
return {
|
|
285
|
-
table: CHAR_WIDTHS_700,
|
|
286
|
-
avg: AVG_CHAR_WIDTH_700,
|
|
287
|
-
scale: WEIGHT_ADJUSTMENT[fontWeight] ?? 1.0,
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
return {
|
|
291
|
-
table: CHAR_WIDTHS_400,
|
|
292
|
-
avg: AVG_CHAR_WIDTH_400,
|
|
293
|
-
scale: WEIGHT_ADJUSTMENT[fontWeight] ?? 1.0,
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
function charWidthRatio(ch: string, table: Record<string, number>, avg: number): number {
|
|
298
|
-
const direct = table[ch];
|
|
299
|
-
if (direct !== undefined) return direct;
|
|
300
|
-
const code = ch.codePointAt(0) ?? 0;
|
|
301
|
-
// Combining marks (diacritics) have zero advance width
|
|
302
|
-
if ((code >= 0x0300 && code <= 0x036f) || (code >= 0x1ab0 && code <= 0x1aff)) return 0;
|
|
303
|
-
// CJK ideographs, kana, hangul, and fullwidth forms are full-em
|
|
304
|
-
if (
|
|
305
|
-
(code >= 0x1100 && code <= 0x11ff) ||
|
|
306
|
-
(code >= 0x2e80 && code <= 0x9fff) ||
|
|
307
|
-
(code >= 0xac00 && code <= 0xd7af) ||
|
|
308
|
-
(code >= 0xf900 && code <= 0xfaff) ||
|
|
309
|
-
(code >= 0xff00 && code <= 0xff60)
|
|
310
|
-
)
|
|
311
|
-
return 1.0;
|
|
312
|
-
return avg;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Estimate the average character width for a given font size and weight.
|
|
317
|
-
* Used by both text width estimation and word-wrap line counting.
|
|
318
|
-
*/
|
|
319
|
-
export function estimateCharWidth(fontSize: number, fontWeight = 400): number {
|
|
320
|
-
const { avg, scale } = resolveTable(fontWeight);
|
|
321
|
-
return fontSize * avg * scale;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Estimate the rendered width of a text string.
|
|
326
|
-
*
|
|
327
|
-
* Uses per-character advance widths measured from Inter (the default chart
|
|
328
|
-
* font). Accurate to ~2-3% for Latin text at weights 400/700; other weights
|
|
329
|
-
* are interpolated from the nearest table.
|
|
330
|
-
*/
|
|
331
|
-
export function estimateTextWidth(text: string, fontSize: number, fontWeight = 400): number {
|
|
332
|
-
const { table, avg, scale } = resolveTable(fontWeight);
|
|
333
|
-
|
|
334
|
-
const measureLine = (line: string): number => {
|
|
335
|
-
let sum = 0;
|
|
336
|
-
for (const ch of line) {
|
|
337
|
-
sum += charWidthRatio(ch, table, avg);
|
|
338
|
-
}
|
|
339
|
-
return sum * fontSize * scale;
|
|
340
|
-
};
|
|
341
|
-
|
|
342
|
-
if (text.includes('\n')) {
|
|
343
|
-
let maxWidth = 0;
|
|
344
|
-
for (const line of text.split('\n')) {
|
|
345
|
-
const w = measureLine(line);
|
|
346
|
-
if (w > maxWidth) maxWidth = w;
|
|
347
|
-
}
|
|
348
|
-
return maxWidth;
|
|
349
|
-
}
|
|
350
|
-
return measureLine(text);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* Width reserved for the "tryOpenData.ai" brand watermark in the bottom-right corner.
|
|
355
|
-
*/
|
|
356
|
-
export const BRAND_RESERVE_WIDTH = 130;
|
|
357
|
-
|
|
358
|
-
/** Font size of the brand watermark (px). Shared between layout and renderer. */
|
|
359
|
-
export const BRAND_FONT_SIZE = 12;
|
|
360
|
-
|
|
361
|
-
/** Minimum chart width to render the brand watermark (px). */
|
|
362
|
-
export const BRAND_MIN_WIDTH = 120;
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Width threshold below which layout tightens spacing (legend gaps, chrome
|
|
366
|
-
* padding, label gaps) to maximize data area on small screens.
|
|
367
|
-
*/
|
|
368
|
-
export const COMPACT_WIDTH = 420;
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* Estimate the rendered height of a text block.
|
|
372
|
-
*/
|
|
373
|
-
export function estimateTextHeight(fontSize: number, lineCount = 1, lineHeight = 1.3): number {
|
|
374
|
-
return fontSize * lineHeight * lineCount;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* Distance from the top of a text box to its alphabetic baseline.
|
|
379
|
-
*
|
|
380
|
-
* Layout stores text positions as top-edge coordinates; renderers convert to
|
|
381
|
-
* baseline coordinates with this ratio instead of `dominant-baseline: hanging`.
|
|
382
|
-
* WebKit computes the hanging baseline from different font metrics than Blink
|
|
383
|
-
* and never inherits dominant-baseline into tspans (WebKit bugs 139258 /
|
|
384
|
-
* 297455), which shifted whole titles above the container on iOS Safari.
|
|
385
|
-
* The 0.8 ratio matches Blink's rendered hanging position for typical UI
|
|
386
|
-
* fonts (hanging sits at ~0.8 x ascent, Inter ascent ~= 0.97em) and the
|
|
387
|
-
* Highcharts fontMetrics convention.
|
|
388
|
-
*/
|
|
389
|
-
export function textAscent(fontSize: number): number {
|
|
390
|
-
return fontSize * 0.8;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// ---------------------------------------------------------------------------
|
|
394
|
-
// Measurer resolver — bridges heuristic ↔ injected canvas measurer
|
|
395
|
-
// ---------------------------------------------------------------------------
|
|
396
|
-
|
|
397
|
-
export const heuristicMeasure: MeasureTextFn = (text, fontSize, fontWeight) => ({
|
|
398
|
-
width: estimateTextWidth(text, fontSize, fontWeight),
|
|
399
|
-
height: estimateTextHeight(fontSize),
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
export function resolveMeasurer(measureText?: MeasureTextFn): MeasureTextFn {
|
|
403
|
-
return measureText ?? heuristicMeasure;
|
|
404
|
-
}
|
package/src/layout/text-wrap.ts
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Word-wrap a text string into lines that fit within a max width.
|
|
3
|
-
*
|
|
4
|
-
* Shared by the SVG chart renderer and the sankey renderer. When a
|
|
5
|
-
* `measureText` callback is provided, uses real DOM measurement for
|
|
6
|
-
* accurate wrapping. Otherwise falls back to a character-width
|
|
7
|
-
* heuristic driven by `estimateCharWidth` from text-measure.ts.
|
|
8
|
-
*
|
|
9
|
-
* Non-browser callers (SSR, tests) omit the measureText argument to
|
|
10
|
-
* use the heuristic fallback. Do not change the signature without
|
|
11
|
-
* re-verifying visual baselines for every caller.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import type { MeasureTextFn } from '../types/layout';
|
|
15
|
-
import { estimateCharWidth } from './text-measure';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Break text into lines that fit within maxWidth using word wrapping.
|
|
19
|
-
*
|
|
20
|
-
* Splits on explicit newlines first, then word-wraps each segment.
|
|
21
|
-
* Preserves empty segments so consecutive newlines produce blank lines.
|
|
22
|
-
*
|
|
23
|
-
* @param text - The text to wrap. May contain `\n` for forced breaks.
|
|
24
|
-
* @param fontSize - Font size in pixels.
|
|
25
|
-
* @param fontWeight - Font weight (100-900).
|
|
26
|
-
* @param maxWidth - Maximum line width in pixels. Non-positive values return `[text]` unchanged.
|
|
27
|
-
* @param measureText - Optional real text measurer. When omitted, uses a character-width heuristic.
|
|
28
|
-
*/
|
|
29
|
-
export function wrapText(
|
|
30
|
-
text: string,
|
|
31
|
-
fontSize: number,
|
|
32
|
-
fontWeight: number,
|
|
33
|
-
maxWidth: number,
|
|
34
|
-
measureText?: MeasureTextFn,
|
|
35
|
-
): string[] {
|
|
36
|
-
if (maxWidth <= 0) return [text];
|
|
37
|
-
|
|
38
|
-
// Split on explicit newlines first
|
|
39
|
-
const segments = text.split('\n');
|
|
40
|
-
if (segments.length > 1) {
|
|
41
|
-
return segments.flatMap((segment) =>
|
|
42
|
-
segment.length === 0 ? [''] : wrapText(segment, fontSize, fontWeight, maxWidth, measureText),
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Use real text measurement when available
|
|
47
|
-
if (measureText) {
|
|
48
|
-
const textWidth = measureText(text, fontSize, fontWeight).width;
|
|
49
|
-
if (textWidth <= maxWidth) return [text];
|
|
50
|
-
|
|
51
|
-
const words = text.split(' ');
|
|
52
|
-
const lines: string[] = [];
|
|
53
|
-
let current = '';
|
|
54
|
-
|
|
55
|
-
for (const word of words) {
|
|
56
|
-
const candidate = current ? `${current} ${word}` : word;
|
|
57
|
-
const candidateWidth = measureText(candidate, fontSize, fontWeight).width;
|
|
58
|
-
if (candidateWidth > maxWidth && current) {
|
|
59
|
-
lines.push(current);
|
|
60
|
-
current = word;
|
|
61
|
-
} else {
|
|
62
|
-
current = candidate;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
if (current) lines.push(current);
|
|
66
|
-
|
|
67
|
-
return lines;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Heuristic: estimate character width from font size and weight.
|
|
71
|
-
// Reuses the same ratio and weight adjustment constants as text-measure.
|
|
72
|
-
const charWidth = estimateCharWidth(fontSize, fontWeight);
|
|
73
|
-
const maxChars = Math.floor(maxWidth / charWidth);
|
|
74
|
-
|
|
75
|
-
if (text.length <= maxChars) return [text];
|
|
76
|
-
|
|
77
|
-
const words = text.split(' ');
|
|
78
|
-
const lines: string[] = [];
|
|
79
|
-
let current = '';
|
|
80
|
-
|
|
81
|
-
for (const word of words) {
|
|
82
|
-
const candidate = current ? `${current} ${word}` : word;
|
|
83
|
-
if (candidate.length > maxChars && current) {
|
|
84
|
-
lines.push(current);
|
|
85
|
-
current = word;
|
|
86
|
-
} else {
|
|
87
|
-
current = candidate;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
if (current) lines.push(current);
|
|
91
|
-
|
|
92
|
-
return lines;
|
|
93
|
-
}
|