@opendata-ai/openchart-core 8.0.0-rc.9 → 8.1.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.
Files changed (83) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts +403 -45
  3. package/dist/index.js +50 -18
  4. package/dist/index.js.map +1 -1
  5. package/dist/styles.css +1 -1
  6. package/dist/styles.css.map +1 -1
  7. package/package.json +1 -2
  8. package/schema/chart.schema.json +375 -69
  9. package/schema/table.schema.json +35 -1
  10. package/schema/vizspec.schema.json +992 -171
  11. package/src/accessibility/__tests__/alt-text.test.ts +0 -110
  12. package/src/accessibility/__tests__/aria.test.ts +0 -125
  13. package/src/accessibility/alt-text.ts +0 -155
  14. package/src/accessibility/aria.ts +0 -75
  15. package/src/accessibility/index.ts +0 -6
  16. package/src/colors/__tests__/colorblind.test.ts +0 -75
  17. package/src/colors/__tests__/contrast.test.ts +0 -119
  18. package/src/colors/__tests__/palettes.test.ts +0 -74
  19. package/src/colors/colorblind.ts +0 -131
  20. package/src/colors/contrast.ts +0 -143
  21. package/src/colors/index.ts +0 -36
  22. package/src/colors/palettes.ts +0 -206
  23. package/src/helpers/__tests__/deep-merge.test.ts +0 -54
  24. package/src/helpers/__tests__/spec-builders.test.ts +0 -338
  25. package/src/helpers/deep-merge.ts +0 -29
  26. package/src/helpers/spec-builders.ts +0 -479
  27. package/src/index.ts +0 -215
  28. package/src/labels/__tests__/collision.test.ts +0 -310
  29. package/src/labels/collision.ts +0 -249
  30. package/src/labels/index.ts +0 -13
  31. package/src/layout/__tests__/chrome.test.ts +0 -208
  32. package/src/layout/__tests__/text-measure.test.ts +0 -57
  33. package/src/layout/chrome.ts +0 -470
  34. package/src/layout/index.ts +0 -18
  35. package/src/layout/text-measure.ts +0 -404
  36. package/src/layout/text-wrap.ts +0 -93
  37. package/src/locale/__tests__/format.test.ts +0 -606
  38. package/src/locale/format.ts +0 -459
  39. package/src/locale/index.ts +0 -21
  40. package/src/responsive/__tests__/breakpoints.test.ts +0 -146
  41. package/src/responsive/__tests__/metrics.test.ts +0 -52
  42. package/src/responsive/breakpoints.ts +0 -177
  43. package/src/responsive/index.ts +0 -53
  44. package/src/responsive/metrics.ts +0 -299
  45. package/src/schema/__tests__/generated-artifacts.test.ts +0 -42
  46. package/src/schema/__tests__/schema.test.ts +0 -179
  47. package/src/styles/__tests__/reduced-motion-selectors.test.ts +0 -28
  48. package/src/styles/animation.css +0 -213
  49. package/src/styles/base.css +0 -102
  50. package/src/styles/chrome.css +0 -174
  51. package/src/styles/dark.css +0 -44
  52. package/src/styles/graph.css +0 -135
  53. package/src/styles/index.css +0 -28
  54. package/src/styles/keyframes.css +0 -151
  55. package/src/styles/legend.css +0 -18
  56. package/src/styles/reduced-motion.css +0 -67
  57. package/src/styles/series-search.css +0 -153
  58. package/src/styles/sparkline.css +0 -16
  59. package/src/styles/table-animation.css +0 -93
  60. package/src/styles/table.css +0 -398
  61. package/src/styles/token-definitions.ts +0 -199
  62. package/src/styles/tokens.css +0 -156
  63. package/src/styles/tooltip.css +0 -126
  64. package/src/styles/you-draw-it.css +0 -126
  65. package/src/theme/__tests__/dark-mode.test.ts +0 -232
  66. package/src/theme/__tests__/defaults.test.ts +0 -73
  67. package/src/theme/__tests__/resolve.test.ts +0 -277
  68. package/src/theme/dark-mode.ts +0 -258
  69. package/src/theme/defaults.ts +0 -110
  70. package/src/theme/index.ts +0 -8
  71. package/src/theme/presets.ts +0 -64
  72. package/src/theme/resolve.ts +0 -312
  73. package/src/types/__tests__/encoding.test.ts +0 -368
  74. package/src/types/__tests__/gradient.test.ts +0 -95
  75. package/src/types/__tests__/spec.test.ts +0 -499
  76. package/src/types/encoding.ts +0 -280
  77. package/src/types/events.ts +0 -236
  78. package/src/types/index.ts +0 -290
  79. package/src/types/layout.ts +0 -2024
  80. package/src/types/schema-roots.ts +0 -32
  81. package/src/types/spec.ts +0 -2925
  82. package/src/types/table.ts +0 -108
  83. package/src/types/theme.ts +0 -209
