-
Notifications
You must be signed in to change notification settings - Fork 187
/
Copy pathlegends.js
85 lines (77 loc) · 3.25 KB
/
legends.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import {rgb} from "d3";
import {createContext} from "./context.js";
import {legendRamp} from "./legends/ramp.js";
import {isSymbolColorLegend, legendSwatches, legendSymbols} from "./legends/swatches.js";
import {inherit, isScaleOptions} from "./options.js";
import {normalizeScale} from "./scales.js";
const legendRegistry = new Map([
["symbol", legendSymbols],
["color", legendColor],
["opacity", legendOpacity]
]);
export function legend(options = {}) {
for (const [key, value] of legendRegistry) {
const scale = options[key];
if (isScaleOptions(scale)) {
// e.g., ignore {color: "red"}
const context = createContext(options);
let hint;
// For symbol legends, pass a hint to the symbol scale.
if (key === "symbol") {
const {fill, stroke = fill === undefined && isScaleOptions(options.color) ? "color" : undefined} = options;
hint = {fill, stroke};
}
return value(normalizeScale(key, scale, hint), legendOptions(context, scale, options), (key) =>
isScaleOptions(options[key]) ? normalizeScale(key, options[key]) : null
);
}
}
throw new Error("unknown legend type; no scale found");
}
export function exposeLegends(scales, context, defaults = {}) {
return (key, options) => {
if (!legendRegistry.has(key)) throw new Error(`unknown legend type: ${key}`);
if (!(key in scales)) return;
return legendRegistry.get(key)(scales[key], legendOptions(context, defaults[key], options), (key) => scales[key]);
};
}
function legendOptions({className, ...context}, {label, ticks, tickFormat} = {}, options) {
return inherit(options, {className, ...context}, {label, ticks, tickFormat});
}
function legendColor(color, {legend = true, ...options}) {
if (legend === true) legend = color.type === "ordinal" ? "swatches" : "ramp";
if (color.domain === undefined) return; // no identity legend
switch (`${legend}`.toLowerCase()) {
case "swatches":
return legendSwatches(color, options);
case "ramp":
return legendRamp(color, options);
default:
throw new Error(`unknown legend type: ${legend}`);
}
}
function legendOpacity({type, interpolate, ...scale}, {legend = true, color = rgb(0, 0, 0), ...options}) {
if (!interpolate) throw new Error(`${type} opacity scales are not supported`);
if (legend === true) legend = "ramp";
if (`${legend}`.toLowerCase() !== "ramp") throw new Error(`${legend} opacity legends are not supported`);
return legendColor({type, ...scale, interpolate: interpolateOpacity(color)}, {legend, ...options});
}
function interpolateOpacity(color) {
const {r, g, b} = rgb(color) || rgb(0, 0, 0); // treat invalid color as black
return (t) => `rgba(${r},${g},${b},${t})`;
}
export function createLegends(scales, context, options) {
const legends = [];
let hasColor = false;
for (const [key, value] of legendRegistry) {
if (!(key in scales)) continue;
if (key === "color" && hasColor) continue;
const o = inherit(options[key], {legend: options.legend});
if (!o.legend) continue;
const legend = value(scales[key], legendOptions(context, scales[key], o), (key) => scales[key]);
if (legend == null) continue;
if (key === "symbol" && isSymbolColorLegend(legend)) hasColor = true;
legends.push(legend);
}
return legends;
}