@flourish/legend
Advanced tools
Comparing version 3.0.0 to 3.1.0
{ | ||
"name": "@flourish/legend", | ||
"version": "3.0.0", | ||
"version": "3.1.0", | ||
"description": "Flourish module for making legend", | ||
@@ -17,2 +17,3 @@ "main": "legend.js", | ||
"dependencies": { | ||
"d3-array": "^1.0.0", | ||
"d3-scale": "^2.2.2", | ||
@@ -19,0 +20,0 @@ "d3-selection": "^1.4.0" |
@@ -0,1 +1,7 @@ | ||
# 3.1.0 | ||
* Support diverging scales | ||
* Improve label placement and spacing | ||
* Add settings for legend min and max | ||
* Slight refactoring | ||
# 3.0.0 | ||
@@ -2,0 +8,0 @@ * Add `autoTitle` method |
@@ -0,1 +1,3 @@ | ||
import { ascending } from "d3-array"; | ||
function appendTo(container) { | ||
@@ -61,2 +63,38 @@ container.appendChild(this._container.node()); | ||
export { appendTo, format, getContainer, visible, autoTitle, _updateTitle }; | ||
function getDomain(domain_, state) { | ||
var domain = domain_.sort(ascending); | ||
var domain_min = domain[0]; | ||
var domain_max = domain[domain.length - 1]; | ||
var domain_mid = domain.length > 2 ? domain[1] : null; | ||
if (state.legend_min !== null || state.legend_max !== null) { | ||
if (state.legend_min !== null && state.legend_max !== null) { | ||
if (state.legend_min < state.legend_max) { | ||
domain_min = state.legend_min; | ||
domain_max = state.legend_max; | ||
} | ||
} | ||
else if (state.legend_min !== null) { | ||
domain_min = state.legend_min; | ||
if (domain_max < domain_min) domain_max = domain_min + 2; | ||
} | ||
else { | ||
domain_max = state.legend_max; | ||
if (domain_min > domain_max) domain_min = domain_max - 2; | ||
} | ||
} | ||
if (domain_mid !== null && (domain_min > domain_mid || domain_max < domain_mid)) domain_mid = null; | ||
var legend_domain = [domain_min]; | ||
if (domain_mid !== null) legend_domain.push(domain_mid); | ||
legend_domain.push(domain_max); | ||
legend_domain.min = domain_min; | ||
legend_domain.max = domain_max; | ||
legend_domain.mid = domain_mid; | ||
return legend_domain; | ||
} | ||
export { appendTo, format, getContainer, visible, autoTitle, _updateTitle, getDomain }; |
import { select } from "d3-selection"; | ||
import { scaleLinear } from "d3-scale"; | ||
import { appendTo, format, getContainer, visible, autoTitle, _updateTitle } from "../common"; | ||
import { appendTo, format, getContainer, visible, autoTitle, _updateTitle, getDomain } from "../common"; | ||
import { remToPx } from "../utils"; | ||
var legend_count = 0; | ||
var pixel_ratio = window.devicePixelRatio || 1; | ||
var DEFAULTS = Object.freeze({ | ||
@@ -22,3 +23,6 @@ show_legend: true, | ||
binned_label_mode: "thresholds", | ||
binned_label_custom: "" | ||
binned_label_custom: "", | ||
legend_min: null, | ||
legend_max: null | ||
}); | ||
@@ -59,7 +63,9 @@ | ||
ContinuousColorLegend.prototype.data = function(items_, colorFunction_) { | ||
// items_ must be a 2-element domain array [minValue, maxValue] | ||
// items_ must be a 2-element or 3-element domain array [minValue, midValue, maxValue] | ||
// colorFunction_ must be a scale function with color range | ||
if (!items_ && !colorFunction_) return this._legend_domain.slice(); | ||
this._legend_domain = Array.isArray(items_) ? items_.slice() : []; | ||
var items = Array.isArray(items_) ? items_.slice() : []; | ||
this._legend_domain = getDomain(items, this._state); | ||
this._colorFunction = colorFunction_; | ||
@@ -90,12 +96,11 @@ | ||
ContinuousColorLegend.prototype._updateLegend = function() { | ||
var domain = this._legend_domain; | ||
this._scale_type = "continuous"; | ||
if (this._colorFunction.midpoint !== undefined) this._scale_type = "diverging"; | ||
else if (this._colorFunction.thresholds) this._scale_type = "binned"; | ||
if (this._colorFunction.thresholds) this._scale_type = "binned"; | ||
else if (domain.length > 2) this._scale_type = "diverging"; | ||
var domain = this._legend_domain; | ||
var s = this._state; | ||
var pixel_ratio = window.devicePixelRatio || 1; | ||
var label_padding = this._scale_type == "continuous" ? 0 : remToPx(s.text_size); | ||
var w = remToPx(s.color_band_width), h = remToPx(s.color_band_height), label_h = Math.ceil(remToPx(s.text_size) * 0.95), label_w = w + label_padding * 2; | ||
var w = remToPx(s.color_band_width), h = remToPx(s.color_band_height), label_h = Math.ceil(remToPx(s.text_size) * 0.95), label_w = w; | ||
@@ -112,3 +117,2 @@ this._container.style("display", "inline-flex") | ||
.attr("height", h * pixel_ratio) | ||
.style("margin", "0 " + label_padding + "px") | ||
.style("border-radius", s.color_band_radius + "px") | ||
@@ -134,3 +138,3 @@ .style("width", w + "px") | ||
function xToVal(x) { | ||
return domain[0] + (x / w) * (domain[1] - domain[0]); | ||
return domain.min + (x / w) * (domain.max - domain.min); | ||
} | ||
@@ -144,3 +148,3 @@ | ||
if (this._marker_value !== null) { | ||
var xScale = scaleLinear().domain(domain).range([0, w]); | ||
var xScale = scaleLinear().domain([domain.min, domain.max]).range([0, w]); | ||
var x_marker = xScale(this.markerValue()); | ||
@@ -184,3 +188,3 @@ var y_top = 0; | ||
.text(function () { | ||
return format ? format(domain[0]) : domain[0]; | ||
return format ? format(domain.min) : domain.min; | ||
}); | ||
@@ -198,6 +202,8 @@ | ||
.text(function () { | ||
return format ? format(domain[1]) : domain[1]; | ||
return format ? format(domain.max) : domain.max; | ||
}); | ||
var labels = []; | ||
var label_padding_start = 0; | ||
var label_padding_end = 0; | ||
if (this._scale_type === "diverging") labels = this._getDivergingLabels(); | ||
@@ -219,15 +225,15 @@ else if (this._scale_type === "binned") labels = this._getBinnedLabels(); | ||
var placed_labels = []; | ||
var label_padding = remToPx(s.text_size); | ||
var inRange = getInRangeFunction([0, label_canvas.node().width]); | ||
var band_width = remToPx(s.color_band_width); | ||
var inRange = getInRangeFunction([0, band_width]); | ||
var scale = scaleLinear().domain([domain.min, domain.max]).range([0, band_width]); | ||
return function (value) { | ||
var scale = scaleLinear().domain(domain).range([label_padding, remToPx(s.color_band_width) + label_padding]); | ||
var center = scale(value); | ||
if (!inRange(center)) return; | ||
var text = format ? format(value) : value; | ||
var half_width = ctx.measureText(text).width / 2 + 1; | ||
var half_width = (ctx.measureText(text).width / 2) + 1; | ||
var left = center - half_width; | ||
var right = center + half_width; | ||
if (!inRange(left) || !inRange(right)) return; | ||
@@ -246,2 +252,4 @@ var index = 0; | ||
if (!at_end && placed_labels[index].left < right) return; | ||
if (at_start) label_padding_start = Math.abs(Math.min(0, left)); | ||
if (at_end) label_padding_end = Math.max(0, right - band_width); | ||
@@ -253,6 +261,21 @@ placed_labels.splice(index, 0, { left: left, center: center, right: right }); | ||
labels.forEach(function (value) { | ||
var label = prepLabel(value); | ||
if (label) ctx.fillText(label.text, label.position, 0); | ||
var prepped_labels = labels.map(function (value) { | ||
return prepLabel(value); | ||
}); | ||
var label_canvas_width = remToPx(s.color_band_width) + label_padding_start + label_padding_end; | ||
label_canvas | ||
.attr("width", label_canvas_width * pixel_ratio) | ||
.style("width", label_canvas_width + "px"); | ||
select(".color-range").style("margin-left", label_padding_start + "px"); | ||
ctx.scale(pixel_ratio, pixel_ratio); | ||
ctx.textAlign = "center"; | ||
ctx.textBaseline = "top"; | ||
ctx.fillStyle = font_color; | ||
ctx.font = remToPx(s.text_size) + "px " + font_family; | ||
prepped_labels.forEach(function(label) { | ||
if (label) ctx.fillText(label.text, label.position + label_padding_start, 0); | ||
}); | ||
} | ||
@@ -262,11 +285,10 @@ }; | ||
ContinuousColorLegend.prototype._getDivergingLabels = function() { | ||
var legend_domain = this._legend_domain; | ||
var inRange = getInRangeFunction(legend_domain); | ||
var domain = this._legend_domain; | ||
var inRange = getInRangeFunction(domain); | ||
var labels = []; | ||
if (inRange(this._colorFunction.midpoint)) labels.push(this._colorFunction.midpoint); | ||
var gradient_domain = this._colorFunction.domain; | ||
if (inRange(gradient_domain[0])) labels.push(gradient_domain[0]); | ||
if (inRange(gradient_domain[1])) labels.push(gradient_domain[1]); | ||
if (legend_domain[0] !== gradient_domain[0]) labels.push(legend_domain[0]); | ||
if (legend_domain[1] !== gradient_domain[1]) labels.push(legend_domain[1]); | ||
if (inRange(domain.min)) labels.push(domain.min); | ||
if (inRange(domain.mid)) labels.push(domain.mid); | ||
if (inRange(domain.max)) labels.push(domain.max); | ||
return labels; | ||
@@ -277,4 +299,4 @@ }; | ||
var s = this._state; | ||
var legend_domain = this._legend_domain; | ||
var inRange = getInRangeFunction(legend_domain); | ||
var domain = this._legend_domain; | ||
var inRange = getInRangeFunction(domain); | ||
var labels; | ||
@@ -284,4 +306,4 @@ | ||
labels = this._colorFunction.thresholds.slice().filter(inRange); | ||
if (legend_domain[0] < labels[0]) labels.push(legend_domain[0]); | ||
if (legend_domain[1] > labels[labels.length - 1]) labels.push(legend_domain[1]); | ||
if (domain.min < labels[0]) labels.unshift(domain.min); | ||
if (domain.max > labels[labels.length - domain.length - 1]) labels.unshift(domain.max); | ||
} | ||
@@ -294,6 +316,6 @@ else if (s.binned_label_mode === "centers") { | ||
var last_threshold = thresholds[thresholds.length - 1]; | ||
if (first_threshold > legend_domain[0]) labels.push((legend_domain[0] + first_threshold) / 2); | ||
if (last_threshold < legend_domain[1]) labels.push((last_threshold + legend_domain[1]) / 2); | ||
if (first_threshold > domain.min) labels.unshift((domain.min + first_threshold) / 2); | ||
if (last_threshold < domain.max) labels.unshift((last_threshold + domain.max) / 2); | ||
} | ||
else labels.push((legend_domain[0] + legend_domain[1]) / 2); | ||
else labels.push((domain.min + domain.max) / 2); | ||
} | ||
@@ -305,6 +327,12 @@ else labels = s.binned_label_custom.split(";").map(parseFloat); | ||
function getInRangeFunction(legend_domain) { | ||
return function (val) { return val >= legend_domain[0] && val <= legend_domain[1]; }; | ||
function getInRangeFunction(range) { | ||
var legend_max = -Infinity; | ||
var legend_min = Infinity; | ||
for (var i in range) { | ||
legend_max = Math.max(legend_max, range[i]); | ||
legend_min = Math.min(legend_min, range[i]); | ||
} | ||
return function (val) { return val >= legend_min && val <= legend_max; }; | ||
} | ||
export default ContinuousColorLegend; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
168042
4290
3
+ Addedd3-array@^1.0.0