@@ -1,470 +0,0 @@
1
- /**
2
- * Chrome layout computation.
3
- *
4
- * Takes a Chrome spec + resolved theme and produces a ResolvedChrome
5
- * with computed text positions, styles, and total chrome heights.
6
- *
7
- * Supports three chrome modes:
8
- * - full: all chrome elements rendered at normal size
9
- * - compact: title only, no subtitle/source/byline/footer
10
- * - hidden: no chrome at all (maximizes chart area)
11
- *
12
- * Font sizes scale down continuously at narrow widths to keep
13
- * chrome proportional to the container.
14
- */
15
-
16
- import type { ChromeMode } from '../responsive/breakpoints';
17
- import type {
18
- MeasureTextFn,
19
- ResolvedChrome,
20
- ResolvedChromeElement,
21
- TextStyle,
22
- } from '../types/layout';
23
- import type { Chrome, ChromeText } from '../types/spec';
24
- import type { ChromeDefaults, ResolvedTheme } from '../types/theme';
25
- import {
26
- BRAND_FONT_SIZE,
27
- BRAND_MIN_WIDTH,
28
- BRAND_RESERVE_WIDTH,
29
- COMPACT_WIDTH,
30
- estimateCharWidth,
31
- estimateTextHeight,
32
- } from './text-measure';
33
-
34
- // ---------------------------------------------------------------------------
35
- // Helpers
36
- // ---------------------------------------------------------------------------
37
-
38
- /** Normalize a chrome field to text + optional style and offset overrides. */
39
- function normalizeChromeText(
40
- value: string | ChromeText | undefined,
41
- ): { text: string; style?: ChromeText['style']; offset?: ChromeText['offset'] } | null {
42
- if (value === undefined) return null;
43
- if (typeof value === 'string') return { text: value };
44
- return { text: value.text, style: value.style, offset: value.offset };
45
- }
46
-
47
- /**
48
- * Scale a font size based on container width. Only applies to default sizes
49
- * (not user overrides). Scales from 100% at >= 500px down to 72% at <= 250px.
50
- */
51
- function scaleFontSize(baseFontSize: number, width: number): number {
52
- if (width >= 500) return baseFontSize;
53
- if (width <= 250) return Math.max(Math.round(baseFontSize * 0.72), 10);
54
- const t = (width - 250) / 250;
55
- return Math.max(Math.round(baseFontSize * (0.72 + t * 0.28)), 10);
56
- }
57
-
58
- /** Build a TextStyle from chrome defaults + optional overrides, with width-based scaling. */
59
- function buildTextStyle(
60
- defaults: ChromeDefaults,
61
- fontFamily: string,
62
- textColor: string,
63
- width: number,
64
- overrides?: ChromeText['style'],
65
- ): TextStyle {
66
- const hasExplicitSize = overrides?.fontSize !== undefined;
67
- const baseFontSize = overrides?.fontSize ?? defaults.fontSize;
68
- const fontSize = hasExplicitSize ? baseFontSize : scaleFontSize(baseFontSize, width);
69
-
70
- // No dominantBaseline: chrome y positions are top-edge coordinates and
71
- // renderers convert to the alphabetic baseline via textAscent(). WebKit
72
- // renders `hanging` from different font metrics than Blink and never
73
- // inherits it into tspans (bugs 139258/297455), which shifted wrapped
74
- // titles above the container on iOS Safari.
75
- return {
76
- fontFamily: overrides?.fontFamily ?? fontFamily,
77
- fontSize,
78
- fontWeight: overrides?.fontWeight ?? defaults.fontWeight,
79
- fill: overrides?.color ?? textColor ?? defaults.color,
80
- lineHeight: defaults.lineHeight,
81
- textAnchor: 'start',
82
- };
83
- }
84
-
85
- /**
86
- * Estimate how many lines text will wrap to, given a max width.
87
- * Uses character-count word-wrapping that matches the SVG renderer's
88
- * wrapText behavior (word-boundary breaks, same charWidth heuristic).
89
- * Returns at least 1.
90
- */
91
- function estimateLineCount(
92
- text: string,
93
- style: TextStyle,
94
- maxWidth: number,
95
- measureText?: MeasureTextFn,
96
- ): number {
97
- if (maxWidth <= 0) return 1;
98
-
99
- // Split on explicit newlines first, then estimate wrapping per segment
100
- const segments = text.split('\n');
101
- if (segments.length > 1) {
102
- return segments.reduce((total, segment) => {
103
- return (
104
- total +
105
- (segment.length === 0 ? 1 : estimateLineCount(segment, style, maxWidth, measureText))
106
- );
107
- }, 0);
108
- }
109
-
110
- // Use real text measurement when available, fall back to heuristic
111
- if (measureText) {
112
- const textWidth = measureText(text, style.fontSize, style.fontWeight).width;
113
- if (textWidth <= maxWidth) return 1;
114
-
115
- const words = text.split(' ');
116
- let lines = 1;
117
- let current = '';
118
-
119
- for (const word of words) {
120
- const candidate = current ? `${current} ${word}` : word;
121
- const candidateWidth = measureText(candidate, style.fontSize, style.fontWeight).width;
122
- if (candidateWidth > maxWidth && current) {
123
- lines++;
124
- current = word;
125
- } else {
126
- current = candidate;
127
- }
128
- }
129
-
130
- return lines;
131
- }
132
-
133
- const charWidth = estimateCharWidth(style.fontSize, style.fontWeight);
134
- const maxChars = Math.floor(maxWidth / charWidth);
135
-
136
- if (text.length <= maxChars) return 1;
137
-
138
- const words = text.split(' ');
139
- let lines = 1;
140
- let current = '';
141
-
142
- for (const word of words) {
143
- const candidate = current ? `${current} ${word}` : word;
144
- if (candidate.length > maxChars && current) {
145
- lines++;
146
- current = word;
147
- } else {
148
- current = candidate;
149
- }
150
- }
151
-
152
- return lines;
153
- }
154
-
155
- // ---------------------------------------------------------------------------
156
- // Public API
157
- // ---------------------------------------------------------------------------
158
-
159
- /**
160
- * Line-height multiplier for the auto-thinning footnote list.
161
- *
162
- * Exported because the chrome renderer steps each footnote line down by it
163
- * while the band height below is computed from it -- the two have to agree, or
164
- * the last line drifts outside the band that was reserved for it.
165
- */
166
- export const FOOTNOTE_LINE_HEIGHT = 1.3;
167
- /** Gap below the footnote list, separating it from the source row. */
168
- const FOOTNOTE_BAND_GAP = 4;
169
-
170
- /**
171
- * Vertical space a footnote list of `count` lines occupies below the plot.
172
- *
173
- * Single source of truth, deliberately: the engine reserves this band in the
174
- * bottom margin, the chrome renderer shifts the source row down by it, and the
175
- * brand watermark shifts by it too. When those three disagree the footnotes
176
- * land on top of the watermark, which is exactly the bug this centralizes away.
177
- */
178
- export function footnoteBandHeight(count: number, theme: ResolvedTheme): number {
179
- if (count <= 0) return 0;
180
- return count * theme.fonts.sizes.small * FOOTNOTE_LINE_HEIGHT + FOOTNOTE_BAND_GAP;
181
- }
182
-
183
- /**
184
- * Compute resolved chrome layout from a Chrome spec and resolved theme.
185
- *
186
- * Produces positioned text elements and total chrome heights (top and bottom).
187
- * Top chrome: title, subtitle. Bottom chrome: source, byline, footer.
188
- *
189
- * @param chrome - The Chrome spec from the user's VizSpec.
190
- * @param theme - The fully resolved theme.
191
- * @param width - Total available width in pixels.
192
- * @param measureText - Optional real text measurement function from the adapter.
193
- * @param chromeMode - Chrome display mode: full, compact (title only), or hidden.
194
- * @param padding - Override padding (for scaled padding from dimensions).
195
- * @param watermark - Whether the brand watermark renders (affects bottom space).
196
- * @param bottomLegendHeight - Reserved height for a bottom-positioned legend
197
- * (legend bounds height + gap). When > 0, source/byline/footer y positions
198
- * are shifted down by this amount so the chrome stacks BELOW the legend
199
- * rather than colliding with it. The returned `bottomHeight` includes this
200
- * reservation, so callers should not double-reserve it in margin math.
201
- */
202
- export function computeChrome(
203
- chrome: Chrome | undefined,
204
- theme: ResolvedTheme,
205
- width: number,
206
- measureText?: MeasureTextFn,
207
- chromeMode: ChromeMode = 'full',
208
- padding?: number,
209
- watermark: boolean = true,
210
- bottomLegendHeight: number = 0,
211
- ): ResolvedChrome {
212
- if (!chrome || chromeMode === 'hidden') {
213
- // Brand watermark is also skipped at cramped sizes (height < 200px triggers
214
- // hidden mode, and those containers are typically < BRAND_MIN_WIDTH), so
215
- // bottomHeight: 0 is safe here.
216
- return { topHeight: 0, bottomHeight: 0 };
217
- }
218
-
219
- const pad = padding ?? theme.spacing.padding;
220
- const chromeGap = theme.spacing.chromeGap;
221
- // Subtract a small buffer beyond padding so the heuristic char-width
222
- // estimate triggers word-wrap before the real font overflows the container.
223
- const WRAP_SAFETY_BUFFER = 5;
224
- const maxWidth = width - pad * 2 - WRAP_SAFETY_BUFFER;
225
- const fontFamily = theme.fonts.family;
226
-
227
- // Track vertical cursor for top elements
228
- let topY = pad;
229
- const topElements: Partial<Pick<ResolvedChrome, 'eyebrow' | 'title' | 'subtitle'>> = {};
230
-
231
- // Eyebrow (hidden in compact mode — same rule as subtitle, keeps the
232
- // title alone at narrow viewports).
233
- const eyebrowNorm = chromeMode === 'compact' ? null : normalizeChromeText(chrome.eyebrow);
234
- if (eyebrowNorm) {
235
- const style = buildTextStyle(
236
- theme.chrome.eyebrow,
237
- fontFamily,
238
- theme.chrome.eyebrow.color,
239
- width,
240
- eyebrowNorm.style,
241
- );
242
- const lineCount = estimateLineCount(eyebrowNorm.text, style, maxWidth, measureText);
243
- const element: ResolvedChromeElement = {
244
- text: eyebrowNorm.text,
245
- x: pad + (eyebrowNorm.offset?.dx ?? 0),
246
- y: topY + (eyebrowNorm.offset?.dy ?? 0),
247
- maxWidth,
248
- style,
249
- };
250
- topElements.eyebrow = element;
251
- topY += estimateTextHeight(style.fontSize, lineCount, style.lineHeight) + chromeGap;
252
- }
253
-
254
- // Title
255
- const titleNorm = normalizeChromeText(chrome.title);
256
- if (titleNorm) {
257
- const style = buildTextStyle(
258
- theme.chrome.title,
259
- fontFamily,
260
- theme.chrome.title.color,
261
- width,
262
- titleNorm.style,
263
- );
264
- const lineCount = estimateLineCount(titleNorm.text, style, maxWidth, measureText);
265
- const element: ResolvedChromeElement = {
266
- text: titleNorm.text,
267
- x: pad + (titleNorm.offset?.dx ?? 0),
268
- y: topY + (titleNorm.offset?.dy ?? 0),
269
- maxWidth,
270
- style,
271
- };
272
- topElements.title = element;
273
- topY += estimateTextHeight(style.fontSize, lineCount, style.lineHeight) + chromeGap;
274
- }
275
-
276
- // Subtitle (hidden in compact mode)
277
- const subtitleNorm = chromeMode === 'compact' ? null : normalizeChromeText(chrome.subtitle);
278
- if (subtitleNorm) {
279
- const style = buildTextStyle(
280
- theme.chrome.subtitle,
281
- fontFamily,
282
- theme.chrome.subtitle.color,
283
- width,
284
- subtitleNorm.style,
285
- );
286
- const lineCount = estimateLineCount(subtitleNorm.text, style, maxWidth, measureText);
287
- const element: ResolvedChromeElement = {
288
- text: subtitleNorm.text,
289
- x: pad + (subtitleNorm.offset?.dx ?? 0),
290
- y: topY + (subtitleNorm.offset?.dy ?? 0),
291
- maxWidth,
292
- style,
293
- };
294
- topElements.subtitle = element;
295
- topY += estimateTextHeight(style.fontSize, lineCount, style.lineHeight) + chromeGap;
296
- }
297
-
298
- // Add chromeToChart gap if there are any top elements. Tighten on narrow
299
- // viewports so the subtitle doesn't float far above a legend or chart area.
300
- const hasTopChrome = eyebrowNorm || titleNorm || subtitleNorm;
301
- const chromeToChart =
302
- width < COMPACT_WIDTH ? Math.min(theme.spacing.chromeToChart, 2) : theme.spacing.chromeToChart;
303
- const topHeight = hasTopChrome ? topY - pad + chromeToChart - chromeGap : 0;
304
-
305
- // Bottom chrome text hidden in compact mode, but brand watermark still
306
- // renders for wide-enough charts. Reserve space so it doesn't overflow.
307
- if (chromeMode === 'compact') {
308
- let compactBottom = 0;
309
- if (watermark && width >= BRAND_MIN_WIDTH) {
310
- const brandHeight = estimateTextHeight(BRAND_FONT_SIZE, 1);
311
- compactBottom = theme.spacing.chartToFooter + brandHeight + pad + bottomLegendHeight;
312
- } else if (bottomLegendHeight > 0) {
313
- // No bottom chrome content but a bottom legend was reserved upstream.
314
- // Surface the reservation through bottomHeight so margin math stays
315
- // additive and consistent with full mode.
316
- compactBottom = bottomLegendHeight;
317
- }
318
- return {
319
- topHeight,
320
- bottomHeight: compactBottom,
321
- ...topElements,
322
- };
323
- }
324
-
325
- // Bottom elements: source, byline, footer, and an optional custom brand
326
- // (`chrome.brand`). When a custom brand is supplied it suppresses the default
327
- // tryOpenData.ai watermark and renders right-anchored on the same baseline.
328
- const brandNorm = normalizeChromeText(chrome.brand);
329
- const showWatermark = watermark && !brandNorm;
330
- // Reserve space on the right for the brand watermark or custom brand so the
331
- // left-anchored bottom chrome items don't overlap.
332
- const shouldReserveBrandWidth = (showWatermark || !!brandNorm) && width >= BRAND_MIN_WIDTH;
333
- const bottomMaxWidth = maxWidth - (shouldReserveBrandWidth ? BRAND_RESERVE_WIDTH : 0);
334
- const bottomElements: Partial<Pick<ResolvedChrome, 'source' | 'byline' | 'footer'>> = {};
335
- let bottomHeight = 0;
336
-
337
- const bottomItems: Array<{
338
- key: 'source' | 'byline' | 'footer';
339
- norm: { text: string; style?: ChromeText['style']; offset?: ChromeText['offset'] };
340
- defaults: ChromeDefaults;
341
- }> = [];
342
-
343
- const sourceNorm = normalizeChromeText(chrome.source);
344
- if (sourceNorm) {
345
- bottomItems.push({
346
- key: 'source',
347
- norm: sourceNorm,
348
- defaults: theme.chrome.source,
349
- });
350
- }
351
-
352
- const bylineNorm = normalizeChromeText(chrome.byline);
353
- if (bylineNorm) {
354
- bottomItems.push({
355
- key: 'byline',
356
- norm: bylineNorm,
357
- defaults: theme.chrome.byline,
358
- });
359
- }
360
-
361
- const footerNorm = normalizeChromeText(chrome.footer);
362
- if (footerNorm) {
363
- bottomItems.push({
364
- key: 'footer',
365
- norm: footerNorm,
366
- defaults: theme.chrome.footer,
367
- });
368
- }
369
-
370
- if (bottomItems.length > 0) {
371
- bottomHeight += theme.spacing.chartToFooter;
372
- // Push bottom chrome below the bottom legend (if reserved). Stored y values
373
- // include this offset so renderers don't need to know about the legend band.
374
- bottomHeight += bottomLegendHeight;
375
-
376
- for (const item of bottomItems) {
377
- const style = buildTextStyle(
378
- item.defaults,
379
- fontFamily,
380
- item.defaults.color,
381
- width,
382
- item.norm.style,
383
- );
384
- const lineCount = estimateLineCount(item.norm.text, style, bottomMaxWidth, measureText);
385
- const height = estimateTextHeight(style.fontSize, lineCount, style.lineHeight);
386
-
387
- // y positions will be computed relative to the bottom of the
388
- // chart area by the engine. We store offsets from bottom start.
389
- bottomElements[item.key] = {
390
- text: item.norm.text,
391
- x: pad + (item.norm.offset?.dx ?? 0),
392
- y: bottomHeight + (item.norm.offset?.dy ?? 0), // offset from where bottom chrome starts
393
- maxWidth: bottomMaxWidth,
394
- style,
395
- };
396
-
397
- bottomHeight += height + chromeGap;
398
- }
399
-
400
- // Remove trailing gap
401
- bottomHeight -= chromeGap;
402
-
403
- // Ensure bottom height accommodates the brand watermark, which renders
404
- // at the same Y as the first bottom chrome item but is taller (20px font
405
- // vs 12px source). Without this, the brand overflows the SVG viewBox.
406
- if (showWatermark && width >= BRAND_MIN_WIDTH) {
407
- const brandHeight = estimateTextHeight(BRAND_FONT_SIZE, 1);
408
- // firstItemY is chartToFooter (the Y offset of the first bottom item).
409
- // The brand needs brandHeight below that point; bottom chrome content
410
- // needs (bottomHeight - chartToFooter). Take the max.
411
- const contentBelowFirstItem = bottomHeight - theme.spacing.chartToFooter;
412
- if (brandHeight > contentBelowFirstItem) {
413
- bottomHeight += brandHeight - contentBelowFirstItem;
414
- }
415
- }
416
-
417
- // Add bottom padding
418
- bottomHeight += pad;
419
- } else if (showWatermark && width >= BRAND_MIN_WIDTH) {
420
- // No bottom chrome items, but brand watermark still renders.
421
- // Reserve space: chartToFooter gap + brand text height + padding,
422
- // plus the bottom-legend reservation so the watermark sits below it.
423
- const brandHeight = estimateTextHeight(BRAND_FONT_SIZE, 1);
424
- bottomHeight = theme.spacing.chartToFooter + brandHeight + pad + bottomLegendHeight;
425
- } else if (bottomLegendHeight > 0) {
426
- // No bottom chrome content and no watermark, but a bottom legend was
427
- // reserved upstream. Surface the reservation so callers don't need to
428
- // re-add it on top of bottomHeight.
429
- bottomHeight = bottomLegendHeight;
430
- }
431
-
432
- // Custom brand is right-anchored on the same row as the first bottom chrome
433
- // item. Compute its style/position once we know whether bottom chrome exists.
434
- let brandElement: ResolvedChromeElement | undefined;
435
- if (brandNorm && width >= BRAND_MIN_WIDTH) {
436
- const brandStyle = buildTextStyle(
437
- theme.chrome.footer,
438
- fontFamily,
439
- theme.chrome.footer.color,
440
- width,
441
- brandNorm.style,
442
- );
443
- brandStyle.textAnchor = 'end';
444
- // Y matches the first bottom item (chartToFooter offset). When there are
445
- // no bottom items, the brand becomes the sole footer element and we need
446
- // to allocate the same baseline.
447
- const brandY =
448
- bottomItems.length > 0 ? theme.spacing.chartToFooter : theme.spacing.chartToFooter;
449
- brandElement = {
450
- text: brandNorm.text,
451
- x: width - pad + (brandNorm.offset?.dx ?? 0),
452
- y: brandY + (brandNorm.offset?.dy ?? 0),
453
- maxWidth: BRAND_RESERVE_WIDTH,
454
- style: brandStyle,
455
- };
456
- // Ensure bottomHeight reserves space when brand is the only footer item.
457
- if (bottomItems.length === 0) {
458
- const brandHeight = estimateTextHeight(brandStyle.fontSize, 1, brandStyle.lineHeight);
459
- bottomHeight = theme.spacing.chartToFooter + brandHeight + pad;
460
- }
461
- }
462
-
463
- return {
464
- topHeight,
465
- bottomHeight,
466
- ...topElements,
467
- ...bottomElements,
468
- ...(brandElement ? { brand: brandElement } : {}),
469
- };
470
- }
@@ -1,18 +0,0 @@
1
- /**
2
- * Layout module barrel export.
3
- */
4
-
5
- export { computeChrome, FOOTNOTE_LINE_HEIGHT, footnoteBandHeight } from './chrome';
6
- export {
7
- BRAND_FONT_SIZE,
8
- BRAND_MIN_WIDTH,
9
- BRAND_RESERVE_WIDTH,
10
- COMPACT_WIDTH,
11
- estimateCharWidth,
12
- estimateTextHeight,
13
- estimateTextWidth,
14
- heuristicMeasure,
15
- resolveMeasurer,
16
- textAscent,
17
- } from './text-measure';
18
- export { wrapText } from './text-wrap';