Skip to content

Commit cd06f1a

Browse files
authored
Improved colour-legend display and labels (#143)
* Use a "nice" number range for display purpose only * Improved colour-legend display Cleaner numbers (reduces display of fractions), and better display of "clean" range Give the colour legend a bit more height, but less width, which it doesn't need * Make legend components non-global Prevents legends from different charts interfering with each other Stores `pageIndex` in settings to preserve page position
1 parent 8b7af78 commit cd06f1a

File tree

4 files changed

+65
-49
lines changed

4 files changed

+65
-49
lines changed

packages/perspective-viewer-d3fc/src/js/legend/colorRangeLegend.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import * as fc from "d3fc";
1111
import {getOrCreateElement} from "../utils/utils";
1212

1313
export function colorRangeLegend() {
14-
const width = 100;
15-
const height = 150;
14+
const width = 90;
15+
const height = 200;
1616
let scale = null;
1717

1818
const xScale = d3
@@ -23,7 +23,10 @@ export function colorRangeLegend() {
2323
const formatFunc = d => (Number.isInteger(d) ? d3.format(",.0f")(d) : d3.format(",.2f")(d));
2424

2525
function legend(container) {
26-
const domain = scale.domain();
26+
const domain = scale
27+
.copy()
28+
.nice()
29+
.domain();
2730
const paddedDomain = fc
2831
.extentLinear()
2932
.pad([0.1, 0.1])
@@ -47,16 +50,19 @@ export function colorRangeLegend() {
4750
selection.selectAll("path").style("fill", d => scale(d));
4851
});
4952

53+
const middle = domain[0] < 0 && domain[1] > 0 ? 0 : Math.round((domain[1] + domain[0]) / 2);
54+
const tickValues = [...domain, middle];
55+
5056
const axisLabel = fc
5157
.axisRight(yScale)
52-
.tickValues([...domain, (domain[1] + domain[0]) / 2])
58+
.tickValues(tickValues)
5359
.tickSizeOuter(0)
5460
.tickFormat(d => formatFunc(d));
5561

5662
const legendSelection = getOrCreateElement(container, "div.legend-container", () =>
5763
container
5864
.append("div")
59-
.attr("class", "legend-container")
65+
.attr("class", "legend-container legend-color")
6066
.style("z-index", "2")
6167
);
6268

packages/perspective-viewer-d3fc/src/js/legend/legend.js

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,22 @@ import {getChartElement} from "../plugin/root";
1515
import {getOrCreateElement} from "../utils/utils";
1616
import {draggableComponent} from "./styling/draggableComponent";
1717

18-
const scrollColorLegend = scrollableLegend(
19-
d3Legend
20-
.legendColor()
21-
.shape("circle")
22-
.shapeRadius(6)
23-
);
24-
const scrollSymbolLegend = scrollableLegend(
25-
d3Legend
26-
.legendSymbol()
27-
.shapePadding(1)
28-
.labelOffset(3)
29-
);
18+
const scrollColorLegend = settings =>
19+
scrollableLegend(
20+
d3Legend
21+
.legendColor()
22+
.shape("circle")
23+
.shapeRadius(6),
24+
settings
25+
);
26+
const scrollSymbolLegend = settings =>
27+
scrollableLegend(
28+
d3Legend
29+
.legendSymbol()
30+
.shapePadding(1)
31+
.labelOffset(3),
32+
settings
33+
);
3034

3135
export const colorLegend = () => legendComponent(scrollColorLegend);
3236
export const symbolLegend = () => legendComponent(scrollSymbolLegend, symbolScale);
@@ -42,14 +46,15 @@ function symbolScale(fromScale) {
4246
.range(range);
4347
}
4448

45-
function legendComponent(scrollLegend, scaleModifier) {
49+
function legendComponent(scrollLegendComponent, scaleModifier) {
4650
let settings = {};
4751
let scale = null;
4852
let color = null;
4953
let draggable = draggableComponent();
5054

5155
function legend(container) {
5256
if (scale && scale.range().length > 1) {
57+
const scrollLegend = scrollLegendComponent(settings);
5358
scrollLegend
5459
.scale(scale)
5560
.orient("vertical")
@@ -71,6 +76,25 @@ function legendComponent(scrollLegend, scaleModifier) {
7176

7277
const legendSelection = getOrCreateElement(container, "div.legend-container", () => container.append("div"));
7378

79+
scrollLegend.decorate(selection => {
80+
const isHidden = data => settings.hideKeys && settings.hideKeys.includes(data);
81+
82+
const cells = selection
83+
.select("g.legendCells")
84+
.attr("transform", "translate(20,20)")
85+
.selectAll("g.cell");
86+
87+
cells.classed("hidden", isHidden);
88+
cells.append("title").html(d => d);
89+
90+
if (color) {
91+
cells
92+
.select("path")
93+
.style("fill", d => (isHidden(d) ? null : color(d)))
94+
.style("stroke", d => (isHidden(d) ? null : withoutOpacity(color(d))));
95+
}
96+
});
97+
7498
// render the legend
7599
legendSelection
76100
.attr("class", "legend-container")
@@ -82,25 +106,6 @@ function legendComponent(scrollLegend, scaleModifier) {
82106
}
83107
}
84108

85-
scrollLegend.decorate(selection => {
86-
const isHidden = data => settings.hideKeys && settings.hideKeys.includes(data);
87-
88-
const cells = selection
89-
.select("g.legendCells")
90-
.attr("transform", "translate(20,20)")
91-
.selectAll("g.cell");
92-
93-
cells.classed("hidden", isHidden);
94-
cells.append("title").html(d => d);
95-
96-
if (color) {
97-
cells
98-
.select("path")
99-
.style("fill", d => (isHidden(d) ? null : color(d)))
100-
.style("stroke", d => (isHidden(d) ? null : withoutOpacity(color(d))));
101-
}
102-
});
103-
104109
legend.settings = (...args) => {
105110
if (!args.length) {
106111
return settings;

packages/perspective-viewer-d3fc/src/js/legend/scrollableLegend.js

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,19 @@
88
*/
99
import * as d3Legend from "d3-svg-legend";
1010
import {rebindAll} from "d3fc";
11-
import {areArraysEqualSimple, getOrCreateElement} from "../utils/utils";
11+
import {getOrCreateElement} from "../utils/utils";
1212
import legendControlsTemplate from "../../html/legend-controls.html";
1313
import {cropCellContents} from "./styling/cropCellContents";
1414

15-
export default fromLegend => {
15+
export default (fromLegend, settings) => {
1616
const legend = fromLegend || d3Legend.legendColor();
17-
let domain = [];
1817
let pageCount = 1;
1918
let pageSize = 15;
20-
let pageIndex = 0;
19+
let pageIndex = settings.legend ? settings.legend.pageIndex : 0;
2120
let decorate = () => {};
2221

2322
const scrollableLegend = selection => {
24-
const newDomain = legend.scale().domain();
25-
if (!areArraysEqualSimple(domain, newDomain)) {
26-
pageIndex = 0;
27-
domain = newDomain;
28-
}
23+
const domain = legend.scale().domain();
2924
pageCount = Math.ceil(domain.length / pageSize);
3025

3126
render(selection);
@@ -48,7 +43,7 @@ export default fromLegend => {
4843
.attr("class", pageIndex === 0 ? "disabled" : "")
4944
.on("click", () => {
5045
if (pageIndex > 0) {
51-
pageIndex--;
46+
setPage(pageIndex - 1);
5247
render(selection);
5348
}
5449
});
@@ -58,7 +53,7 @@ export default fromLegend => {
5853
.attr("class", pageIndex >= pageCount - 1 ? "disabled" : "")
5954
.on("click", () => {
6055
if (pageIndex < pageCount - 1) {
61-
pageIndex++;
56+
setPage(pageIndex + 1);
6257
render(selection);
6358
}
6459
});
@@ -80,6 +75,11 @@ export default fromLegend => {
8075
decorate(selection);
8176
};
8277

78+
const setPage = index => {
79+
pageIndex = index;
80+
settings.legend = {pageIndex};
81+
};
82+
8383
const cellFilter = () => {
8484
return (_, i) => i >= pageSize * pageIndex && i < pageSize * pageIndex + pageSize;
8585
};
@@ -103,5 +103,6 @@ export default fromLegend => {
103103
};
104104

105105
rebindAll(scrollableLegend, legend);
106+
106107
return scrollableLegend;
107108
};

packages/perspective-viewer-d3fc/src/less/chart.less

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
overflow: hidden;
2121

2222
&.d3_heatmap {
23-
padding-right: 165px;
23+
padding-right: 105px;
2424
}
2525

2626
&.d3_sunburst {
@@ -151,6 +151,10 @@
151151
left: auto;
152152
width: 150px;
153153

154+
&.legend-color {
155+
width: 90px;
156+
}
157+
154158
&[borderbox-on-hover="true"] {
155159
&:hover {
156160
box-shadow: 2px 2px 6px #000;

0 commit comments

Comments
 (0)