From 14eef0a7f56d59242907323b4e53cec71812f7f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Mon, 11 Jan 2021 17:58:44 +0100 Subject: [PATCH 1/6] first pass for legends - implements color legend (#23) --- src/legend.js | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/plot.js | 10 ++- 2 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 src/legend.js diff --git a/src/legend.js b/src/legend.js new file mode 100644 index 0000000000..d037c55c33 --- /dev/null +++ b/src/legend.js @@ -0,0 +1,202 @@ +import {axisBottom, create, format, interpolate, interpolateRound, range, scaleBand, scaleLinear, quantile, quantize} from "d3"; + +export function Legend( + {color: colorScale}, + {color} = {} +) { + return { + ...(color && color.legend && {color: new ColorLegend(colorScale, color)}) + }; +} + +export class ColorLegend { + constructor({name}, color) { + this.name = name || "color"; + this.color = color; + } + render( + index, + {[this.name]: color} + , + channels, + { + width: canvasWidth, + height: canvasHeight + } + ) { + let { color: { + legend: { + title, + tickSize = 6, + width = 320, + height = 44 + tickSize, + top = -20, + right = 0, + bottom, + left, + ticks = width / 64, + tickFormat, + tickValues + } = {} + } = {} } = this; + console.warn({ + title, + tickSize, + width, + height, + top, + bottom, + left, + right + }); + const tx = left !== undefined ? left : canvasWidth - width + right; + const ty = bottom !== undefined ? canvasHeight - bottom - height : top; + return create("svg:g") + .attr("transform", `translate(${tx},${ty})`) + .call(g => legend(g, {color, title, tickSize, width, height, ticks, tickFormat, tickValues})) + .node(); + } +} + +function legend(svg, { + color, + title, + tickSize, + width, + height, + marginTop = 18, + marginRight = 0, + marginBottom = 16 + tickSize, + marginLeft = 0, + ticks, + tickFormat, + tickValues +} = {}) { + + let tickAdjust = g => g.selectAll(".tick line").attr("y1", marginTop + marginBottom - height); + let x; + + // Continuous + if (color.interpolate) { + const n = Math.min(color.domain().length, color.range().length); + + x = color.copy().rangeRound(quantize(interpolate(marginLeft, width - marginRight), n)); + + svg.append("image") + .attr("x", marginLeft) + .attr("y", marginTop) + .attr("width", width - marginLeft - marginRight) + .attr("height", height - marginTop - marginBottom) + .attr("preserveAspectRatio", "none") + .attr("xlink:href", ramp(color.copy().domain(quantize(interpolate(0, 1), n))).toDataURL()); + } + + // Sequential + else if (color.interpolator) { + x = Object.assign(color.copy() + .interpolator(interpolateRound(marginLeft, width - marginRight)), + {range() { return [marginLeft, width - marginRight]; }}); + + svg.append("image") + .attr("x", marginLeft) + .attr("y", marginTop) + .attr("width", width - marginLeft - marginRight) + .attr("height", height - marginTop - marginBottom) + .attr("preserveAspectRatio", "none") + .attr("xlink:href", ramp(color.interpolator()).toDataURL()); + + // scaleSequentialQuantile doesn’t implement ticks or tickFormat. + if (!x.ticks) { + if (tickValues === undefined) { + const n = Math.round(ticks + 1); + tickValues = range(n).map(i => quantile(color.domain(), i / (n - 1))); + } + if (typeof tickFormat !== "function") { + tickFormat = format(tickFormat === undefined ? ",f" : tickFormat); + } + } + } + + // Threshold + else if (color.invertExtent) { + const thresholds + = color.thresholds ? color.thresholds() // scaleQuantize + : color.quantiles ? color.quantiles() // scaleQuantile + : color.domain(); // scaleThreshold + + const thresholdFormat + = tickFormat === undefined ? d => d + : typeof tickFormat === "string" ? format(tickFormat) + : tickFormat; + + x = scaleLinear() + .domain([-1, color.range().length - 1]) + .rangeRound([marginLeft, width - marginRight]); + + svg.append("g") + .selectAll("rect") + .data(color.range()) + .join("rect") + .attr("x", (d, i) => x(i - 1)) + .attr("y", marginTop) + .attr("width", (d, i) => x(i) - x(i - 1)) + .attr("height", height - marginTop - marginBottom) + .attr("fill", d => d); + + tickValues = range(thresholds.length); + tickFormat = i => thresholdFormat(thresholds[i], i); + } + + // Ordinal + else { + x = scaleBand() + .domain(color.domain()) + .rangeRound([marginLeft, width - marginRight]); + + svg.append("g") + .selectAll("rect") + .data(color.domain()) + .join("rect") + .attr("x", x) + .attr("y", marginTop) + .attr("width", Math.max(0, x.bandwidth() - 1)) + .attr("height", height - marginTop - marginBottom) + .attr("fill", color); + + tickAdjust = () => {}; + } + + svg.append("g") + .attr("transform", `translate(0,${height - marginBottom})`) + .call(axisBottom(x) + .ticks(ticks, typeof tickFormat === "string" ? tickFormat : undefined) + .tickFormat(typeof tickFormat === "function" ? tickFormat : undefined) + .tickSize(tickSize) + .tickValues(tickValues)) + .call(tickAdjust) + .call(g => g.select(".domain").remove()) + .call(g => g.append("text") + .attr("x", marginLeft) + .attr("y", marginTop + marginBottom - height - 6) + .attr("fill", "currentColor") + .attr("text-anchor", "start") + .attr("font-weight", "bold") + .attr("class", "title") + .text(title)); + + return svg.node(); +} + + +function ramp(color, n = 256) { + const canvas = create("canvas") + .attr("width", n) + .attr("height", 1) + .node(); + const context = canvas.getContext("2d"); + for (let i = 0; i < n; ++i) { + context.fillStyle = color(i / (n - 1)); + context.fillRect(i, 0, 1, 1); + } + return canvas; +} diff --git a/src/plot.js b/src/plot.js index c1527cc5f0..a363496678 100644 --- a/src/plot.js +++ b/src/plot.js @@ -1,5 +1,6 @@ import {create} from "d3"; import {Axes, autoAxisTicks, autoAxisLabels} from "./axes.js"; +import {Legend} from "./legend.js"; import {facets} from "./facet.js"; import {Scales, autoScaleRange} from "./scales.js"; @@ -50,7 +51,8 @@ export function plot(options = {}) { const scaleDescriptors = Scales(scaleChannels, options); const scales = ScaleFunctions(scaleDescriptors); const axes = Axes(scaleDescriptors, options); - const dimensions = Dimensions(scaleDescriptors, axes, options); + const legend = Legend(scaleDescriptors, options); + const dimensions = Dimensions(scaleDescriptors, axes, legend, options); autoScaleRange(scaleDescriptors, dimensions); autoAxisTicks(scaleDescriptors, axes); @@ -68,6 +70,8 @@ export function plot(options = {}) { const y = facet !== undefined && scales.fy ? "fy" : "y"; if (axes[x]) marks.unshift(axes[x]); if (axes[y]) marks.unshift(axes[y]); + + if (legend.color) marks.push(legend.color); const {width, height} = dimensions; @@ -101,6 +105,9 @@ function Dimensions( fx: {axis: fxAxis} = {}, fy: {axis: fyAxis} = {} }, + { + color + }, { width = 640, height = y || fy ? 396 : 60, @@ -116,6 +123,7 @@ function Dimensions( marginLeft = Math.max((yAxis === "left" ? 40 : 0) + facetMarginLeft, xAxis || fxAxis ? 20 : 0) } = {} ) { + color; // TDB: reserve space for the color legend? return { width, height, From afac319230f0930d3abf918f91abc4cf695afbdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Mon, 11 Jan 2021 18:16:37 +0100 Subject: [PATCH 2/6] top default depends on there being a title or not --- src/legend.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legend.js b/src/legend.js index d037c55c33..fc505c38da 100644 --- a/src/legend.js +++ b/src/legend.js @@ -30,7 +30,7 @@ export class ColorLegend { tickSize = 6, width = 320, height = 44 + tickSize, - top = -20, + top = title === undefined ? -20 : -3, right = 0, bottom, left, From 9727cf485a75927649af337e7d13ccf338970468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Fri, 12 Mar 2021 09:37:29 +0100 Subject: [PATCH 3/6] rebase --- package.json | 1 + src/legend.js | 10 - test/output/gistempAnomaly.svg | 17 ++ test/plots/gistemp-anomaly.js | 3 +- yarn.lock | 362 ++++++++++++++++++++++++++++++++- 5 files changed, 371 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 4a38a89e9d..400c8563f4 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ }, "devDependencies": { "clean-css": "^5.1.1", + "canvas": "^2.6.1", "eslint": "^7.12.1", "esm": "^3.2.25", "js-beautify": "^1.13.0", diff --git a/src/legend.js b/src/legend.js index fc505c38da..0f3383339d 100644 --- a/src/legend.js +++ b/src/legend.js @@ -39,16 +39,6 @@ export class ColorLegend { tickValues } = {} } = {} } = this; - console.warn({ - title, - tickSize, - width, - height, - top, - bottom, - left, - right - }); const tx = left !== undefined ? left : canvasWidth - width + right; const ty = bottom !== undefined ? canvasHeight - bottom - height : top; return create("svg:g") diff --git a/test/output/gistempAnomaly.svg b/test/output/gistempAnomaly.svg index 0c45efd716..585865996d 100644 --- a/test/output/gistempAnomaly.svg +++ b/test/output/gistempAnomaly.svg @@ -1713,4 +1713,21 @@ + + + + + −0.5 + + + 0.0 + + + 0.5 + + + 1.0 + + + \ No newline at end of file diff --git a/test/plots/gistemp-anomaly.js b/test/plots/gistemp-anomaly.js index aede87001c..b40805b501 100644 --- a/test/plots/gistemp-anomaly.js +++ b/test/plots/gistemp-anomaly.js @@ -10,7 +10,8 @@ export default async function() { grid: true }, color: { - type: "diverging" + type: "diverging", + legend: true }, marks: [ Plot.ruleY([0]), diff --git a/yarn.lock b/yarn.lock index feed050546..6d52e1810b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -118,6 +118,11 @@ ansi-regex@^2.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + ansi-regex@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" @@ -142,6 +147,19 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -236,6 +254,15 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +canvas@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.6.1.tgz#0d087dd4d60f5a5a9efa202757270abea8bef89e" + integrity sha512-S98rKsPcuhfTcYbtF53UIJhcbgIAK533d1kJKMwsMwAIFgfd58MOyxRud3kktlzWiEkFliaJtvyZCBtud/XVEA== + dependencies: + nan "^2.14.0" + node-pre-gyp "^0.11.0" + simple-get "^3.0.3" + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -269,6 +296,11 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + clean-css@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.1.1.tgz#e0d168b5f5497a85f58606f009c81fdf89d84e65" @@ -276,6 +308,11 @@ clean-css@^5.1.1: dependencies: source-map "~0.6.0" +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -325,6 +362,11 @@ config-chain@^1.1.12: ini "^1.3.4" proto-list "~1.2.1" +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -614,6 +656,13 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +debug@^3.2.6: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + debug@^4.0.1, debug@^4.1.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" @@ -626,6 +675,13 @@ decimal.js@^10.2.1: resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw== +decompress-response@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986" + integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== + dependencies: + mimic-response "^2.0.0" + deep-equal@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" @@ -638,6 +694,11 @@ deep-equal@~1.1.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -674,6 +735,16 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" @@ -965,6 +1036,13 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +fs-minipass@^1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -985,6 +1063,20 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" @@ -1067,6 +1159,11 @@ has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + has@^1.0.3, has@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -1090,13 +1187,20 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -iconv-lite@0.4, iconv-lite@0.4.24: +iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" +ignore-walk@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" + integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== + dependencies: + minimatch "^3.0.4" + ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -1128,7 +1232,7 @@ inherits@2, inherits@~2.0.3, inherits@~2.0.4: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@^1.3.4: +ini@^1.3.4, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -1184,6 +1288,18 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -1421,10 +1537,10 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -mime-db@1.46.0: - version "1.46.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.46.0.tgz#6267748a7f799594de3cbc8cde91def349661cee" - integrity sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ== +mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== mime-types@^2.1.12, mime-types@~2.1.19: version "2.1.29" @@ -1433,6 +1549,11 @@ mime-types@^2.1.12, mime-types@~2.1.19: dependencies: mime-db "1.46.0" +mimic-response@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" + integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -1440,11 +1561,33 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@~1.2.5: +minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" @@ -1460,11 +1603,54 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nan@^2.14.0: + version "2.14.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" + integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +needle@^2.2.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.6.0.tgz#24dbb55f2509e2324b4a99d61f413982013ccdbe" + integrity sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + +node-pre-gyp@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054" + integrity sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + +nopt@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" + integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== + dependencies: + abbrev "1" + osenv "^0.1.4" + nopt@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" @@ -1472,6 +1658,42 @@ nopt@^5.0.0: dependencies: abbrev "1" +npm-bundled@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" + integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== + dependencies: + npm-normalize-package-bin "^1.0.1" + +npm-normalize-package-bin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== + +npm-packlist@^1.1.6: + version "1.4.8" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" + integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + npm-normalize-package-bin "^1.0.1" + +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" @@ -1482,6 +1704,11 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + object-inspect@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" @@ -1515,7 +1742,7 @@ object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" -once@^1.3.0: +once@^1.3.0, once@^1.3.1: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= @@ -1559,6 +1786,19 @@ os-homedir@^1.0.0: resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= +os-tmpdir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -1648,12 +1888,22 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + re-emitter@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/re-emitter/-/re-emitter-1.1.4.tgz#15bdefca3d47e05d6442838fd26759c8a4eaa236" integrity sha512-C0SIXdXDSus2yqqvV7qifnb4NoWP7mEBXJq3axci301mXHCZb8Djwm4hrEZo4UeXRaEnfjH98uQ8EBppk2oNWA== -readable-stream@^2.0.0, readable-stream@~2.3.6: +readable-stream@^2.0.0, readable-stream@^2.0.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -1753,6 +2003,13 @@ resumer@~0.0.0: dependencies: through "~2.3.4" +rimraf@^2.6.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -1797,6 +2054,11 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + saxes@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" @@ -1804,7 +2066,7 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -semver@^5.6.0: +semver@^5.3.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -1823,6 +2085,11 @@ serialize-javascript@^4.0.0: dependencies: randombytes "^2.1.0" +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -1840,6 +2107,25 @@ sigmund@^1.0.1: resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= +signal-exit@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.0.tgz#b45be062435e50d159540b576202ceec40b9c6b3" + integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA== + dependencies: + decompress-response "^4.2.0" + once "^1.3.1" + simple-concat "^1.0.0" + slice-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" @@ -1912,6 +2198,23 @@ stealthy-require@^1.1.1: resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + string-width@^4.2.0: version "4.2.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" @@ -1953,13 +2256,20 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -strip-ansi@^3.0.0: +strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= dependencies: ansi-regex "^2.0.0" +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" @@ -1972,6 +2282,11 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -2051,6 +2366,19 @@ tape@^4.13.3: string.prototype.trim "~1.2.1" through "~2.3.8" +tar@^4: + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + terser@^5.0.0: version "5.6.0" resolved "https://registry.yarnpkg.com/terser/-/terser-5.6.0.tgz#138cdf21c5e3100b1b3ddfddf720962f88badcd2" @@ -2254,6 +2582,13 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" @@ -2289,6 +2624,11 @@ yallist@^2.1.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= +yallist@^3.0.0, yallist@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" From fe8272f12cf6e304dc964f0b0a9ad2fd3f031d17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Fri, 12 Mar 2021 10:29:08 +0100 Subject: [PATCH 4/6] swatches with automatic layout --- package.json | 2 +- src/legend.js | 81 ++++++++++++++++++++++++++++++++-- test/plots/learning-poverty.js | 12 ++--- test/plots/music-revenue.js | 4 ++ 4 files changed, 89 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 400c8563f4..6acf4cf197 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,8 @@ "@observablehq/plot": "./dist/plot.cjs.js" }, "devDependencies": { - "clean-css": "^5.1.1", "canvas": "^2.6.1", + "clean-css": "^5.1.1", "eslint": "^7.12.1", "esm": "^3.2.25", "js-beautify": "^1.13.0", diff --git a/src/legend.js b/src/legend.js index 0f3383339d..79be2d2851 100644 --- a/src/legend.js +++ b/src/legend.js @@ -137,8 +137,8 @@ function legend(svg, { tickFormat = i => thresholdFormat(thresholds[i], i); } - // Ordinal - else { + // Ordinal, bar + else if (false) { x = scaleBand() .domain(color.domain()) .rangeRound([marginLeft, width - marginRight]); @@ -155,6 +155,23 @@ function legend(svg, { tickAdjust = () => {}; } + // ordinal, swatches + else { + return swatches(svg, { + color, + title, + // tickSize, + width, + height, + marginTop, + marginRight, + marginBottom, + marginLeft, + ticks, + tickFormat + //, tickValues + }); + } svg.append("g") .attr("transform", `translate(0,${height - marginBottom})`) @@ -165,14 +182,14 @@ function legend(svg, { .tickValues(tickValues)) .call(tickAdjust) .call(g => g.select(".domain").remove()) - .call(g => g.append("text") + .call(title ? (g => g.append("text") .attr("x", marginLeft) .attr("y", marginTop + marginBottom - height - 6) .attr("fill", "currentColor") .attr("text-anchor", "start") .attr("font-weight", "bold") .attr("class", "title") - .text(title)); + .text(title)) : () => {}); return svg.node(); } @@ -190,3 +207,59 @@ function ramp(color, n = 256) { } return canvas; } + +function swatches(svg, { + color, + title, +// tickSize, + width, + height, + marginTop, + marginRight, + marginBottom, + marginLeft, + ticks, + tickFormat + // , tickValues +}) { + const l = svg.append("g").attr("transform", `translate(0,${height - marginBottom})`); + const n = ticks; // maximum swatches per row (maybe we shouldn't use ticks?) + const cwidth = (width - marginLeft - marginRight) / Math.max(1, Math.min(n, color.domain().length)); + + if (tickFormat === undefined) tickFormat = d => shorten(d, cwidth / 7); + + const domain = color.domain().map(d => ({ + d, + fill: color(d), + text: tickFormat(d) + })) + .filter(d => d.text); + + const cheight = 20; + const swatches = l.append("g") + .selectAll() + .data(domain) + .join("g") + .attr("transform", (_,i) => `translate(${(i % n) * cwidth},${Math.floor(i/n) * cheight})`); + swatches.append("rect").attr("y", -5).attr("height", 10).attr("width", 10).attr("fill", d => d.fill); + swatches.append("text").attr("dx", 13).attr("text-anchor", "start").attr("dy", "0.35em").text(d => d.text); + + if (title) { + l.append("text") + .attr("x", marginLeft) + .attr("y", marginTop + marginBottom - height - 6) + .attr("fill", "currentColor") + .attr("text-anchor", "start") + .attr("font-weight", "bold") + .attr("class", "title") + .text(title); + } + + + return svg.node(); +} + +function shorten(text, l = 20) { + text = String(text); + return (text.length > l) ? text.substring(0, l - 3).concat("…") : text; +} \ No newline at end of file diff --git a/test/plots/learning-poverty.js b/test/plots/learning-poverty.js index a12a94bf4d..5dc47eebe0 100644 --- a/test/plots/learning-poverty.js +++ b/test/plots/learning-poverty.js @@ -4,13 +4,14 @@ import * as d3 from "d3"; export default async function() { const data = await d3.csv("data/learning-poverty.csv", d3.autoType); const values = data.flatMap(d => [ - {...d, type: "ok", share: 100 - d["Learning Poverty"]}, + {...d, type: "doing well", share: 100 - d["Learning Poverty"]}, {...d, type: "poor", share: d["Learning Poverty"] - d["Out-of-School (OoS)"]}, {...d, type: "out of school", share: d["Out-of-School (OoS)"]} ]); return Plot.plot({ marginLeft: 120, - height: 1160, + marginTop: 60, + height: 1200, x: { axis: "top", grid: true, @@ -23,12 +24,13 @@ export default async function() { label: null }, color: { - domain: ["ok", "poor", "out of school"], - range: ["#aed9e0", "#edd382", "#ffa69e"] + domain: ["doing well", "poor", "out of school"], + range: ["#aed9e0", "#edd382", "#ffa69e"], + legend: { title: "Students" } }, marks: [ Plot.barX(values, Plot.stackX({ - x: d => (d.type === "ok" ? -1 : 1) * d.share, // diverging bars + x: d => (d.type === "doing well" ? -1 : 1) * d.share, y: "Country Name", fill: "type" })), diff --git a/test/plots/music-revenue.js b/test/plots/music-revenue.js index ce90ebfd7d..3fd94c8363 100644 --- a/test/plots/music-revenue.js +++ b/test/plots/music-revenue.js @@ -10,6 +10,10 @@ export default async function() { label: "↑ Annual revenue (billions, adj.)", transform: d => d / 1000 }, + color: { + domain: d3.groupSort(data, g => d3.sum(g, d => d.revenue), d => d.group).reverse(), + legend: { ticks: 6 } + }, marks: [ Plot.areaY(data, Plot.stackY({...stack, fill: "group", title: d => `${d.format}\n${d.group}`})), Plot.lineY(data, Plot.stackY2({...stack, stroke: "white", strokeWidth: 1})), From 4723e3e253f3f918b9e769b1a5a5ea667e0864a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Fri, 12 Mar 2021 10:30:23 +0100 Subject: [PATCH 5/6] tests --- test/output/gistempAnomaly.svg | 2 +- test/output/learningPoverty.svg | 843 ++++++++++++++++---------------- test/output/musicRevenue.svg | 54 +- 3 files changed, 469 insertions(+), 430 deletions(-) diff --git a/test/output/gistempAnomaly.svg b/test/output/gistempAnomaly.svg index 585865996d..8025df0158 100644 --- a/test/output/gistempAnomaly.svg +++ b/test/output/gistempAnomaly.svg @@ -1727,7 +1727,7 @@ 1.0 - + \ No newline at end of file diff --git a/test/output/learningPoverty.svg b/test/output/learningPoverty.svg index 5f5a7e08c3..80f3f95a41 100644 --- a/test/output/learningPoverty.svg +++ b/test/output/learningPoverty.svg @@ -1,655 +1,670 @@ - + - + Niger - + Chad - + Madagascar - + Yemen, Rep - + Afghanistan - + Burundi - + Mali - + Ethiopia - + Congo, Dem Rep - + Togo - + Burkina Faso - + Congo, Rep - + Uganda - + Cote d’Ivoire - + Dominican Republic - + South Africa - + Benin - + Cameroon - + Honduras - + Pakistan - + Paraguay - + Senegal - + Nicaragua - + Egypt, Arab Rep - + Guatemala - + Panama - + Morocco - + Tunisia - + Kyrgyz Republic - + Ecuador - + Bangladesh - + Peru - + India - + Argentina - + Jordan - + Cambodia - + Kuwait - + Colombia - + Brazil - + Botswana - + Mexico - + Oman - + Uruguay - + Saudi Arabia - + Chile - + Iran, Islamic Rep - + Indonesia - + Qatar - + Armenia - + United Arab Emirates - + Costa Rica - + Bahrain - + Malta - + Thailand - + Azerbaijan - + Turkey - + Trinidad and Tobago - + Romania - + China - + Cyprus - + Sri Lanka - + Georgia - + Malaysia - + Israel - + Bulgaria - + New Zealand - + Australia - + Slovak Republic - + Serbia - + United States - + France - + Portugal - + Belgium - + Poland - + Norway - + Hungary - + Slovenia - + Germany - + Spain - + Canada - + Latvia - + Croatia - + Macao SAR, China - + Denmark - + Italy - + United Kingdom - + Russian Federation - + Hong Kong SAR, China - + Lithuania - + Korea, Rep - + Czech Republic - + Singapore - + Finland - + Austria - + Sweden - + Ireland - + Kazakhstan - + Japan - + Vietnam - + Netherlands - + - 100% + 100% - 80% + 80% - 60% + 60% - 40% + 40% - 20% + 20% - 0% + 0% - 20% + 20% - 40% + 40% - 60% + 60% - 80% + 80% - 100% + 100% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + doing well + + + poor + + + out of school + + Students + \ No newline at end of file diff --git a/test/output/musicRevenue.svg b/test/output/musicRevenue.svg index f8464868fe..c5a46fc09c 100644 --- a/test/output/musicRevenue.svg +++ b/test/output/musicRevenue.svg @@ -79,7 +79,7 @@ - + 8 - Track Tape @@ -91,35 +91,35 @@ CD Single Disc - + Cassette Tape - + Cassette Single Tape - + DVD Audio Other - + Download Album Download - + Download Music Video Download - + Download Single Download - + Kiosk Other - + LP/EP Vinyl @@ -127,7 +127,7 @@ Limited Tier Paid Subscription Streaming - + Music Video (Physical) Other @@ -139,11 +139,11 @@ Other Ad-Supported Streaming Streaming - + Other Digital Download - + Other Tapes Tape @@ -151,7 +151,7 @@ Paid Subscription Streaming - + Ringtones & Ringbacks Download @@ -163,11 +163,11 @@ SoundExchange Distributions Streaming - + Synchronization Other - + Vinyl Single Vinyl @@ -200,4 +200,28 @@ + + + + + Disc + + + Tape + + + Vinyl + + + Stre… + + + Down… + + + Other + + + + \ No newline at end of file From 1e3e0ac1b2ebf4fe3284450bb16eee61b9c5b1e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Fri, 12 Mar 2021 10:59:38 +0100 Subject: [PATCH 6/6] alignments --- src/legend.js | 10 +++++----- test/output/learningPoverty.svg | 4 ++-- test/output/musicRevenue.svg | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/legend.js b/src/legend.js index 79be2d2851..42cf3684f2 100644 --- a/src/legend.js +++ b/src/legend.js @@ -223,10 +223,11 @@ function swatches(svg, { // , tickValues }) { const l = svg.append("g").attr("transform", `translate(0,${height - marginBottom})`); - const n = ticks; // maximum swatches per row (maybe we shouldn't use ticks?) - const cwidth = (width - marginLeft - marginRight) / Math.max(1, Math.min(n, color.domain().length)); + const n = Math.round(ticks); // maximum swatches per row (maybe we shouldn't use ticks?) + const cwidth = Math.floor((width - marginLeft - marginRight) / Math.max(1, Math.min(n, color.domain().length))); + const cheight = 20; - if (tickFormat === undefined) tickFormat = d => shorten(d, cwidth / 7); + if (tickFormat === undefined) tickFormat = d => shorten(d, (cwidth - 14) / 5); const domain = color.domain().map(d => ({ d, @@ -235,12 +236,11 @@ function swatches(svg, { })) .filter(d => d.text); - const cheight = 20; const swatches = l.append("g") .selectAll() .data(domain) .join("g") - .attr("transform", (_,i) => `translate(${(i % n) * cwidth},${Math.floor(i/n) * cheight})`); + .attr("transform", (_, i) => `translate(${(i % n) * cwidth},${Math.floor(i / n) * cheight})`); swatches.append("rect").attr("y", -5).attr("height", 10).attr("width", 10).attr("fill", d => d.fill); swatches.append("text").attr("dx", 13).attr("text-anchor", "start").attr("dy", "0.35em").text(d => d.text); diff --git a/test/output/learningPoverty.svg b/test/output/learningPoverty.svg index 80f3f95a41..c57b4343d7 100644 --- a/test/output/learningPoverty.svg +++ b/test/output/learningPoverty.svg @@ -658,10 +658,10 @@ doing well - + poor - + out of school Students diff --git a/test/output/musicRevenue.svg b/test/output/musicRevenue.svg index c5a46fc09c..96785b807a 100644 --- a/test/output/musicRevenue.svg +++ b/test/output/musicRevenue.svg @@ -206,19 +206,19 @@ Disc - + Tape - + Vinyl - + Stre… - + Down… - + Other