UNPKG

4.24 kBJavaScriptView Raw
1import {Transform, ingest} from 'vega-dataflow';
2import {
3 GradientLegend, SymbolLegend,
4 labelFormat, labelFraction, labelValues,
5 scaleFraction, tickCount
6} from 'vega-scale';
7import {constant, inherits, isFunction, peek} from 'vega-util';
8
9/**
10 * Generates legend entries for visualizing a scale.
11 * @constructor
12 * @param {object} params - The parameters for this operator.
13 * @param {Scale} params.scale - The scale to generate items for.
14 * @param {*} [params.count=5] - The approximate number of items, or
15 * desired tick interval, to use.
16 * @param {*} [params.limit] - The maximum number of entries to
17 * include in a symbol legend.
18 * @param {Array<*>} [params.values] - The exact tick values to use.
19 * These must be legal domain values for the provided scale.
20 * If provided, the count argument is ignored.
21 * @param {string} [params.formatSpecifier] - A format specifier
22 * to use in conjunction with scale.tickFormat. Legal values are
23 * any valid D3 format specifier string.
24 * @param {function(*):string} [params.format] - The format function to use.
25 * If provided, the formatSpecifier argument is ignored.
26 */
27export default function LegendEntries(params) {
28 Transform.call(this, [], params);
29}
30
31var prototype = inherits(LegendEntries, Transform);
32
33prototype.transform = function(_, pulse) {
34 if (this.value != null && !_.modified()) {
35 return pulse.StopPropagation;
36 }
37
38 var locale = pulse.dataflow.locale(),
39 out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS),
40 items = this.value,
41 type = _.type || SymbolLegend,
42 scale = _.scale,
43 limit = +_.limit,
44 count = tickCount(scale, _.count == null ? 5 : _.count, _.minstep),
45 lskip = !!_.values || type === SymbolLegend,
46 format = _.format || labelFormat(locale, scale, count, type, _.formatSpecifier, _.formatType, lskip),
47 values = _.values || labelValues(scale, count),
48 domain, fraction, size, offset, ellipsis;
49
50 if (items) out.rem = items;
51
52 if (type === SymbolLegend) {
53 if (limit && values.length > limit) {
54 pulse.dataflow.warn('Symbol legend count exceeds limit, filtering items.');
55 items = values.slice(0, limit - 1);
56 ellipsis = true;
57 } else {
58 items = values;
59 }
60
61 if (isFunction(size = _.size)) {
62 // if first value maps to size zero, remove from list (vega#717)
63 if (!_.values && scale(items[0]) === 0) {
64 items = items.slice(1);
65 }
66 // compute size offset for legend entries
67 offset = items.reduce(function(max, value) {
68 return Math.max(max, size(value, _));
69 }, 0);
70 } else {
71 size = constant(offset = size || 8);
72 }
73
74 items = items.map(function(value, index) {
75 return ingest({
76 index: index,
77 label: format(value, index, items),
78 value: value,
79 offset: offset,
80 size: size(value, _)
81 });
82 });
83
84 if (ellipsis) {
85 ellipsis = values[items.length];
86 items.push(ingest({
87 index: items.length,
88 label: `\u2026${values.length-items.length} entries`,
89 value: ellipsis,
90 offset: offset,
91 size: size(ellipsis, _)
92 }));
93 }
94 }
95
96 else if (type === GradientLegend) {
97 domain = scale.domain(),
98 fraction = scaleFraction(scale, domain[0], peek(domain));
99
100 // if automatic label generation produces 2 or fewer values,
101 // use the domain end points instead (fixes vega/vega#1364)
102 if (values.length < 3 && !_.values && domain[0] !== peek(domain)) {
103 values = [domain[0], peek(domain)];
104 }
105
106 items = values.map(function(value, index) {
107 return ingest({
108 index: index,
109 label: format(value, index, values),
110 value: value,
111 perc: fraction(value)
112 });
113 });
114 }
115
116 else {
117 size = values.length - 1;
118 fraction = labelFraction(scale);
119
120 items = values.map(function(value, index) {
121 return ingest({
122 index: index,
123 label: format(value, index, values),
124 value: value,
125 perc: index ? fraction(value) : 0,
126 perc2: index === size ? 1 : fraction(values[index+1])
127 });
128 });
129 }
130
131 out.source = items;
132 out.add = items;
133 this.value = items;
134
135 return out;
136};