Skip to content

Commit b2d0ebd

Browse files
committed
refactor formatting logic
1 parent ec4e8f0 commit b2d0ebd

File tree

1 file changed

+51
-37
lines changed

1 file changed

+51
-37
lines changed

src/marks/tip.js

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -114,40 +114,13 @@ export class Tip extends Mark {
114114
const widthof = monospace ? monospaceWidth : defaultWidth;
115115
const ee = widthof(ellipsis);
116116

117-
// We borrow the scale’s tick format for facet channels; this is safe for
118-
// ordinal scales (but not continuous scales where the display value may
119-
// need higher precision), and generally better than the default format.
120-
const formatFx = fx && inferTickFormat(fx);
121-
const formatFy = fy && inferTickFormat(fy);
122-
123-
function* format(sources, i) {
124-
if ("title" in sources) {
125-
const text = sources.title.value[i];
126-
for (const line of mark.splitLines(formatDefault(text))) {
127-
yield {name: "", value: mark.clipLine(line)};
128-
}
129-
return;
130-
}
131-
for (const key in sources) {
132-
if (key === "x1" && "x2" in sources) continue;
133-
if (key === "y1" && "y2" in sources) continue;
134-
const channel = sources[key];
135-
const value = channel.value[i];
136-
if (!defined(value) && channel.scale == null) continue;
137-
if (key === "x2" && "x1" in sources) {
138-
yield {name: formatPairLabel(scales, sources.x1, channel, "x"), value: formatPair(sources.x1, channel, i)};
139-
} else if (key === "y2" && "y1" in sources) {
140-
yield {name: formatPairLabel(scales, sources.y1, channel, "y"), value: formatPair(sources.y1, channel, i)};
141-
} else {
142-
const scale = channel.scale;
143-
const line = {name: formatLabel(scales, channel, key), value: formatDefault(value)};
144-
if (scale === "color" || scale === "opacity") line[scale] = values[key][i];
145-
yield line;
146-
}
147-
}
148-
if (index.fi != null && fx) yield {name: String(fx.label ?? "fx"), value: formatFx(index.fx)};
149-
if (index.fi != null && fy) yield {name: String(fy.label ?? "fy"), value: formatFy(index.fy)};
150-
}
117+
// Determine the appropriate formatter.
118+
const format =
119+
"title" in sources // if there is a title channel
120+
? formatTitle // display the title as-is
121+
: index.fi == null // if this mark is not faceted
122+
? formatChannels // display name-value pairs for channels
123+
: formatFacetedChannels(index, scales); // same, plus facets
151124

152125
// We don’t call applyChannelStyles because we only use the channels to
153126
// derive the content of the tip, not its aesthetics.
@@ -173,8 +146,8 @@ export class Tip extends Mark {
173146
this.setAttribute("stroke", "none");
174147
// iteratively render each channel value
175148
const names = new Set();
176-
for (const line of format(sources, i)) {
177-
const name = line.name;
149+
for (const line of format.call(mark, i, sources, scales, values)) {
150+
const {name = ""} = line;
178151
if (name && names.has(name)) continue;
179152
else names.add(name);
180153
renderLine(that, line);
@@ -188,7 +161,7 @@ export class Tip extends Mark {
188161
// just the initial layout of the text; in postrender we will compute the
189162
// exact text metrics and translate the text as needed once we know the
190163
// tip’s orientation (anchor).
191-
function renderLine(selection, {name, value, color, opacity}) {
164+
function renderLine(selection, {name = "", value = "", color, opacity}) {
192165
const swatch = color != null || opacity != null;
193166
let title;
194167
let w = lineWidth * 100;
@@ -328,6 +301,47 @@ function getSources({channels}) {
328301
return sources;
329302
}
330303

304+
function* formatTitle(i, {title}) {
305+
const text = title.value[i];
306+
for (const line of this.splitLines(formatDefault(text))) {
307+
yield {name: "", value: this.clipLine(line)};
308+
}
309+
}
310+
311+
function* formatChannels(i, channels, scales, values) {
312+
for (const key in channels) {
313+
if (key === "x1" && "x2" in channels) continue;
314+
if (key === "y1" && "y2" in channels) continue;
315+
const channel = channels[key];
316+
const value = channel.value[i];
317+
if (!defined(value) && channel.scale == null) continue;
318+
if (key === "x2" && "x1" in channels) {
319+
yield {name: formatPairLabel(scales, channels.x1, channel, "x"), value: formatPair(channels.x1, channel, i)};
320+
} else if (key === "y2" && "y1" in channels) {
321+
yield {name: formatPairLabel(scales, channels.y1, channel, "y"), value: formatPair(channels.y1, channel, i)};
322+
} else {
323+
const scale = channel.scale;
324+
const line = {name: formatLabel(scales, channel, key), value: formatDefault(value)};
325+
if (scale === "color" || scale === "opacity") line[scale] = values[key][i];
326+
yield line;
327+
}
328+
}
329+
}
330+
331+
function formatFacetedChannels(index, scales) {
332+
const {fx, fy} = scales;
333+
// We borrow the scale’s tick format for facet channels; this is safe for
334+
// ordinal scales (but not continuous scales where the display value may need
335+
// higher precision), and generally better than the default format.
336+
const formatFx = fx && inferTickFormat(fx);
337+
const formatFy = fy && inferTickFormat(fy);
338+
return function* (i, channels, scales, values) {
339+
yield* formatChannels(i, channels, scales, values);
340+
if (fx) yield {name: String(fx.label ?? "fx"), value: formatFx(index.fx)};
341+
if (fy) yield {name: String(fy.label ?? "fy"), value: formatFy(index.fy)};
342+
};
343+
}
344+
331345
function formatPair(c1, c2, i) {
332346
return c2.hint?.length // e.g., stackY’s y1 and y2
333347
? `${formatDefault(c2.value[i] - c1.value[i])}`

0 commit comments

Comments
 (0)