Skip to content

Commit b81ee09

Browse files
committed
refactor(vanilla): extract renderChrome into renderers/chrome.ts
1 parent 3b59d20 commit b81ee09

2 files changed

Lines changed: 99 additions & 99 deletions

File tree

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* Chrome rendering: title, subtitle, source, byline, footer.
3+
*/
4+
5+
import type {
6+
ChartLayout,
7+
MeasureTextFn,
8+
ResolvedChromeElement,
9+
} from '@opendata-ai/openchart-core';
10+
import { wrapText } from '@opendata-ai/openchart-core';
11+
import { applyTextStyle, computeXAxisExtent, createSVGElement, setAttrs } from './svg-dom';
12+
13+
function renderChromeElement(
14+
parent: SVGElement,
15+
element: ResolvedChromeElement,
16+
className: string,
17+
chromeKey: string,
18+
measureText?: MeasureTextFn,
19+
): void {
20+
const text = createSVGElement('text');
21+
setAttrs(text, { x: element.x, y: element.y });
22+
applyTextStyle(text, element.style);
23+
text.setAttribute('class', className);
24+
text.setAttribute('data-chrome-key', chromeKey);
25+
26+
const lines = wrapText(
27+
element.text,
28+
element.style.fontSize,
29+
element.style.fontWeight,
30+
element.maxWidth,
31+
measureText,
32+
);
33+
34+
if (lines.length === 1) {
35+
text.textContent = element.text;
36+
} else {
37+
const lineHeight = element.style.fontSize * (element.style.lineHeight ?? 1.3);
38+
for (let i = 0; i < lines.length; i++) {
39+
const tspan = createSVGElement('tspan');
40+
setAttrs(tspan, { x: element.x, dy: i === 0 ? 0 : lineHeight });
41+
tspan.textContent = lines[i];
42+
text.appendChild(tspan);
43+
}
44+
}
45+
46+
parent.appendChild(text);
47+
}
48+
49+
export function renderChrome(parent: SVGElement, layout: ChartLayout): void {
50+
const g = createSVGElement('g');
51+
g.setAttribute('class', 'oc-chrome');
52+
53+
const { chrome, measureText } = layout;
54+
55+
// Top chrome: render at their stored y positions (already absolute)
56+
if (chrome.title) {
57+
renderChromeElement(g, chrome.title, 'oc-title', 'title', measureText);
58+
}
59+
if (chrome.subtitle) {
60+
renderChromeElement(g, chrome.subtitle, 'oc-subtitle', 'subtitle', measureText);
61+
}
62+
63+
// Bottom chrome starts below x-axis labels/title, not at chart area bottom.
64+
// Accounts for rotated tick labels which need more vertical space.
65+
const xAxisExtent = computeXAxisExtent(layout);
66+
const bottomOffset = layout.area.y + layout.area.height + xAxisExtent;
67+
if (chrome.source) {
68+
renderChromeElement(
69+
g,
70+
{ ...chrome.source, y: bottomOffset + chrome.source.y },
71+
'oc-source',
72+
'source',
73+
measureText,
74+
);
75+
}
76+
if (chrome.byline) {
77+
renderChromeElement(
78+
g,
79+
{ ...chrome.byline, y: bottomOffset + chrome.byline.y },
80+
'oc-byline',
81+
'byline',
82+
measureText,
83+
);
84+
}
85+
if (chrome.footer) {
86+
renderChromeElement(
87+
g,
88+
{ ...chrome.footer, y: bottomOffset + chrome.footer.y },
89+
'oc-footer',
90+
'footer',
91+
measureText,
92+
);
93+
}
94+
95+
parent.appendChild(g);
96+
}

packages/vanilla/src/svg-renderer.ts

