From 2b797cc1528b8d1b54bb123a9add351422ccf7da Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 2 Dec 2020 20:13:27 -0800 Subject: [PATCH] top-level style! --- package.json | 1 + src/mark.js | 26 +++++++ src/marks/area.js | 13 ++-- src/marks/bar.js | 21 +++--- src/marks/dot.js | 36 +++++----- src/marks/line.js | 17 +++-- src/marks/link.js | 18 ++--- src/marks/rect.js | 29 ++++---- src/marks/rule.js | 30 +++++---- src/marks/text.js | 31 +++++---- test/aapl-candlestick.html | 6 +- test/aapl-close.html | 2 +- test/cars-parcoords.html | 6 +- test/covid-ihme-projected-deaths.html | 18 ++--- test/metro-inequality-change.html | 7 +- test/metro-unemployment-ridgeline.html | 2 +- test/sf-temperature-band.html | 6 +- test/travelers-year-over-year.html | 14 ++-- test/us-retail-sales.html | 9 +-- yarn.lock | 93 +++++++++++++++----------- 20 files changed, 205 insertions(+), 180 deletions(-) diff --git a/package.json b/package.json index 01509a7f6a..da1e307195 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "dependencies": { "d3-array": "^2.8.0", "d3-axis": "^2.0.0", + "d3-color": "^2.0.0", "d3-interpolate": "^2.0.1", "d3-scale": "^3.2.3", "d3-scale-chromatic": "^2.0.0", diff --git a/src/mark.js b/src/mark.js index 00f4277017..261ac52ed1 100644 --- a/src/mark.js +++ b/src/mark.js @@ -1,3 +1,5 @@ +import {color} from "d3-color"; + export class Mark { constructor(data, channels = [], transform = identity) { const names = new Set(); @@ -46,3 +48,27 @@ export const string = x => x == null ? undefined : x + ""; export const number = x => x == null ? undefined : +x; export const first = d => d[0]; export const second = d => d[1]; + +// Some channels may allow a string constant to be specified; to differentiate +// string constants (e.g., "red") from named fields (e.g., "date"), this +// function tests whether the given value is a CSS color string and returns a +// tuple [channel, constant] where one of the two is undefined, and the other is +// the given value. If you wish to reference a named field that is also a valid +// CSS color, use an accessor (d => d.red) instead. +export function maybeColor(value) { + return typeof value === "string" && (value === "currentColor" || color(value)) ? [undefined, value] : [value, undefined]; +} + +// Similar to maybeColor, this tests whether the given value is a number +// indicating a constant, and otherwise assumes that it’s a channel value. +export function maybeNumber(value) { + return typeof value === "number" ? [undefined, value] : [value, undefined]; +} + +export function applyAttr(selection, name, value) { + if (value != null) selection.attr(name, value); +} + +export function applyStyle(selection, name, value) { + if (value != null) selection.style(name, value); +} diff --git a/src/marks/area.js b/src/marks/area.js index e67cc669fb..9cbb7734ba 100644 --- a/src/marks/area.js +++ b/src/marks/area.js @@ -1,8 +1,8 @@ import {create} from "d3-selection"; import {area as shapeArea} from "d3-shape"; import {Curve} from "../curve.js"; -import {Mark, identity, indexOf, zero} from "../mark.js"; import {defined} from "../defined.js"; +import {Mark, identity, indexOf, zero} from "../mark.js"; import {Style, applyStyles} from "../style.js"; export class Area extends Mark { @@ -14,8 +14,8 @@ export class Area extends Mark { x2, y2, curve, - style, - transform + transform, + ...style } = {} ) { super( @@ -29,14 +29,13 @@ export class Area extends Mark { transform ); this.curve = Curve(curve); - this.style = Style(style); + Object.assign(this, Style(style)); } render(I, {x, y}, {x1: X1, y1: Y1, x2: X2 = X1, y2: Y2 = Y1}) { - const {curve, style} = this; return create("svg:path") - .call(applyStyles, style) + .call(applyStyles, this) .attr("d", shapeArea() - .curve(curve) + .curve(this.curve) .defined(i => defined(X1[i]) && defined(Y1[i]) && defined(X2[i]) && defined(Y2[i])) .x0(i => x(X1[i])) .y0(i => y(Y1[i])) diff --git a/src/marks/bar.js b/src/marks/bar.js index d9f88faab7..4c861ce74f 100644 --- a/src/marks/bar.js +++ b/src/marks/bar.js @@ -1,8 +1,8 @@ import {ascending} from "d3-array"; import {create} from "d3-selection"; import {defined} from "../defined.js"; -import {Mark, number, identity, indexOf, first, second} from "../mark.js"; -import {Style, applyIndirectStyles, applyDirectStyles} from "../style.js"; +import {Mark, number, identity, indexOf, first, second, maybeColor} from "../mark.js"; +import {Style, applyDirectStyles, applyIndirectStyles} from "../style.js"; class AbstractBar extends Mark { constructor( @@ -12,25 +12,27 @@ class AbstractBar extends Mark { z, fill, stroke, - style, insetTop = 0, insetRight = 0, insetBottom = 0, insetLeft = 0, - transform + transform, + ...style } = {} ) { + const [vfill, cfill] = maybeColor(fill); + const [vstroke, cstroke] = maybeColor(stroke); super( data, [ ...channels, {name: "z", value: z, optional: true}, - {name: "fill", value: fill, scale: "color", optional: true}, - {name: "stroke", value: stroke, scale: "color", optional: true} + {name: "fill", value: vfill, scale: "color", optional: true}, + {name: "stroke", value: vstroke, scale: "color", optional: true} ], transform ); - this.style = Style(style); + Object.assign(this, Style({fill: cfill, stroke: cstroke, ...style})); this.insetTop = number(insetTop); this.insetRight = number(insetRight); this.insetBottom = number(insetBottom); @@ -39,17 +41,16 @@ class AbstractBar extends Mark { render(I, scales, channels) { const {color} = scales; const {x: X, y: Y, z: Z, fill: F, stroke: S} = channels; - const {style} = this; let index = I.filter(i => defined(X[i]) && defined(Y[i])); if (F) index = index.filter(i => defined(F[i])); if (S) index = index.filter(i => defined(S[i])); if (Z) index.sort((i, j) => ascending(Z[i], Z[j])); return create("svg:g") - .call(applyIndirectStyles, style) + .call(applyIndirectStyles, this) .call(g => g.selectAll() .data(index) .join("rect") - .call(applyDirectStyles, style) + .call(applyDirectStyles, this) .attr("x", this._x(scales, channels)) .attr("width", this._width(scales, channels)) .attr("y", this._y(scales, channels)) diff --git a/src/marks/dot.js b/src/marks/dot.js index 9fe6037b18..e4676ccd43 100644 --- a/src/marks/dot.js +++ b/src/marks/dot.js @@ -1,8 +1,8 @@ import {ascending} from "d3-array"; import {create} from "d3-selection"; import {defined, nonempty} from "../defined.js"; -import {Mark, indexOf, identity, first, second, number} from "../mark.js"; -import {applyDirectStyles, applyIndirectStyles, Style} from "../style.js"; +import {Mark, indexOf, identity, first, second, maybeColor, maybeNumber} from "../mark.js"; +import {Style, applyDirectStyles, applyIndirectStyles} from "../style.js"; export class Dot extends Mark { constructor( @@ -15,51 +15,53 @@ export class Dot extends Mark { title, fill, stroke, - style = {}, - transform + transform, + ...style } = {} ) { + const [vr, cr = vr == null ? 3 : undefined] = maybeNumber(r); + const [vfill, cfill = vfill == null ? "none" : undefined] = maybeColor(fill); + const [vstroke, cstroke = vstroke == null && cfill === "none" ? "currentColor" : undefined] = maybeColor(stroke); super( data, [ {name: "x", value: x, scale: "x"}, {name: "y", value: y, scale: "y"}, {name: "z", value: z, optional: true}, - {name: "r", value: r, scale: "r", optional: true}, + {name: "r", value: vr, scale: "r", optional: true}, {name: "title", value: title, optional: true}, - {name: "fill", value: fill, scale: "color", optional: true}, - {name: "stroke", value: stroke, scale: "color", optional: true} + {name: "fill", value: vfill, scale: "color", optional: true}, + {name: "stroke", value: vstroke, scale: "color", optional: true} ], transform ); - this.style = Style({ - fill: fill === undefined ? "none" : undefined, - stroke: stroke === undefined && fill === undefined && style.fill === undefined ? "currentColor" : undefined, - strokeWidth: fill === undefined && style.fill === undefined ? 1.5 : undefined, + this.r = cr; + Object.assign(this, Style({ + fill: cfill, + stroke: cstroke, + strokeWidth: cstroke != null || vstroke != null ? 1.5 : undefined, ...style - }); - this.style.r = style.r === undefined ? 3 : number(style.r); + })); } render( I, {x, y, r, color}, {x: X, y: Y, z: Z, r: R, title: L, fill: F, stroke: S} ) { - const {style} = this; let index = I.filter(i => defined(X[i]) && defined(Y[i])); if (R) index = index.filter(i => defined(R[i])); if (F) index = index.filter(i => defined(F[i])); if (S) index = index.filter(i => defined(S[i])); if (Z) index.sort((i, j) => ascending(Z[i], Z[j])); return create("svg:g") - .call(applyIndirectStyles, style) + .call(applyIndirectStyles, this) .call(g => g.selectAll() .data(index) .join("circle") - .call(applyDirectStyles, style) + .call(applyDirectStyles, this) .attr("cx", i => x(X[i])) .attr("cy", i => y(Y[i])) - .attr("r", R ? i => r(R[i]) : style.r) + .attr("r", R ? i => r(R[i]) : this.r) .attr("fill", F && (i => color(F[i]))) .attr("stroke", S && (i => color(S[i]))) .call(L ? text => text diff --git a/src/marks/line.js b/src/marks/line.js index 6776522ff9..f09d621bad 100644 --- a/src/marks/line.js +++ b/src/marks/line.js @@ -4,7 +4,7 @@ import {line as shapeLine} from "d3-shape"; import {Curve} from "../curve.js"; import {defined} from "../defined.js"; import {Mark, indexOf, identity, first, second} from "../mark.js"; -import {Style, applyIndirectStyles, applyDirectStyles} from "../style.js"; +import {Style, applyDirectStyles, applyIndirectStyles} from "../style.js"; export class Line extends Mark { constructor( @@ -14,8 +14,8 @@ export class Line extends Mark { y, z, // optional grouping for multiple series curve, - style, - transform + transform, + ...style } = {} ) { super( @@ -28,23 +28,22 @@ export class Line extends Mark { transform ); this.curve = Curve(curve); - this.style = Style({ + Object.assign(this, Style({ fill: "none", stroke: "currentColor", strokeWidth: z ? 1 : 1.5, ...style - }); + })); } render(I, {x, y}, {x: X, y: Y, z: Z}) { - const {curve, style} = this; return create("svg:g") - .call(applyIndirectStyles, style) + .call(applyIndirectStyles, this) .call(g => g.selectAll() .data(Z ? group(I, i => Z[i]).values() : [I]) .join("path") - .call(applyDirectStyles, style) + .call(applyDirectStyles, this) .attr("d", shapeLine() - .curve(curve) + .curve(this.curve) .defined(i => defined(X[i]) && defined(Y[i])) .x(i => x(X[i])) .y(i => y(Y[i])))) diff --git a/src/marks/link.js b/src/marks/link.js index c1db807660..8e66f6ea42 100644 --- a/src/marks/link.js +++ b/src/marks/link.js @@ -1,8 +1,8 @@ import {ascending} from "d3-array"; import {create} from "d3-selection"; import {defined} from "../defined.js"; -import {Mark} from "../mark.js"; -import {Style, applyIndirectStyles, applyDirectStyles} from "../style.js"; +import {Mark, maybeColor} from "../mark.js"; +import {Style, applyDirectStyles, applyIndirectStyles} from "../style.js"; export class Link extends Mark { constructor( @@ -14,10 +14,11 @@ export class Link extends Mark { y2, z, stroke, - style, - transform + transform, + ...style } = {} ) { + const [vstroke, cstroke = vstroke == null ? "currentColor" : undefined] = maybeColor(stroke); super( data, [ @@ -26,27 +27,26 @@ export class Link extends Mark { {name: "x2", value: x2, scale: "x", label: x2.label}, {name: "y2", value: y2, scale: "y", label: y2.label}, {name: "z", value: z, optional: true}, - {name: "stroke", value: stroke, scale: "color", optional: true} + {name: "stroke", value: vstroke, scale: "color", optional: true} ], transform ); - this.style = Style({stroke: "currentColor", ...style}); + Object.assign(this, Style({stroke: cstroke, ...style})); } render( I, {x, y, color}, {x1: X1, y1: Y1, x2: X2, y2: Y2, z: Z, stroke: S} ) { - const {style} = this; let index = I.filter(i => defined(X1[i]) && defined(Y1[i]) && defined(X2[i]) && defined(Y2[i])); if (S) index = index.filter(i => defined(S[i])); if (Z) index.sort((i, j) => ascending(Z[i], Z[j])); return create("svg:g") - .call(applyIndirectStyles, style) + .call(applyIndirectStyles, this) .call(g => g.selectAll() .data(index) .join("line") - .call(applyDirectStyles, style) + .call(applyDirectStyles, this) .attr("x1", i => x(X1[i])) .attr("y1", i => y(Y1[i])) .attr("x2", i => x(X2[i])) diff --git a/src/marks/rect.js b/src/marks/rect.js index 9ba34daa28..932eb314c0 100644 --- a/src/marks/rect.js +++ b/src/marks/rect.js @@ -2,8 +2,8 @@ import {ascending} from "d3-array"; import {create} from "d3-selection"; import {zero} from "../mark.js"; import {defined} from "../defined.js"; -import {Mark, number} from "../mark.js"; -import {Style, applyIndirectStyles, applyDirectStyles} from "../style.js"; +import {Mark, number, maybeColor} from "../mark.js"; +import {Style, applyDirectStyles, applyIndirectStyles} from "../style.js"; export class Rect extends Mark { constructor( @@ -16,14 +16,16 @@ export class Rect extends Mark { z, fill, stroke, - style, insetTop = 0, insetRight = 0, insetBottom = 0, insetLeft = 0, - transform + transform, + ...style } = {} ) { + const [vfill, cfill] = maybeColor(fill); + const [vstroke, cstroke] = maybeColor(stroke); super( data, [ @@ -32,12 +34,12 @@ export class Rect extends Mark { {name: "x2", value: x2, scale: "x", label: x2.label}, {name: "y2", value: y2, scale: "y", label: y2.label}, {name: "z", value: z, optional: true}, - {name: "fill", value: fill, scale: "color", optional: true}, - {name: "stroke", value: stroke, scale: "color", optional: true} + {name: "fill", value: vfill, scale: "color", optional: true}, + {name: "stroke", value: vstroke, scale: "color", optional: true} ], transform ); - this.style = Style(style); + Object.assign(this, Style({fill: cfill, stroke: cstroke, ...style})); this.insetTop = number(insetTop); this.insetRight = number(insetRight); this.insetBottom = number(insetBottom); @@ -48,21 +50,20 @@ export class Rect extends Mark { {x, y, color}, {x1: X1, y1: Y1, x2: X2, y2: Y2, z: Z, fill: F, stroke: S} ) { - const {style, insetTop, insetRight, insetBottom, insetLeft} = this; let index = I.filter(i => defined(X1[i]) && defined(Y1[i]) && defined(X2[i]) && defined(Y2[i])); if (F) index = index.filter(i => defined(F[i])); if (S) index = index.filter(i => defined(S[i])); if (Z) index.sort((i, j) => ascending(Z[i], Z[j])); return create("svg:g") - .call(applyIndirectStyles, style) + .call(applyIndirectStyles, this) .call(g => g.selectAll() .data(index) .join("rect") - .call(applyDirectStyles, style) - .attr("x", i => Math.min(x(X1[i]), x(X2[i])) + insetLeft) - .attr("y", i => Math.min(y(Y1[i]), y(Y2[i])) + insetTop) - .attr("width", i => Math.max(0, Math.abs(x(X2[i]) - x(X1[i])) - insetLeft - insetRight)) - .attr("height", i => Math.max(0, Math.abs(y(Y1[i]) - y(Y2[i])) - insetTop - insetBottom)) + .call(applyDirectStyles, this) + .attr("x", i => Math.min(x(X1[i]), x(X2[i])) + this.insetLeft) + .attr("y", i => Math.min(y(Y1[i]), y(Y2[i])) + this.insetTop) + .attr("width", i => Math.max(0, Math.abs(x(X2[i]) - x(X1[i])) - this.insetLeft - this.insetRight)) + .attr("height", i => Math.max(0, Math.abs(y(Y1[i]) - y(Y2[i])) - this.insetTop - this.insetBottom)) .attr("fill", F && (i => color(F[i]))) .attr("stroke", S && (i => color(S[i])))) .node(); diff --git a/src/marks/rule.js b/src/marks/rule.js index eea5b124ee..095f41e438 100644 --- a/src/marks/rule.js +++ b/src/marks/rule.js @@ -1,8 +1,8 @@ import {ascending} from "d3-array"; import {create} from "d3-selection"; import {defined} from "../defined.js"; -import {Mark, identity} from "../mark.js"; -import {Style, applyStyles} from "../style.js"; +import {Mark, identity, maybeColor} from "../mark.js"; +import {Style, applyDirectStyles, applyIndirectStyles} from "../style.js"; export class RuleX extends Mark { constructor( @@ -13,10 +13,11 @@ export class RuleX extends Mark { y2, z, stroke, - style, - transform + transform, + ...style } = {} ) { + const [vstroke, cstroke = vstroke == null ? "currentColor" : undefined] = maybeColor(stroke); super( data, [ @@ -24,11 +25,11 @@ export class RuleX extends Mark { {name: "y1", value: y1, scale: "y", optional: true}, {name: "y2", value: y2, scale: "y", optional: true}, {name: "z", value: z, optional: true}, - {name: "stroke", value: stroke, scale: "color", optional: true} + {name: "stroke", value: vstroke, scale: "color", optional: true} ], transform ); - this.style = Style({stroke: "currentColor", ...style}); + Object.assign(this, Style({stroke: cstroke, ...style})); } render( I, @@ -36,17 +37,17 @@ export class RuleX extends Mark { {x: X, y1: Y1, y2: Y2, z: Z, stroke: S}, {marginTop, height, marginBottom} ) { - const {style} = this; let index = I.filter(i => defined(X[i])); if (Y1) index = index.filter(i => defined(Y1[i])); if (Y2) index = index.filter(i => defined(Y2[i])); if (S) index = index.filter(i => defined(S[i])); if (Z) index.sort((i, j) => ascending(Z[i], Z[j])); return create("svg:g") - .call(applyStyles, style) + .call(applyIndirectStyles, this) .call(g => g.selectAll("line") .data(index) .join("line") + .call(applyDirectStyles, this) .attr("x1", i => Math.round(x(X[i])) + 0.5) .attr("x2", i => Math.round(x(X[i])) + 0.5) .attr("y1", Y1 ? i => y(Y1[i]) : marginTop) @@ -65,10 +66,11 @@ export class RuleY extends Mark { y = identity, z, stroke, - style, - transform + transform, + ...style } = {} ) { + const [vstroke, cstroke = vstroke == null ? "currentColor" : undefined] = maybeColor(stroke); super( data, [ @@ -76,11 +78,11 @@ export class RuleY extends Mark { {name: "x1", value: x1, scale: "x", optional: true}, {name: "x2", value: x2, scale: "x", optional: true}, {name: "z", value: z, optional: true}, - {name: "stroke", value: stroke, scale: "color", optional: true} + {name: "stroke", value: vstroke, scale: "color", optional: true} ], transform ); - this.style = Style({stroke: "currentColor", ...style}); + Object.assign(this, Style({stroke: cstroke, ...style})); } render( I, @@ -88,16 +90,16 @@ export class RuleY extends Mark { {y: Y, x1: X1, x2: X2, z: Z, stroke: S}, {width, marginLeft, marginRight} ) { - const {style} = this; let index = I.filter(i => defined(Y[i])); if (X1) index = index.filter(i => defined(X1[i])); if (X2) index = index.filter(i => defined(X2[i])); if (Z) index.sort((i, j) => ascending(Z[i], Z[j])); return create("svg:g") - .call(applyStyles, style) + .call(applyIndirectStyles, this) .call(g => g.selectAll("line") .data(index) .join("line") + .call(applyDirectStyles, this) .attr("x1", X1 ? i => x(X1[i]) : marginLeft) .attr("x2", X2 ? i => x(X2[i]) : width - marginRight) .attr("y1", i => Math.round(y(Y[i])) + 0.5) diff --git a/src/marks/text.js b/src/marks/text.js index c9bd9771c1..2e58838d0d 100644 --- a/src/marks/text.js +++ b/src/marks/text.js @@ -1,8 +1,8 @@ import {ascending} from "d3-array"; import {create} from "d3-selection"; import {defined, nonempty} from "../defined.js"; -import {Mark, indexOf, identity, string} from "../mark.js"; -import {applyDirectStyles, applyIndirectStyles, applyAttr, Style} from "../style.js"; +import {Mark, indexOf, identity, string, maybeColor} from "../mark.js"; +import {Style, applyDirectStyles, applyIndirectStyles, applyAttr} from "../style.js"; const first = d => d[0]; const second = d => d[1]; @@ -17,10 +17,14 @@ export class Text extends Mark { text = indexOf, title, fill, - style = {}, - transform + transform, + textAnchor, + dx, + dy = "0.32em", + ...style } = {} ) { + const [vfill, cfill] = maybeColor(fill); super( data, [ @@ -29,34 +33,33 @@ export class Text extends Mark { {name: "z", value: z, optional: true}, {name: "text", value: text}, {name: "title", value: title, optional: true}, - {name: "fill", value: fill, scale: "color", optional: true} + {name: "fill", value: vfill, scale: "color", optional: true} ], transform ); - this.style = Style(style); - this.style.dx = string(style.dx); - this.style.dy = style.dy === undefined ? "0.32em" : string(style.dy); - this.style.textAnchor = string(style.textAnchor); + Object.assign(this, Style({fill: cfill, ...style})); + this.textAnchor = string(textAnchor); + this.dx = string(dx); + this.dy = string(dy); } render( I, {x, y, color}, {x: X, y: Y, z: Z, text: T, title: L, fill: F} ) { - const {style} = this; let index = I.filter(i => defined(X[i]) && defined(Y[i]) && nonempty(T[i])); if (F) index = index.filter(i => defined(F[i])); if (Z) index.sort((i, j) => ascending(Z[i], Z[j])); return create("svg:g") - .call(applyIndirectStyles, style) + .call(applyIndirectStyles, this) .attr("transform", `translate(${ x.bandwidth ? x.bandwidth() / 2 : 0},${ y.bandwidth ? y.bandwidth() / 2 : 0})`) .call(g => g.selectAll() .data(index) .join("text") - .call(applyDirectStyles, style) - .call(applyTextStyles, style) + .call(applyDirectStyles, this) + .call(applyTextStyles, this) .attr("x", i => x(X[i])) .attr("y", i => y(Y[i])) .attr("fill", F && (i => color(F[i]))) @@ -82,7 +85,7 @@ export function textY(data, {y = identity, ...options} = {}) { } function applyTextStyles(selection, style) { + applyAttr(selection, "text-anchor", style.textAnchor); applyAttr(selection, "dx", style.dx); applyAttr(selection, "dy", style.dy); - applyAttr(selection, "text-anchor", style.textAnchor); } diff --git a/test/aapl-candlestick.html b/test/aapl-candlestick.html index 3bf243f2c1..28cc04a0cb 100644 --- a/test/aapl-candlestick.html +++ b/test/aapl-candlestick.html @@ -30,10 +30,8 @@ y1: "Open", y2: "Close", stroke: d => Math.sign(d.Close - d.Open), - style: { - strokeWidth: 4, - strokeLinecap: "round" - } + strokeWidth: 4, + strokeLinecap: "round" }) ], width: 960 diff --git a/test/aapl-close.html b/test/aapl-close.html index 440c89016f..3caaa550d2 100644 --- a/test/aapl-close.html +++ b/test/aapl-close.html @@ -11,7 +11,7 @@ grid: true }, marks: [ - Plot.areaY(AAPL, {x: "Date", y: "Close", style: {fillOpacity: 0.1}}), + Plot.areaY(AAPL, {x: "Date", y: "Close", fillOpacity: 0.1}), Plot.line(AAPL, {x: "Date", y: "Close"}), Plot.ruleY([0]) ] diff --git a/test/cars-parcoords.html b/test/cars-parcoords.html index 5726777283..26927039c5 100644 --- a/test/cars-parcoords.html +++ b/test/cars-parcoords.html @@ -38,10 +38,8 @@ x: "value", y: "dimension", z: "name", - style: { - stroke: "#444", - strokeOpacity: 0.3 - } + stroke: "#444", + strokeOpacity: 0.3 }) ], marginLeft: 100 diff --git a/test/covid-ihme-projected-deaths.html b/test/covid-ihme-projected-deaths.html index 2d9fe4f91c..072b4bb781 100644 --- a/test/covid-ihme-projected-deaths.html +++ b/test/covid-ihme-projected-deaths.html @@ -24,9 +24,7 @@ x: "date", y1: d => Math.max(1, d.lower), // avoid zero y2: "upper", - style: { - fillOpacity: 0.2 - } + fillOpacity: 0.2 }), Plot.line(data.slice(0, i + 1), { x: "date", @@ -35,25 +33,19 @@ Plot.line(data.slice(i), { x: "date", y: "mean", - style: { - strokeDasharray: "2,2" - } + strokeDasharray: "2,2" }), Plot.dot([data[i]], { x: "date", y: "mean", - style: { - fill: "currentColor" - } + fill: "currentColor" }), Plot.text([data[i]], { x: "date", y: "mean", text: "mean", - style: { - textAnchor: "start", - dx: 6 - } + textAnchor: "start", + dx: 6 }) ] })); diff --git a/test/metro-inequality-change.html b/test/metro-inequality-change.html index 457e2334cc..c4ea71da43 100644 --- a/test/metro-inequality-change.html +++ b/test/metro-inequality-change.html @@ -32,14 +32,13 @@ Plot.dot(data, { x: "POP_2015", y: "R90_10_2015", - style: { - r: 1 - } + r: 1 }), Plot.text(data, { x: "POP_2015", y: "R90_10_2015", - text: d => d.highlight && d.nyt_display + text: d => d.highlight && d.nyt_display, + dy: -6 }) ] })); diff --git a/test/metro-unemployment-ridgeline.html b/test/metro-unemployment-ridgeline.html index 3e24f7cbae..ac468fa05f 100644 --- a/test/metro-unemployment-ridgeline.html +++ b/test/metro-unemployment-ridgeline.html @@ -25,7 +25,7 @@ {y: "division"}, [ Plot.ruleY([0]), - Plot.areaY(data, {x: "date", y: "unemployment", style: {fill: "#eee"}}), + Plot.areaY(data, {x: "date", y: "unemployment", fill: "#eee"}), Plot.line(data, {x: "date", y: "unemployment"}) ] ) diff --git a/test/sf-temperature-band.html b/test/sf-temperature-band.html index 0302758c88..9f63622596 100644 --- a/test/sf-temperature-band.html +++ b/test/sf-temperature-band.html @@ -15,9 +15,9 @@ label: "↑ Daily temperature range (°F)" }, marks: [ - Plot.areaY(temperatures, {x: "date", y1: "low", y2: "high", curve: "step", style: {fill: "#ccc"}}), - Plot.line(temperatures, {x: "date", y: movingAverage(temperatures, 7, d => d.low), curve: "step", style: {stroke: "blue"}}), - Plot.line(temperatures, {x: "date", y: movingAverage(temperatures, 7, d => d.high), curve: "step", style: {stroke: "red"}}) + Plot.areaY(temperatures, {x: "date", y1: "low", y2: "high", curve: "step", fill: "#ccc"}), + Plot.line(temperatures, {x: "date", y: movingAverage(temperatures, 7, d => d.low), curve: "step", stroke: "blue"}), + Plot.line(temperatures, {x: "date", y: movingAverage(temperatures, 7, d => d.high), curve: "step", stroke: "red"}) ], width: 960 })); diff --git a/test/travelers-year-over-year.html b/test/travelers-year-over-year.html index fec70ff7fe..f574ab9502 100644 --- a/test/travelers-year-over-year.html +++ b/test/travelers-year-over-year.html @@ -17,9 +17,7 @@ Plot.line(data, { x: d => d.date, y: d => d.previous / 1e6, - style: { - stroke: "#bab0ab" - } + stroke: "#bab0ab" }), Plot.line(data, { x: d => d.date, @@ -29,18 +27,14 @@ x: d => d.date, y: d => d.previous / 1e6, text: ["2019"], - style: { - fill: "#bab0ab", - dy: "-0.5em" - } + fill: "#bab0ab", + dy: "-0.5em" }), Plot.text(data.slice(0, 1), { x: d => d.date, y: d => d.current / 1e6, text: ["2020"], - style: { - dy: "-0.5em" - } + dy: "-0.5em" }) ] })); diff --git a/test/us-retail-sales.html b/test/us-retail-sales.html index cbd957191d..d9b187899a 100644 --- a/test/us-retail-sales.html +++ b/test/us-retail-sales.html @@ -20,16 +20,11 @@ Plot.line(data, { x: "Date", y: d => d.Sales / 1e3, - style: { - stroke: "#bab0ab" - } + stroke: "#bab0ab" }), Plot.line(data, { x: "Date", - y: d => d["Seasonally Adjusted Sales"] / 1e3, - style: { - stroke: "currentColor" - } + y: d => d["Seasonally Adjusted Sales"] / 1e3 }), Plot.ruleY([0]) ] diff --git a/yarn.lock b/yarn.lock index 0956b36ae6..b2535a3737 100644 --- a/yarn.lock +++ b/yarn.lock @@ -40,9 +40,9 @@ strip-json-comments "^3.1.1" "@types/node@*": - version "14.14.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.7.tgz#8ea1e8f8eae2430cf440564b98c6dfce1ec5945d" - integrity sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg== + version "14.14.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.10.tgz#5958a82e41863cfc71f2307b3748e3491ba03785" + integrity sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ== abab@^2.0.3: version "2.0.5" @@ -297,7 +297,7 @@ d3-axis@^2.0.0: resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-2.0.0.tgz#40aebb65626ffe6d95e9441fbf9194274b328a8b" integrity sha512-9nzB0uePtb+u9+dWir+HTuEAKJOEUYJoEwbJPsZ1B4K3iZUgzJcSENQ05Nj7S4CIfbZZ8/jQGoUzGKFznBhiiQ== -"d3-color@1 - 2": +"d3-color@1 - 2", d3-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-2.0.0.tgz#8d625cab42ed9b8f601a1760a389f7ea9189d62e" integrity sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ== @@ -379,9 +379,9 @@ data-urls@^2.0.0: whatwg-url "^8.0.0" debug@^4.0.1, debug@^4.1.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" - integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: ms "2.1.2" @@ -482,7 +482,7 @@ es-abstract@^1.17.0-next.1: string.prototype.trimend "^1.0.1" string.prototype.trimstart "^1.0.1" -es-abstract@^1.18.0-next.0, es-abstract@^1.18.0-next.1: +es-abstract@^1.18.0-next.1: version "1.18.0-next.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== @@ -552,9 +552,9 @@ eslint-visitor-keys@^2.0.0: integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== eslint@^7.12.1: - version "7.13.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.13.0.tgz#7f180126c0dcdef327bfb54b211d7802decc08da" - integrity sha512-uCORMuOO8tUzJmsdRtrvcGq5qposf7Rw0LwkTJkoDbOycVQtQjmnhZSuLQnozLE4TmAzlMVV45eCHmQ1OpDKUQ== + version "7.14.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.14.0.tgz#2d2cac1d28174c510a97b377f122a5507958e344" + integrity sha512-5YubdnPXrlrYAFCKybPuHIAH++PINe1pmKNc5wQRB9HSbqIK1ywAnntE3Wwua4giKu0bjligf1gLF6qxMGOYRA== dependencies: "@babel/code-frame" "^7.0.0" "@eslint/eslintrc" "^0.2.1" @@ -1058,6 +1058,13 @@ lodash@^4.17.14, lodash@^4.17.19: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -1115,9 +1122,9 @@ oauth-sign@~0.9.0: integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== object-inspect@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" - integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== + version "1.9.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" + integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== object-inspect@~1.7.0: version "1.7.0" @@ -1125,12 +1132,12 @@ object-inspect@~1.7.0: integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== object-is@^1.0.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.3.tgz#2e3b9e65560137455ee3bd62aec4d90a2ea1cc81" - integrity sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg== + version "1.1.4" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.4.tgz#63d6c83c00a43f4cbc9434eb9757c8a5b8565068" + integrity sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg== dependencies: + call-bind "^1.0.0" define-properties "^1.1.3" - es-abstract "^1.18.0-next.1" object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" @@ -1339,9 +1346,9 @@ rollup-plugin-terser@^7.0.2: terser "^5.0.0" rollup@^2.32.1: - version "2.33.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.33.1.tgz#802795164164ee63cd47769d8879c33ec8ae0c40" - integrity sha512-uY4O/IoL9oNW8MMcbA5hcOaz6tZTMIh7qJHx/tzIJm+n1wLoY38BLn6fuy7DhR57oNFLMbDQtDeJoFURt5933w== + version "2.34.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.34.0.tgz#ecc7f1d4ce2cb88bb51bec2f56b984f3c35b8271" + integrity sha512-dW5iLvttZzdVehjEuNJ1bWvuMEJjOWGmnuFS82WeKHTGXDkRHQeq/ExdifkSyJv9dLcR86ysKRmrIDyR6O0X8g== optionalDependencies: fsevents "~2.1.2" @@ -1363,9 +1370,11 @@ saxes@^5.0.0: xmlchars "^2.2.0" semver@^7.2.1: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + dependencies: + lru-cache "^6.0.0" serialize-javascript@^4.0.0: version "4.0.0" @@ -1448,28 +1457,29 @@ string-width@^3.0.0: strip-ansi "^5.1.0" string.prototype.trim@~1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.2.tgz#f538d0bacd98fc4297f0bef645226d5aaebf59f3" - integrity sha512-b5yrbl3BXIjHau9Prk7U0RRYcUYdN4wGSVaqoBQS50CCE3KBuYU0TYRNPFCP7aVoNMX87HKThdMRVIP3giclKg== + version "1.2.3" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.3.tgz#d23a22fde01c1e6571a7fadcb9be11decd8061a7" + integrity sha512-16IL9pIBA5asNOSukPfxX2W68BaBvxyiRK16H3RA/lWW9BDosh+w7f+LhomPHpXJ82QEe7w7/rY/S1CV97raLg== dependencies: + call-bind "^1.0.0" define-properties "^1.1.3" - es-abstract "^1.18.0-next.0" + es-abstract "^1.18.0-next.1" string.prototype.trimend@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz#6ddd9a8796bc714b489a3ae22246a208f37bfa46" - integrity sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw== + version "1.0.3" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz#a22bd53cca5c7cf44d7c9d5c732118873d6cd18b" + integrity sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw== dependencies: + call-bind "^1.0.0" define-properties "^1.1.3" - es-abstract "^1.18.0-next.1" string.prototype.trimstart@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz#22d45da81015309cd0cdd79787e8919fc5c613e7" - integrity sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg== + version "1.0.3" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa" + integrity sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg== dependencies: + call-bind "^1.0.0" define-properties "^1.1.3" - es-abstract "^1.18.0-next.1" strip-ansi@^5.1.0: version "5.2.0" @@ -1546,9 +1556,9 @@ tape@^4.13.3: through "~2.3.8" terser@^5.0.0: - version "5.3.8" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.8.tgz#991ae8ba21a3d990579b54aa9af11586197a75dd" - integrity sha512-zVotuHoIfnYjtlurOouTazciEfL7V38QMAOhGqpXDEg6yT13cF4+fEP9b0rrCEQTn+tT46uxgFsTZzhygk+CzQ== + version "5.5.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.5.1.tgz#540caa25139d6f496fdea056e414284886fb2289" + integrity sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ== dependencies: commander "^2.20.0" source-map "~0.7.2" @@ -1728,3 +1738,8 @@ xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==