vega-encode
Version:
Visual encoding transforms for Vega dataflows.
135 lines (119 loc) • 4.3 kB
JavaScript
import {Transform, ingest} from 'vega-dataflow';
import {
GradientLegend, SymbolLegend,
labelFormat, labelFraction, labelValues,
scaleFraction, tickCount
} from 'vega-scale';
import {constant, inherits, isFunction, peek} from 'vega-util';
/**
* Generates legend entries for visualizing a scale.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Scale} params.scale - The scale to generate items for.
* @param {*} [params.count=5] - The approximate number of items, or
* desired tick interval, to use.
* @param {*} [params.limit] - The maximum number of entries to
* include in a symbol legend.
* @param {Array<*>} [params.values] - The exact tick values to use.
* These must be legal domain values for the provided scale.
* If provided, the count argument is ignored.
* @param {string} [params.formatSpecifier] - A format specifier
* to use in conjunction with scale.tickFormat. Legal values are
* any valid D3 format specifier string.
* @param {function(*):string} [params.format] - The format function to use.
* If provided, the formatSpecifier argument is ignored.
*/
export default function LegendEntries(params) {
Transform.call(this, [], params);
}
inherits(LegendEntries, Transform, {
transform(_, pulse) {
if (this.value != null && !_.modified()) {
return pulse.StopPropagation;
}
var locale = pulse.dataflow.locale(),
out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS),
items = this.value,
type = _.type || SymbolLegend,
scale = _.scale,
limit = +_.limit,
count = tickCount(scale, _.count == null ? 5 : _.count, _.minstep),
lskip = !!_.values || type === SymbolLegend,
format = _.format || labelFormat(locale, scale, count, type, _.formatSpecifier, _.formatType, lskip),
values = _.values || labelValues(scale, count),
domain, fraction, size, offset, ellipsis;
if (items) out.rem = items;
if (type === SymbolLegend) {
if (limit && values.length > limit) {
pulse.dataflow.warn('Symbol legend count exceeds limit, filtering items.');
items = values.slice(0, limit - 1);
ellipsis = true;
} else {
items = values;
}
if (isFunction(size = _.size)) {
// if first value maps to size zero, remove from list (vega#717)
if (!_.values && scale(items[0]) === 0) {
items = items.slice(1);
}
// compute size offset for legend entries
offset = items.reduce((max, value) => Math.max(max, size(value, _)), 0);
} else {
size = constant(offset = size || 8);
}
items = items.map((value, index) =>
ingest({
index: index,
label: format(value, index, items),
value: value,
offset: offset,
size: size(value, _)
})
);
if (ellipsis) {
ellipsis = values[items.length];
items.push(ingest({
index: items.length,
label: `\u2026${values.length-items.length} entries`,
value: ellipsis,
offset: offset,
size: size(ellipsis, _)
}));
}
}
else if (type === GradientLegend) {
domain = scale.domain(),
fraction = scaleFraction(scale, domain[0], peek(domain));
// if automatic label generation produces 2 or fewer values,
// use the domain end points instead (fixes vega/vega#1364)
if (values.length < 3 && !_.values && domain[0] !== peek(domain)) {
values = [domain[0], peek(domain)];
}
items = values.map((value, index) =>
ingest({
index: index,
label: format(value, index, values),
value: value,
perc: fraction(value)
})
);
}
else {
size = values.length - 1;
fraction = labelFraction(scale);
items = values.map((value, index) =>
ingest({
index: index,
label: format(value, index, values),
value: value,
perc: index ? fraction(value) : 0,
perc2: index === size ? 1 : fraction(values[index+1])
})
);
}
out.source = items;
out.add = items;
this.value = items;
return out;
}
});