Lines changed: 3 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,21 @@ import type {
1717
LegendLayout,
1818
LineMark,
1919
Mark,
20-
MeasureTextFn,
2120
Point,
2221
PointMark,
2322
RectMark,
2423
ResolvedAnimation,
2524
ResolvedAnnotation,
26-
ResolvedChromeElement,
2725
RuleMarkLayout,
2826
TextMarkLayout,
2927
TickMarkLayout,
3028
} from '@opendata-ai/openchart-core';
31-
import { estimateTextWidth, wrapText } from '@opendata-ai/openchart-core';
29+
import { estimateTextWidth } from '@opendata-ai/openchart-core';
3230
import { clampStaggerDelay } from '@opendata-ai/openchart-engine';
3331
import { buildGradientDefs, resolveMarkFill } from './gradient-utils';
3432
import { renderBrand } from './renderers/brand';
35-
import {
36-
applyTextStyle,
37-
computeXAxisExtent,
38-
createSVGElement,
39-
SVG_NS,
40-
setAttrs,
41-
} from './renderers/svg-dom';
33+
import { renderChrome } from './renderers/chrome';
34+
import { applyTextStyle, createSVGElement, SVG_NS, setAttrs } from './renderers/svg-dom';
4235
import { nextSvgId } from './svg-ids';
4336

4437
/**
@@ -75,95 +68,6 @@ const EASE_VAR_MAP: Record<string, string> = {
7568
snappy: 'var(--oc-ease-snappy)',
7669
};
7770

78-
// ---------------------------------------------------------------------------
79-
// Chrome rendering
80-
// ---------------------------------------------------------------------------
81-
82-
function renderChromeElement(
83-
parent: SVGElement,
84-
element: ResolvedChromeElement,
85-
className: string,
86-
chromeKey: string,
87-
measureText?: MeasureTextFn,
88-
): void {
89-
const text = createSVGElement('text');
90-
setAttrs(text, { x: element.x, y: element.y });
91-
applyTextStyle(text, element.style);
92-
text.setAttribute('class', className);
93-
text.setAttribute('data-chrome-key', chromeKey);
94-
95-
const lines = wrapText(
96-
element.text,
97-
element.style.fontSize,
98-
element.style.fontWeight,
99-
element.maxWidth,
100-
measureText,
101-
);
102-
103-
if (lines.length === 1) {
104-
text.textContent = element.text;
105-
} else {
106-
const lineHeight = element.style.fontSize * (element.style.lineHeight ?? 1.3);
107-
for (let i = 0; i < lines.length; i++) {
108-
const tspan = createSVGElement('tspan');
109-
setAttrs(tspan, { x: element.x, dy: i === 0 ? 0 : lineHeight });
110-
tspan.textContent = lines[i];
111-
text.appendChild(tspan);
112-
}
113-
}
114-
115-
parent.appendChild(text);
116-
}
117-
118-
function renderChrome(parent: SVGElement, layout: ChartLayout): void {
119-
const g = createSVGElement('g');
120-
g.setAttribute('class', 'oc-chrome');
121-
122-
const { chrome, measureText } = layout;
123-
124-
// Top chrome: render at their stored y positions (already absolute)
125-
if (chrome.title) {
126-
renderChromeElement(g, chrome.title, 'oc-title', 'title', measureText);
127-
}
128-
if (chrome.subtitle) {
129-
renderChromeElement(g, chrome.subtitle, 'oc-subtitle', 'subtitle', measureText);
130-
}
131-
132-
// Bottom chrome starts below x-axis labels/title, not at chart area bottom.
133-
// Accounts for rotated tick labels which need more vertical space.
134-
const xAxisExtent = computeXAxisExtent(layout);
135-
const bottomOffset = layout.area.y + layout.area.height + xAxisExtent;
136-
if (chrome.source) {
137-
renderChromeElement(
138-
g,
139-
{ ...chrome.source, y: bottomOffset + chrome.source.y },
140-
'oc-source',
141-
'source',
142-
measureText,
143-
);
144-
}
145-
if (chrome.byline) {
146-
renderChromeElement(
147-
g,
148-
{ ...chrome.byline, y: bottomOffset + chrome.byline.y },
149-
'oc-byline',
150-
'byline',
151-
measureText,
152-
);
153-
}
154-
if (chrome.footer) {
155-
renderChromeElement(
156-
g,
157-
{ ...chrome.footer, y: bottomOffset + chrome.footer.y },
158-
'oc-footer',
159-
'footer',
160-
measureText,
161-
);
162-
}
163-
164-
parent.appendChild(g);
165-
}
166-
16771
// ---------------------------------------------------------------------------
16872
// Axis rendering
16973
// ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)