diff --git a/package.json b/package.json index d8cebdcebc..2b6722e30a 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "snowpack": "3" }, "dependencies": { - "d3": "7", + "d3": "^7.3.0", "isoformat": "0.2" }, "engines": { diff --git a/src/legends.js b/src/legends.js index 8e3a564b5a..4e01df2ef2 100644 --- a/src/legends.js +++ b/src/legends.js @@ -1,10 +1,12 @@ +import {rgb} from "d3"; import {normalizeScale} from "./scales.js"; -import {legendColor} from "./legends/color.js"; -import {legendOpacity} from "./legends/opacity.js"; +import {legendRamp} from "./legends/ramp.js"; +import {legendSwatches, legendSymbols} from "./legends/swatches.js"; import {isObject} from "./mark.js"; const legendRegistry = new Map([ ["color", legendColor], + ["symbol", legendSymbols], ["opacity", legendOpacity] ]); @@ -12,7 +14,7 @@ export function legend(options = {}) { for (const [key, value] of legendRegistry) { const scale = options[key]; if (isObject(scale)) { // e.g., ignore {color: "red"} - return value(normalizeScale(key, scale), legendOptions(scale, options)); + return value(normalizeScale(key, scale), legendOptions(scale, options), key => isObject(options[key]) ? normalizeScale(key, options[key]) : null); } } throw new Error("unknown legend type"); @@ -22,7 +24,7 @@ export function exposeLegends(scales, defaults = {}) { return (key, options) => { if (!legendRegistry.has(key)) throw new Error(`unknown legend type: ${key}`); if (!(key in scales)) return; - return legendRegistry.get(key)(scales[key], legendOptions(defaults[key], options)); + return legendRegistry.get(key)(scales[key], legendOptions(defaults[key], options), key => scales[key]); }; } @@ -30,12 +32,40 @@ function legendOptions({label, ticks, tickFormat} = {}, options = {}) { return {label, ticks, tickFormat, ...options}; } +function legendColor(color, { + legend = true, + ...options +}) { + if (legend === true) legend = color.type === "ordinal" ? "swatches" : "ramp"; + switch (`${legend}`.toLowerCase()) { + case "swatches": return legendSwatches(color, options); + case "ramp": return legendRamp(color, options); + default: throw new Error(`unknown legend type: ${legend}`); + } +} + +function legendOpacity({type, interpolate, ...scale}, { + legend = true, + color = rgb(0, 0, 0), + ...options +}) { + if (!interpolate) throw new Error(`${type} opacity scales are not supported`); + if (legend === true) legend = "ramp"; + if (`${legend}`.toLowerCase() !== "ramp") throw new Error(`${legend} opacity legends are not supported`); + return legendColor({type, ...scale, interpolate: interpolateOpacity(color)}, {legend, ...options}); +} + +function interpolateOpacity(color) { + const {r, g, b} = rgb(color) || rgb(0, 0, 0); // treat invalid color as black + return t => `rgba(${r},${g},${b},${t})`; +} + export function Legends(scales, options) { const legends = []; for (const [key, value] of legendRegistry) { const o = options[key]; if (o && o.legend) { - legends.push(value(scales[key], legendOptions(scales[key], o))); + legends.push(value(scales[key], legendOptions(scales[key], o), key => scales[key])); } } return legends; diff --git a/src/legends/color.js b/src/legends/color.js deleted file mode 100644 index c9d87170a3..0000000000 --- a/src/legends/color.js +++ /dev/null @@ -1,14 +0,0 @@ -import {legendRamp} from "./ramp.js"; -import {legendSwatches} from "./swatches.js"; - -export function legendColor(color, { - legend = true, - ...options -}) { - if (legend === true) legend = color.type === "ordinal" ? "swatches" : "ramp"; - switch (`${legend}`.toLowerCase()) { - case "swatches": return legendSwatches(color, options); - case "ramp": return legendRamp(color, options); - default: throw new Error(`unknown legend type: ${legend}`); - } -} diff --git a/src/legends/opacity.js b/src/legends/opacity.js deleted file mode 100644 index 7393c36289..0000000000 --- a/src/legends/opacity.js +++ /dev/null @@ -1,20 +0,0 @@ -import {rgb} from "d3"; -import {legendColor} from "./color.js"; - -const black = rgb(0, 0, 0); - -export function legendOpacity({type, interpolate, ...scale}, { - legend = true, - color = black, - ...options -}) { - if (!interpolate) throw new Error(`${type} opacity scales are not supported`); - if (legend === true) legend = "ramp"; - if (`${legend}`.toLowerCase() !== "ramp") throw new Error(`${legend} opacity legends are not supported`); - return legendColor({type, ...scale, interpolate: interpolateOpacity(color)}, {legend, ...options}); -} - -function interpolateOpacity(color) { - const {r, g, b} = rgb(color) || black; // treat invalid color as black - return t => `rgba(${r},${g},${b},${t})`; -} diff --git a/src/legends/swatches.js b/src/legends/swatches.js index 4c6a8440d2..98185579e0 100644 --- a/src/legends/swatches.js +++ b/src/legends/swatches.js @@ -1,12 +1,79 @@ -import {create} from "d3"; +import {create, path} from "d3"; import {inferFontVariant} from "../axes.js"; import {maybeTickFormat} from "../axis.js"; -import {applyInlineStyles, impliedString, maybeClassName} from "../style.js"; +import {maybeColorChannel, maybeNumberChannel} from "../mark.js"; +import {applyInlineStyles, impliedString, maybeClassName, none} from "../style.js"; -export function legendSwatches(color, { +function maybeScale(scale, key) { + if (key == null) return key; + const s = scale(key); + if (!s) throw new Error(`scale not found: ${key}`); + return s; +} + +export function legendSwatches(color, options) { + return legendItems( + color, + options, + selection => selection.style("--color", color.scale), + className => `.${className}-swatch::before { + content: ""; + width: var(--swatchWidth); + height: var(--swatchHeight); + margin-right: 0.5em; + background: var(--color); + }` + ); +} + +export function legendSymbols(symbol, { + fill = symbol.hint?.fill !== undefined ? symbol.hint.fill : "none", + fillOpacity = 1, + stroke = symbol.hint?.stroke !== undefined ? symbol.hint.stroke : none(fill) ? "currentColor" : "none", + strokeOpacity = 1, + strokeWidth = 1.5, + r = 4.5, + ...options +} = {}, scale) { + const [vf, cf] = maybeColorChannel(fill); + const [vs, cs] = maybeColorChannel(stroke); + const sf = maybeScale(scale, vf); + const ss = maybeScale(scale, vs); + const size = r * r * Math.PI; + fillOpacity = maybeNumberChannel(fillOpacity)[1]; + strokeOpacity = maybeNumberChannel(strokeOpacity)[1]; + strokeWidth = maybeNumberChannel(strokeWidth)[1]; + return legendItems( + symbol, + options, + selection => selection.append("svg") + .attr("viewBox", "-8 -8 16 16") + .attr("fill", vf === "color" ? d => sf.scale(d) : null) + .attr("stroke", vs === "color" ? d => ss.scale(d) : null) + .append("path") + .attr("d", d => { + const p = path(); + symbol.scale(d).draw(p, size); + return p; + }), + className => `.${className}-swatch > svg { + width: var(--swatchWidth); + height: var(--swatchHeight); + margin-right: 0.5em; + overflow: visible; + fill: ${cf}; + fill-opacity: ${fillOpacity}; + stroke: ${cs}; + stroke-width: ${strokeWidth}px; + stroke-opacity: ${strokeOpacity}; + }` + ); +} + +function legendItems(scale, { columns, tickFormat, - fontVariant = inferFontVariant(color), + fontVariant = inferFontVariant(scale), // TODO label, swatchSize = 15, swatchWidth = swatchSize, @@ -15,9 +82,9 @@ export function legendSwatches(color, { className, style, width -} = {}) { +} = {}, swatch, swatchStyle) { className = maybeClassName(className); - tickFormat = maybeTickFormat(tickFormat, color.domain); + tickFormat = maybeTickFormat(tickFormat, scale.domain); const swatches = create("div") .attr("class", className) @@ -49,10 +116,10 @@ export function legendSwatches(color, { swatches .style("columns", columns) .selectAll() - .data(color.domain) + .data(scale.domain) .join("div") .attr("class", `${className}-swatch`) - .style("--color", color.scale) + .call(swatch, scale) .call(item => item.append("div") .attr("class", `${className}-label`) .attr("title", tickFormat) @@ -74,11 +141,13 @@ export function legendSwatches(color, { swatches .selectAll() - .data(color.domain) + .data(scale.domain) .join("span") .attr("class", `${className}-swatch`) - .style("--color", color.scale) - .text(tickFormat); + .call(swatch, scale) + .append(function() { + return document.createTextNode(tickFormat.apply(this, arguments)); + }); } return swatches @@ -90,13 +159,7 @@ export function legendSwatches(color, { margin-left: ${+marginLeft}px;`}${width === undefined ? "" : ` width: ${width}px;`} } - .${className}-swatch::before { - content: ""; - width: var(--swatchWidth); - height: var(--swatchHeight); - margin-right: 0.5em; - background: var(--color); - } + ${swatchStyle(className)} ${extraStyle} `)) .style("font-variant", impliedString(fontVariant, "normal")) diff --git a/src/mark.js b/src/mark.js index adf0cd06a5..7996b1a420 100644 --- a/src/mark.js +++ b/src/mark.js @@ -58,12 +58,13 @@ export class Mark { } // TODO Type coercion? -function Channel(data, {scale, type, value}) { +function Channel(data, {scale, type, value, hint}) { return { scale, type, value: valueof(data, value), - label: labelof(value) + label: labelof(value), + hint }; } @@ -134,16 +135,16 @@ const colors = new Set(["currentColor", "none"]); // 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, defaultValue) { +export function maybeColorChannel(value, defaultValue) { if (value === undefined) value = defaultValue; return value === null ? [undefined, "none"] : typeof value === "string" && (colors.has(value) || color(value)) ? [undefined, value] : [value, undefined]; } -// Similar to maybeColor, this tests whether the given value is a number +// Similar to maybeColorChannel, 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, defaultValue) { +export function maybeNumberChannel(value, defaultValue) { if (value === undefined) value = defaultValue; return value === null || typeof value === "number" ? [undefined, value] : [value, undefined]; @@ -202,8 +203,8 @@ export function maybeTuple(x, y) { // A helper for extracting the z channel, if it is variable. Used by transforms // that require series, such as moving average and normalize. export function maybeZ({z, fill, stroke} = {}) { - if (z === undefined) ([z] = maybeColor(fill)); - if (z === undefined) ([z] = maybeColor(stroke)); + if (z === undefined) ([z] = maybeColorChannel(fill)); + if (z === undefined) ([z] = maybeColorChannel(stroke)); return z; } diff --git a/src/marks/cell.js b/src/marks/cell.js index 39611ed573..04acb9cdd2 100644 --- a/src/marks/cell.js +++ b/src/marks/cell.js @@ -1,4 +1,4 @@ -import {identity, indexOf, maybeColor, maybeTuple} from "../mark.js"; +import {identity, indexOf, maybeColorChannel, maybeTuple} from "../mark.js"; import {AbstractBar} from "./bar.js"; export class Cell extends AbstractBar { @@ -26,11 +26,11 @@ export function cell(data, {x, y, ...options} = {}) { } export function cellX(data, {x = indexOf, fill, stroke, ...options} = {}) { - if (fill === undefined && maybeColor(stroke)[0] === undefined) fill = identity; + if (fill === undefined && maybeColorChannel(stroke)[0] === undefined) fill = identity; return new Cell(data, {...options, x, fill, stroke}); } export function cellY(data, {y = indexOf, fill, stroke, ...options} = {}) { - if (fill === undefined && maybeColor(stroke)[0] === undefined) fill = identity; + if (fill === undefined && maybeColorChannel(stroke)[0] === undefined) fill = identity; return new Cell(data, {...options, y, fill, stroke}); } diff --git a/src/marks/dot.js b/src/marks/dot.js index 6f11c623ac..9c460aba6c 100644 --- a/src/marks/dot.js +++ b/src/marks/dot.js @@ -1,6 +1,7 @@ -import {create} from "d3"; +import {create, path, symbolCircle} from "d3"; import {filter, positive} from "../defined.js"; -import {Mark, identity, maybeNumber, maybeTuple} from "../mark.js"; +import {Mark, identity, maybeNumberChannel, maybeTuple} from "../mark.js"; +import {maybeSymbolChannel} from "../scales/symbol.js"; import {applyChannelStyles, applyDirectStyles, applyIndirectStyles, applyTransform, offset} from "../style.js"; const defaults = { @@ -11,19 +12,39 @@ const defaults = { export class Dot extends Mark { constructor(data, options = {}) { - const {x, y, r} = options; - const [vr, cr] = maybeNumber(r, 3); + const {x, y, r, rotate, symbol = symbolCircle} = options; + const [vrotate, crotate] = maybeNumberChannel(rotate, 0); + const [vsymbol, csymbol] = maybeSymbolChannel(symbol); + const [vr, cr] = maybeNumberChannel(r, vsymbol == null ? 3 : 4.5); super( data, [ {name: "x", value: x, scale: "x", optional: true}, {name: "y", value: y, scale: "y", optional: true}, - {name: "r", value: vr, scale: "r", optional: true} + {name: "r", value: vr, scale: "r", optional: true}, + {name: "rotate", value: vrotate, optional: true}, + {name: "symbol", value: vsymbol, scale: "symbol", optional: true} ], options, defaults ); this.r = cr; + this.rotate = crotate; + this.symbol = csymbol; + + // Give a hint to the symbol scale; this allows the symbol scale to chose + // appropriate default symbols based on whether the dots are filled or + // stroked, and for the symbol legend to match the appearance of the dots. + const {channels} = this; + const symbolChannel = channels.find(({scale}) => scale === "symbol"); + if (symbolChannel) { + const fillChannel = channels.find(({name}) => name === "fill"); + const strokeChannel = channels.find(({name}) => name === "stroke"); + symbolChannel.hint = { + fill: fillChannel?.value === symbolChannel.value ? "color" : this.fill, + stroke: strokeChannel?.value === symbolChannel.value ? "color" : this.stroke + }; + } } render( I, @@ -31,20 +52,42 @@ export class Dot extends Mark { channels, {width, height, marginTop, marginRight, marginBottom, marginLeft} ) { - const {x: X, y: Y, r: R} = channels; + const {x: X, y: Y, r: R, rotate: A, symbol: S} = channels; const {dx, dy} = this; - let index = filter(I, X, Y); + const cx = (marginLeft + width - marginRight) / 2; + const cy = (marginTop + height - marginBottom) / 2; + let index = filter(I, X, Y, A, S); if (R) index = index.filter(i => positive(R[i])); + const circle = this.symbol === symbolCircle; return create("svg:g") .call(applyIndirectStyles, this) .call(applyTransform, x, y, offset + dx, offset + dy) .call(g => g.selectAll() .data(index) - .join("circle") + .join(circle ? "circle" : "path") .call(applyDirectStyles, this) - .attr("cx", X ? i => X[i] : (marginLeft + width - marginRight) / 2) - .attr("cy", Y ? i => Y[i] : (marginTop + height - marginBottom) / 2) - .attr("r", R ? i => R[i] : this.r) + .call(circle + ? selection => { + selection + .attr("cx", X ? i => X[i] : cx) + .attr("cy", Y ? i => Y[i] : cy) + .attr("r", R ? i => R[i] : this.r); + } + : selection => { + const translate = X && Y ? i => `translate(${X[i]},${Y[i]})` + : X ? i => `translate(${X[i]},${cy})` + : Y ? i => `translate(${cx},${X[i]})` + : () => `translate(${cx},${cy})`; + selection + .attr("transform", A ? i => `${translate(i)} rotate(${A[i]})` + : this.rotate ? i => `${translate(i)} rotate(${this.rotate})` + : translate) + .attr("d", i => { + const p = path(), r = R ? R[i] : this.r; + (S ? S[i] : this.symbol).draw(p, r * r * Math.PI); + return p; + }); + }) .call(applyChannelStyles, this, channels)) .node(); } diff --git a/src/marks/image.js b/src/marks/image.js index 302b59b535..dd037de450 100644 --- a/src/marks/image.js +++ b/src/marks/image.js @@ -1,6 +1,6 @@ import {create} from "d3"; import {filter, positive} from "../defined.js"; -import {Mark, maybeNumber, maybeTuple, string} from "../mark.js"; +import {Mark, maybeNumberChannel, maybeTuple, string} from "../mark.js"; import {applyChannelStyles, applyDirectStyles, applyIndirectStyles, applyTransform, applyAttr, offset, impliedString} from "../style.js"; const defaults = { @@ -24,7 +24,7 @@ function isUrl(string) { // Disambiguates a constant src definition from a channel. A path or URL string // is assumed to be a constant; any other string is assumed to be a field name. -function maybePath(value) { +function maybePathChannel(value) { return typeof value === "string" && (isPath(value) || isUrl(value)) ? [undefined, value] : [value, undefined]; @@ -35,9 +35,9 @@ export class Image extends Mark { let {x, y, width, height, src, preserveAspectRatio, crossOrigin} = options; if (width === undefined && height !== undefined) width = height; else if (height === undefined && width !== undefined) height = width; - const [vs, cs] = maybePath(src); - const [vw, cw] = maybeNumber(width, 16); - const [vh, ch] = maybeNumber(height, 16); + const [vs, cs] = maybePathChannel(src); + const [vw, cw] = maybeNumberChannel(width, 16); + const [vh, ch] = maybeNumberChannel(height, 16); super( data, [ diff --git a/src/marks/text.js b/src/marks/text.js index 66b1f1704f..d344329e25 100644 --- a/src/marks/text.js +++ b/src/marks/text.js @@ -1,6 +1,6 @@ import {create} from "d3"; import {filter, nonempty} from "../defined.js"; -import {Mark, indexOf, identity, string, maybeNumber, maybeTuple, numberChannel, isNumeric, isTemporal} from "../mark.js"; +import {Mark, indexOf, identity, string, maybeNumberChannel, maybeTuple, numberChannel, isNumeric, isTemporal} from "../mark.js"; import {applyChannelStyles, applyDirectStyles, applyIndirectStyles, applyAttr, applyText, applyTransform, offset} from "../style.js"; const defaults = { @@ -23,8 +23,8 @@ export class Text extends Mark { dy = "0.32em", rotate } = options; - const [vrotate, crotate] = maybeNumber(rotate, 0); - const [vfontSize, cfontSize] = maybeNumber(fontSize); + const [vrotate, crotate] = maybeNumberChannel(rotate, 0); + const [vfontSize, cfontSize] = maybeNumberChannel(fontSize); super( data, [ diff --git a/src/marks/vector.js b/src/marks/vector.js index 741389f87a..20cd0b14ea 100644 --- a/src/marks/vector.js +++ b/src/marks/vector.js @@ -1,6 +1,6 @@ import {create} from "d3"; import {filter} from "../defined.js"; -import {Mark, identity, maybeNumber, maybeTuple, keyword} from "../mark.js"; +import {Mark, identity, maybeNumberChannel, maybeTuple, keyword} from "../mark.js"; import {radians} from "../math.js"; import {applyChannelStyles, applyDirectStyles, applyIndirectStyles, applyTransform, offset} from "../style.js"; @@ -14,8 +14,8 @@ const defaults = { export class Vector extends Mark { constructor(data, options = {}) { const {x, y, length, rotate, anchor = "middle"} = options; - const [vl, cl] = maybeNumber(length, 12); - const [vr, cr] = maybeNumber(rotate, 0); + const [vl, cl] = maybeNumberChannel(length, 12); + const [vr, cr] = maybeNumberChannel(rotate, 0); super( data, [ diff --git a/src/scales.js b/src/scales.js index 3e4c5d8f21..c8332eaeaf 100644 --- a/src/scales.js +++ b/src/scales.js @@ -1,6 +1,6 @@ import {descending} from "d3"; import {parse as isoParse} from "isoformat"; -import {registry, color, position, radius, opacity} from "./scales/index.js"; +import {registry, color, position, radius, opacity, symbol} from "./scales/index.js"; import {ScaleLinear, ScaleSqrt, ScalePow, ScaleLog, ScaleSymlog, ScaleQuantile, ScaleThreshold, ScaleIdentity} from "./scales/quantitative.js"; import {ScaleDiverging, ScaleDivergingSqrt, ScaleDivergingPow, ScaleDivergingLog, ScaleDivergingSymlog} from "./scales/diverging.js"; import {ScaleTime, ScaleUtc} from "./scales/temporal.js"; @@ -196,6 +196,7 @@ function inferScaleType(key, channels, {type, domain, range}) { } if (registry.get(key) === radius) return "sqrt"; if (registry.get(key) === opacity) return "linear"; + if (registry.get(key) === symbol) return "ordinal"; for (const {type} of channels) if (type !== undefined) return type; if ((domain || range || []).length > 2) return asOrdinalType(key); if (domain !== undefined) { diff --git a/src/scales/index.js b/src/scales/index.js index d2ec0fd647..80c16410f9 100644 --- a/src/scales/index.js +++ b/src/scales/index.js @@ -15,6 +15,9 @@ export const radius = Symbol("radius"); // the maximum value of associated channels. export const opacity = Symbol("opacity"); +// Symbol scales have a default range of d3.symbols. +export const symbol = Symbol("symbol"); + // TODO Rather than hard-coding the list of known scale names, collect the names // and categories for each plot specification, so that custom marks can register // custom scales. @@ -25,5 +28,6 @@ export const registry = new Map([ ["fy", position], ["r", radius], ["color", color], - ["opacity", opacity] + ["opacity", opacity], + ["symbol", symbol] ]); diff --git a/src/scales/ordinal.js b/src/scales/ordinal.js index c1858462a3..7cd547f40e 100644 --- a/src/scales/ordinal.js +++ b/src/scales/ordinal.js @@ -1,14 +1,17 @@ -import {InternSet, quantize, reverse as reverseof, sort} from "d3"; +import {InternSet, quantize, reverse as reverseof, sort, symbolsFill, symbolsStroke} from "d3"; import {scaleBand, scaleOrdinal, scalePoint, scaleImplicit} from "d3"; -import {ordinalScheme, quantitativeScheme} from "./schemes.js"; import {ascendingDefined} from "../defined.js"; -import {registry, color} from "./index.js"; +import {none} from "../style.js"; +import {registry, color, symbol} from "./index.js"; +import {ordinalScheme, quantitativeScheme} from "./schemes.js"; +import {maybeSymbol} from "./symbol.js"; export function ScaleO(scale, channels, { type, domain = inferDomain(channels), range, - reverse + reverse, + hint }) { if (type === "categorical") type = "ordinal"; // shorthand for color schemes if (reverse) domain = reverseof(domain); @@ -18,7 +21,7 @@ export function ScaleO(scale, channels, { if (typeof range === "function") range = range(domain); scale.range(range); } - return {type, domain, range, scale}; + return {type, domain, range, scale, hint}; } export function ScaleOrdinal(key, channels, { @@ -28,7 +31,11 @@ export function ScaleOrdinal(key, channels, { unknown, ...options }) { - if (registry.get(key) === color && scheme !== undefined) { + let hint; + if (registry.get(key) === symbol) { + hint = inferSymbolHint(channels); + range = range === undefined ? inferSymbolRange(hint) : Array.from(range, maybeSymbol); + } else if (registry.get(key) === color && scheme !== undefined) { if (range !== undefined) { const interpolate = quantitativeScheme(scheme); const t0 = range[0], d = range[1] - range[0]; @@ -38,7 +45,7 @@ export function ScaleOrdinal(key, channels, { } } if (unknown === scaleImplicit) throw new Error("implicit unknown is not supported"); - return ScaleO(scaleOrdinal().unknown(unknown), channels, {type, range, ...options}); + return ScaleO(scaleOrdinal().unknown(unknown), channels, {...options, type, range, hint}); } export function ScalePoint(key, channels, { @@ -89,3 +96,20 @@ function inferDomain(channels) { } return sort(values, ascendingDefined); } + +// If all channels provide a consistent hint, propagate it to the scale. +function inferSymbolHint(channels) { + const hint = {}; + for (const {hint: channelHint} of channels) { + for (const key of ["fill", "stroke"]) { + const value = channelHint[key]; + if (!(key in hint)) hint[key] = value; + else if (hint[key] !== value) hint[key] = undefined; + } + } + return hint; +} + +function inferSymbolRange(hint) { + return none(hint.fill) ? symbolsStroke : symbolsFill; +} diff --git a/src/scales/symbol.js b/src/scales/symbol.js new file mode 100644 index 0000000000..8e91341697 --- /dev/null +++ b/src/scales/symbol.js @@ -0,0 +1,38 @@ +import {symbolAsterisk, symbolDiamond2, symbolPlus, symbolSquare2, symbolTriangle2, symbolX} from "d3"; +import {symbolCircle, symbolCross, symbolDiamond, symbolSquare, symbolStar, symbolTriangle, symbolWye} from "d3"; + +const symbols = new Map([ + ["asterisk", symbolAsterisk], + ["circle", symbolCircle], + ["cross", symbolCross], + ["diamond", symbolDiamond], + ["diamond2", symbolDiamond2], + ["plus", symbolPlus], + ["square", symbolSquare], + ["square2", symbolSquare2], + ["star", symbolStar], + ["triangle", symbolTriangle], + ["triangle2", symbolTriangle2], + ["wye", symbolWye], + ["x", symbolX] +]); + +function isSymbol(symbol) { + return symbol && typeof symbol.draw === "function"; +} + +export function maybeSymbol(symbol) { + if (symbol == null || isSymbol(symbol)) return symbol; + const value = symbols.get(`${symbol}`.toLowerCase()); + if (value) return value; + throw new Error(`invalid symbol: ${symbol}`); +} + +export function maybeSymbolChannel(symbol) { + if (symbol == null || isSymbol(symbol)) return [undefined, symbol]; + if (typeof symbol === "string") { + const value = symbols.get(`${symbol}`.toLowerCase()); + if (value) return [undefined, value]; + } + return [symbol, undefined]; +} diff --git a/src/style.js b/src/style.js index c46fd16fc4..d8a3b310f3 100644 --- a/src/style.js +++ b/src/style.js @@ -1,5 +1,5 @@ import {isoFormat, namespaces} from "d3"; -import {string, number, maybeColor, maybeNumber, isTemporal, isNumeric} from "./mark.js"; +import {string, number, maybeColorChannel, maybeNumberChannel, isTemporal, isNumeric} from "./mark.js"; import {filter, nonempty} from "./defined.js"; import {formatNumber} from "./format.js"; @@ -59,11 +59,11 @@ export function styles( if (none(defaultStroke) && !none(stroke)) defaultFill = "none"; } - const [vfill, cfill] = maybeColor(fill, defaultFill); - const [vfillOpacity, cfillOpacity] = maybeNumber(fillOpacity); - const [vstroke, cstroke] = maybeColor(stroke, defaultStroke); - const [vstrokeOpacity, cstrokeOpacity] = maybeNumber(strokeOpacity); - const [vopacity, copacity] = maybeNumber(opacity); + const [vfill, cfill] = maybeColorChannel(fill, defaultFill); + const [vfillOpacity, cfillOpacity] = maybeNumberChannel(fillOpacity); + const [vstroke, cstroke] = maybeColorChannel(stroke, defaultStroke); + const [vstrokeOpacity, cstrokeOpacity] = maybeNumberChannel(strokeOpacity); + const [vopacity, copacity] = maybeNumberChannel(opacity); // For styles that have no effect if there is no stroke, only apply the // defaults if the stroke is not (constant) none. @@ -74,7 +74,7 @@ export function styles( if (strokeMiterlimit === undefined) strokeMiterlimit = defaultStrokeMiterlimit; } - const [vstrokeWidth, cstrokeWidth] = maybeNumber(strokeWidth); + const [vstrokeWidth, cstrokeWidth] = maybeNumberChannel(strokeWidth); // Some marks don’t support fill (e.g., tick and rule). if (defaultFill !== null) { @@ -207,7 +207,7 @@ export function filterStyles(index, {fill: F, fillOpacity: FO, stroke: S, stroke return filter(index, F, FO, S, SO, SW); } -function none(color) { +export function none(color) { return color == null || color === "none"; } diff --git a/src/transforms/bin.js b/src/transforms/bin.js index 4e87b89ad4..e080e0d793 100644 --- a/src/transforms/bin.js +++ b/src/transforms/bin.js @@ -1,5 +1,5 @@ import {bin as binner, extent, thresholdFreedmanDiaconis, thresholdScott, thresholdSturges, utcTickInterval} from "d3"; -import {valueof, range, identity, maybeLazyChannel, maybeTuple, maybeColor, maybeValue, mid, labelof, isTemporal} from "../mark.js"; +import {valueof, range, identity, maybeLazyChannel, maybeTuple, maybeColorChannel, maybeValue, mid, labelof, isTemporal} from "../mark.js"; import {coerceDate} from "../scales.js"; import {basic} from "./basic.js"; import {hasOutput, maybeEvaluator, maybeGroup, maybeOutput, maybeOutputs, maybeReduce, maybeSort, maybeSubgroup, reduceCount, reduceIdentity} from "./group.js"; @@ -81,8 +81,8 @@ function binn( ...options } = inputs; const [GZ, setGZ] = maybeLazyChannel(z); - const [vfill] = maybeColor(fill); - const [vstroke] = maybeColor(stroke); + const [vfill] = maybeColorChannel(fill); + const [vstroke] = maybeColorChannel(stroke); const [GF = fill, setGF] = maybeLazyChannel(vfill); const [GS = stroke, setGS] = maybeLazyChannel(vstroke); diff --git a/src/transforms/group.js b/src/transforms/group.js index d61628e992..fbc3a00648 100644 --- a/src/transforms/group.js +++ b/src/transforms/group.js @@ -1,6 +1,6 @@ import {group as grouper, sort, sum, deviation, min, max, mean, median, mode, variance, InternSet, minIndex, maxIndex} from "d3"; import {ascendingDefined, firstof} from "../defined.js"; -import {valueof, maybeColor, maybeInput, maybeTuple, maybeLazyChannel, lazyChannel, first, identity, take, labelof, range} from "../mark.js"; +import {valueof, maybeColorChannel, maybeInput, maybeTuple, maybeLazyChannel, lazyChannel, first, identity, take, labelof, range} from "../mark.js"; import {basic} from "./basic.js"; // Group on {z, fill, stroke}. @@ -66,8 +66,8 @@ function groupn( ...options } = inputs; const [GZ, setGZ] = maybeLazyChannel(z); - const [vfill] = maybeColor(fill); - const [vstroke] = maybeColor(stroke); + const [vfill] = maybeColorChannel(fill); + const [vstroke] = maybeColorChannel(stroke); const [GF = fill, setGF] = maybeLazyChannel(vfill); const [GS = stroke, setGS] = maybeLazyChannel(vstroke); diff --git a/test/data/README.md b/test/data/README.md index 5baf12df93..790dd6e31e 100644 --- a/test/data/README.md +++ b/test/data/README.md @@ -35,6 +35,10 @@ https://covid19.healthdata.org/ D3 Community Survey, 2015 https://github.com/enjalot/d3surveys +## decathlon.csv +Sample of decathlon dataset from JMP Statistical Discovery (http://www.jmp.com). +https://github.com/hemanrobinson/preattentive/blob/a58dd4795d0ee063a38a2d7bf33812d969ca6256/src/Data.js#L5598-L5650 + ## diamonds.csv ggplot2 “diamonds” dataset (carat and price columns only) https://github.com/tidyverse/ggplot2/blob/master/data-raw/diamonds.csv diff --git a/test/data/decathlon.csv b/test/data/decathlon.csv new file mode 100644 index 0000000000..a17b3fb6dc --- /dev/null +++ b/test/data/decathlon.csv @@ -0,0 +1,51 @@ +Country,100 Meters,Long Jump,High Jump,100 Meter Hurdles +USA,10.43,8.08,2.07,13.98 +GBR,10.44,8.01,2.03,14.33 +USA,10.23,7.96,2.08,13.95 +FRG,10.7,7.76,2.07,14.07 +FRG,10.92,7.74,2.15,14.1 +USA,10.5,7.57,2.07,13.87 +USA,10.57,7.99,2.03,14.08 +USA,10.41,7.9,1.91,13.94 +GBR,10.26,7.72,2,14.04 +DDR,11.06,7.79,2.03,14.66 +GBR,10.51,7.8,2.03,14.39 +FRG,10.89,7.49,2.09,14 +FRG,10.74,7.85,2.15,14.64 +BLS,10.5,7.26,2.11,13.82 +GBR,10.5,7.95,2.08,14.31 +FRG,10.87,7.89,2.12,14.52 +USA,10.32,7.46,2.08,14.04 +BLS,10.72,7.05,2.09,13.57 +USA,10.49,7.81,2.2,13.81 +GBR,10.6,7.88,2.03,14.37 +USA,10.43,7.75,2.06,13.91 +SOV,10.96,7.57,1.97,13.93 +USA,10.31,7.81,2.17,13.98 +USA,10.96,7.43,2.04,14.17 +SOV,10.87,7.42,2.1,14.53 +FRG,10.91,7.8,2.12,14.29 +USA,10.57,7.55,2.13,13.78 +USA,10.36,7.82,2.09,13.84 +DDR,10.69,7.88,2.1,14.13 +FRG,10.83,7.6,2.12,14.07 +FRG,10.58,7.8,2,13.92 +GBR,10.56,7.81,1.98,14.35 +GBR,10.37,7.7,2.08,14.22 +USA,11.18,7.27,2,14.44 +GBR,10.55,7.72,2.11,13.92 +FRG,10.85,7.49,2.05,13.96 +USA,10.9,7.77,2.04,13.97 +USA,10.9,7.77,2.04,13.97 +USA,10.94,7.22,2.03,14.8 +TCH,10.62,8.02,2.05,13.84 +SOV,11.05,7.73,2.08,14.45 +DDR,11.1,7.79,1.94,14.54 +BLS,10.85,7.48,1.98,13.95 +TCH,10.78,7.87,2.06,13.95 +BLS,10.74,7.67,2.08,13.65 +USA,10.75,7.39,2.04,13.74 +USA,10.78,7.39,2.1,14.35 +FRG,10.95,7.75,2,14.36 +DDR,11.06,7.69,1.97,14.11 +SOV,10.92,7.61,1.95,14.11 \ No newline at end of file diff --git a/test/output/decathlon.html b/test/output/decathlon.html new file mode 100644 index 0000000000..190469706f --- /dev/null +++ b/test/output/decathlon.html @@ -0,0 +1,180 @@ +
+
+ + + BLS + + DDR + + FRG + + GBR + + SOV + + TCH + + USA +
+ + + + + 10.3 + + + + 10.4 + + + + 10.5 + + + + 10.6 + + + + 10.7 + + + + 10.8 + + + + 10.9 + + + + 11.0 + + + + 11.1 + ↑ 100 Meters + + + + + 7.2 + + + + 7.4 + + + + 7.6 + + + + 7.8 + + + + 8.0 + Long Jump → + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/test/plots/decathlon.js b/test/plots/decathlon.js new file mode 100644 index 0000000000..264b48c592 --- /dev/null +++ b/test/plots/decathlon.js @@ -0,0 +1,16 @@ +import * as Plot from "@observablehq/plot"; +import * as d3 from "d3"; + +export default async function() { + const decathlon = await d3.csv("data/decathlon.csv", d3.autoType); + return Plot.plot({ + grid: true, + inset: 12, + symbol: { + legend: true + }, + marks: [ + Plot.dot(decathlon, {x: "Long Jump", y: "100 Meters", symbol: "Country", stroke: "Country"}) + ] + }); +} diff --git a/test/plots/index.js b/test/plots/index.js index 44260a100c..4526d2b592 100644 --- a/test/plots/index.js +++ b/test/plots/index.js @@ -32,6 +32,7 @@ export {default as collapsedHistogram} from "./collapsed-histogram.js"; export {default as covidIhmeProjectedDeaths} from "./covid-ihme-projected-deaths.js"; export {default as d3Survey2015Comfort} from "./d3-survey-2015-comfort.js"; export {default as d3Survey2015Why} from "./d3-survey-2015-why.js"; +export {default as decathlon} from "./decathlon.js"; export {default as diamondsCaratPrice} from "./diamonds-carat-price.js"; export {default as diamondsCaratPriceDots} from "./diamonds-carat-price-dots.js"; export {default as documentationLinks} from "./documentation-links.js"; diff --git a/yarn.lock b/yarn.lock index fdee1f9b34..3ddff120a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,23 +3,23 @@ "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431" - integrity sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== dependencies: - "@babel/highlight" "^7.16.0" + "@babel/highlight" "^7.16.7" -"@babel/helper-validator-identifier@^7.15.7": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" - integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== -"@babel/highlight@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.0.tgz#6ceb32b2ca4b8f5f361fb7fd821e3fddf4a1725a" - integrity sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g== +"@babel/highlight@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.7.tgz#81a01d7d675046f0d96f82450d9d9578bdfd6b0b" + integrity sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw== dependencies: - "@babel/helper-validator-identifier" "^7.15.7" + "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" js-tokens "^4.0.0" @@ -63,16 +63,16 @@ integrity sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ== "@mapbox/node-pre-gyp@^1.0.0": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.7.tgz#a26919cac6595662703330d1820a0ca206f45521" - integrity sha512-PplSvl4pJ5N3BkVjAdDzpPhVUPdC73JgttkR+LnBx2OORC1GCQsBjUeEuipf9uOaAM1SbxcdZFfR3KDTKm2S0A== + version "1.0.8" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz#32abc8a5c624bc4e46c43d84dfb8b26d33a96f58" + integrity sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg== dependencies: detect-libc "^1.0.3" https-proxy-agent "^5.0.0" make-dir "^3.1.0" node-fetch "^2.6.5" nopt "^5.0.0" - npmlog "^6.0.0" + npmlog "^5.0.1" rimraf "^3.0.2" semver "^7.3.5" tar "^6.1.11" @@ -220,9 +220,9 @@ resolve "^1.17.0" "@rollup/plugin-inject@^4.0.0", "@rollup/plugin-inject@^4.0.2": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@rollup/plugin-inject/-/plugin-inject-4.0.3.tgz#6f04ebc14790a8bf892286fe9b8c0eb1ddf4d5ce" - integrity sha512-lzMXmj0LZjd67MI+M8H9dk/oCxR0TYqYAdZ6ZOejWQLSUtud+FUPu4NCMAO8KyWWAalFo8ean7yFHCMvCNsCZw== + version "4.0.4" + resolved "https://registry.yarnpkg.com/@rollup/plugin-inject/-/plugin-inject-4.0.4.tgz#fbeee66e9a700782c4f65c8b0edbafe58678fbc2" + integrity sha512-4pbcU4J/nS+zuHk+c+OL3WtmEQhqxlZ9uqfjQMQDOHOPld7PsCd8k5LWs8h5wjwJN7MgnAn768F2sDxEP4eNFQ== dependencies: "@rollup/pluginutils" "^3.1.0" estree-walker "^2.0.1" @@ -236,9 +236,9 @@ "@rollup/pluginutils" "^3.0.8" "@rollup/plugin-node-resolve@13": - version "13.0.6" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.6.tgz#29629070bb767567be8157f575cfa8f2b8e9ef77" - integrity sha512-sFsPDMPd4gMqnh2gS0uIxELnoRUp5kBl5knxD2EO0778G1oOJv4G1vyT2cpWz75OU2jDVcXhjVUuTAczGyFNKA== + version "13.1.3" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.3.tgz#2ed277fb3ad98745424c1d2ba152484508a92d79" + integrity sha512-BdxNk+LtmElRo5d06MGY4zoepyrXX1tkzX2hrnPEZ53k78GuOMWLqmJDGIIOPwVRIFZrLQOo+Yr6KtCuLIA0AQ== dependencies: "@rollup/pluginutils" "^3.1.0" "@types/resolve" "1.17.1" @@ -277,9 +277,9 @@ picomatch "^2.2.2" "@sindresorhus/is@^4.0.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.2.0.tgz#667bfc6186ae7c9e0b45a08960c551437176e1ca" - integrity sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw== + version "4.2.1" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.2.1.tgz#b88b5724283db80b507cd612caee9a1947412a20" + integrity sha512-BrzrgtaqEre0qfvI8sMTaEvx+bayuhPmfe2rfeUGPPHYr/PLxCOqkOe4TQTDPb+qcqgNcsAtXV/Ew74mcDIE8w== "@szmarczak/http-timer@^4.0.5": version "4.0.6" @@ -331,9 +331,9 @@ "@types/node" "*" "@types/node@*": - version "16.11.12" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.12.tgz#ac7fb693ac587ee182c3780c26eb65546a1a3c10" - integrity sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw== + version "17.0.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.8.tgz#50d680c8a8a78fe30abe6906453b21ad8ab0ad7b" + integrity sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg== "@types/parse-json@^4.0.0": version "4.0.0" @@ -392,10 +392,10 @@ acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.5.0, acorn@^8.6.0: - version "8.6.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895" - integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw== +acorn@^8.5.0, acorn@^8.7.0: + version "8.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== address@^1.0.1: version "1.1.2" @@ -410,9 +410,9 @@ agent-base@6, agent-base@^6.0.2: debug "4" agentkeepalive@^4.1.3: - version "4.1.4" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.1.4.tgz#d928028a4862cb11718e55227872e842a44c945b" - integrity sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ== + version "4.2.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.0.tgz#616ce94ccb41d1a39a45d203d8076fe98713062d" + integrity sha512-0PhAp58jZNw13UJv7NVdTGb0ZcghHUb3DrZ046JiiJY/BOaTTpbwdHq2VObPCBV8M2GPh7sgrJ3AQ8Ey468LJw== dependencies: debug "^4.1.0" depd "^1.1.2" @@ -561,11 +561,6 @@ big-integer@^1.6.7: resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== -big.js@^5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" - integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== - bin-links@^2.2.1: version "2.3.0" resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-2.3.0.tgz#1ff241c86d2c29b24ae52f49544db5d78a4eb967" @@ -626,9 +621,9 @@ buffer-from@^1.0.0: integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== bufferutil@^4.0.2: - version "4.0.5" - resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.5.tgz#da9ea8166911cc276bf677b8aed2d02d31f59028" - integrity sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A== + version "4.0.6" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.6.tgz#ebd6c67c7922a0e902f053e5d8be5ec850e48433" + integrity sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw== dependencies: node-gyp-build "^4.3.0" @@ -695,9 +690,9 @@ callsites@^3.0.0: integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase@^6.0.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.1.tgz#250fd350cfd555d0d2160b1d51510eaf8326e86e" - integrity sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA== + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== canvas@2: version "2.8.0" @@ -929,17 +924,17 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: which "^2.0.1" css-select@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067" - integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA== + version "4.2.1" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.2.1.tgz#9e665d6ae4c7f9d65dbe69d0316e3221fb274cdd" + integrity sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ== dependencies: boolbase "^1.0.0" - css-what "^5.0.0" - domhandler "^4.2.0" - domutils "^2.6.0" - nth-check "^2.0.0" + css-what "^5.1.0" + domhandler "^4.3.0" + domutils "^2.8.0" + nth-check "^2.0.1" -css-what@^5.0.0, css-what@^5.0.1: +css-what@^5.0.1, css-what@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe" integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw== @@ -1127,9 +1122,9 @@ d3-scale@4: integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== d3-shape@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.0.1.tgz#9ccdfb28fd9b0d12f2d8aec234cd5c4a9ea27931" - integrity sha512-HNZNEQoDhuCrDWEc/BMbF/hKtzMZVoe64TvisFLDp2Iyj0UShB/E6/lBsLlJTfBMbYgftHj90cXJ0SEitlE6Xw== + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.1.0.tgz#c8a495652d83ea6f524e482fca57aa3f8bc32556" + integrity sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ== dependencies: d3-path "1 - 3" @@ -1174,10 +1169,10 @@ d3-zoom@3: d3-selection "2 - 3" d3-transition "2 - 3" -d3@7: - version "7.2.1" - resolved "https://registry.yarnpkg.com/d3/-/d3-7.2.1.tgz#97eafaa6fc8cd7c564c3ace1e6678cbecf63f3ea" - integrity sha512-E/5sP0aeK6YPXI/+4QlefvBFgmcyR2jYftId0PrYWv4Y/gW3c3thp1XG4rQzF0eUwV9tR1x05X5eWuJ6rQXvew== +d3@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/d3/-/d3-7.3.0.tgz#f3d5a22c1f658952a6491cf50132f5267ed7a40a" + integrity sha512-MDRLJCMK232OJQRqGljQ/gCxtB8k3/sLKFjftMjzPB3nKVUODpdW9Rb3vcq7U8Ka5YKoZkAmp++Ur6I+6iNWIw== dependencies: d3-array "3" d3-axis "3" @@ -1381,14 +1376,14 @@ domexception@^4.0.0: dependencies: webidl-conversions "^7.0.0" -domhandler@^4.0.0, domhandler@^4.2.0: +domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626" integrity sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g== dependencies: domelementtype "^2.2.0" -domutils@^2.5.2, domutils@^2.6.0, domutils@^2.7.0: +domutils@^2.5.2, domutils@^2.7.0, domutils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== @@ -1420,11 +1415,6 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emojis-list@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" - integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== - encoding@^0.1.12: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" @@ -1562,9 +1552,9 @@ eslint-visitor-keys@^3.1.0: integrity sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA== eslint@8: - version "8.4.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.4.1.tgz#d6531bbf3e598dffd7c0c7d35ec52a0b30fdfa2d" - integrity sha512-TxU/p7LB1KxQ6+7aztTnO7K0i+h0tDi81YRY9VzB6Id71kNz+fFYnf5HD5UOQmxkzcoa0TlVZf9dpMtUv0GpWg== + version "8.6.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.6.0.tgz#4318c6a31c5584838c1a2e940c478190f58d558e" + integrity sha512-UvxdOJ7mXFlw7iuHZA4jmzPaUqIw54mZrv+XPYKNbKdLR0et4rf60lIZUU9kiNtnzzMzGWxMV+tQ7uG7JG8DPw== dependencies: "@eslint/eslintrc" "^1.0.5" "@humanwhocodes/config-array" "^0.9.2" @@ -1578,7 +1568,7 @@ eslint@8: eslint-scope "^7.1.0" eslint-utils "^3.0.0" eslint-visitor-keys "^3.1.0" - espree "^9.2.0" + espree "^9.3.0" esquery "^1.4.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -1605,12 +1595,12 @@ eslint@8: text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.2.0.tgz#c50814e01611c2d0f8bd4daa83c369eabba80dbc" - integrity sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg== +espree@^9.2.0, espree@^9.3.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.0.tgz#c1240d79183b72aaee6ccfa5a90bc9111df085a8" + integrity sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ== dependencies: - acorn "^8.6.0" + acorn "^8.7.0" acorn-jsx "^5.3.1" eslint-visitor-keys "^3.1.0" @@ -1825,16 +1815,16 @@ 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@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.0.tgz#afba07aa0374a93c6219603b1fb83eaa2264d8f8" - integrity sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw== +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== dependencies: - ansi-regex "^5.0.1" aproba "^1.0.3 || ^2.0.0" color-support "^1.1.2" console-control-strings "^1.0.0" has-unicode "^2.0.1" + object-assign "^4.1.1" signal-exit "^3.0.0" string-width "^4.2.3" strip-ansi "^6.0.1" @@ -1854,12 +1844,12 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -generic-names@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/generic-names/-/generic-names-2.0.1.tgz#f8a378ead2ccaa7a34f0317b05554832ae41b872" - integrity sha512-kPCHWa1m9wGG/OwQpeweTwM/PYiQLrUIxXbt/P4Nic3LbGjCP0YwrALHW1uNLKZ0LIMg+RF+XRlj2ekT9ZlZAQ== +generic-names@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/generic-names/-/generic-names-4.0.0.tgz#0bd8a2fd23fe8ea16cbd0a279acd69c06933d9a3" + integrity sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A== dependencies: - loader-utils "^1.1.0" + loader-utils "^3.2.0" get-caller-file@^2.0.5: version "2.0.5" @@ -1948,9 +1938,9 @@ got@^11.1.4: responselike "^2.0.0" graceful-fs@^4.1.2, graceful-fs@^4.2.3: - version "4.2.8" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" - integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== + version "4.2.9" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" + integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== growl@1.10.5: version "1.10.5" @@ -1998,9 +1988,9 @@ he@1.2.0: integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== hosted-git-info@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.2.tgz#5e425507eede4fea846b7262f0838456c4209961" - integrity sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg== + version "4.1.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" + integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== dependencies: lru-cache "^6.0.0" @@ -2188,10 +2178,10 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-core-module@^2.2.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548" - integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw== +is-core-module@^2.8.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== dependencies: has "^1.0.3" @@ -2419,13 +2409,6 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - dependencies: - minimist "^1.2.0" - jsonparse@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" @@ -2489,14 +2472,10 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -loader-utils@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^1.0.1" +loader-utils@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.0.tgz#bcecc51a7898bee7473d4bc6b845b23af8304d4f" + integrity sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ== locate-path@^5.0.0: version "5.0.0" @@ -2635,11 +2614,6 @@ minimatch@3.0.4, minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@^1.2.0: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - minipass-collect@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" @@ -2913,17 +2887,17 @@ npmlog@^4.1.2: gauge "~2.7.3" set-blocking "~2.0.0" -npmlog@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.0.tgz#ba9ef39413c3d936ea91553db7be49c34ad0520c" - integrity sha512-03ppFRGlsyUaQFbGC2C8QWJN/C/K7PsfyD9aQdhVKAQIH4sQBc8WASqFBP7O+Ut4d2oo5LoeoboB3cGdBZSp6Q== +npmlog@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== dependencies: are-we-there-yet "^2.0.0" console-control-strings "^1.1.0" - gauge "^4.0.0" + gauge "^3.0.0" set-blocking "^2.0.0" -nth-check@^2.0.0: +nth-check@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w== @@ -3145,7 +3119,7 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6: +path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -3174,9 +3148,9 @@ picocolors@^1.0.0: integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pify@^2.3.0: version "2.3.0" @@ -3219,11 +3193,11 @@ postcss-modules-values@^4.0.0: icss-utils "^5.0.0" postcss-modules@^4.0.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/postcss-modules/-/postcss-modules-4.2.2.tgz#5e7777c5a8964ea176919d90b2e54ef891321ce5" - integrity sha512-/H08MGEmaalv/OU8j6bUKi/kZr2kqGF6huAW8m9UAgOLWtpFdhA14+gPBoymtqyv+D4MLsmqaF2zvIegdCxJXg== + version "4.3.0" + resolved "https://registry.yarnpkg.com/postcss-modules/-/postcss-modules-4.3.0.tgz#1cb32f16a8cfffe2b989598f8135eb6427106ec7" + integrity sha512-zoUttLDSsbWDinJM9jH37o7hulLRyEgH6fZm2PchxN7AZ8rkdWiALyNhnQ7+jg7cX9f10m6y5VhHsrjO0Mf/DA== dependencies: - generic-names "^2.0.1" + generic-names "^4.0.0" icss-replace-symbols "^1.1.0" lodash.camelcase "^4.3.0" postcss-modules-extract-imports "^3.0.0" @@ -3233,9 +3207,9 @@ postcss-modules@^4.0.0: string-hash "^1.1.1" postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: - version "6.0.7" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.7.tgz#48404830a635113a71fd79397de8209ed05a66fc" - integrity sha512-U+b/Deoi4I/UmE6KOVPpnhS7I7AYdKbhGcat+qTQ27gycvaACvNEw11ba6RrkwVmDVRW7sigWgLj4/KbbJjeDA== + version "6.0.8" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.8.tgz#f023ed7a9ea736cd7ef70342996e8e78645a7914" + integrity sha512-D5PG53d209Z1Uhcc0qAZ5U3t5HagH3cxu+WLZ22jt3gLUpXM4eXXfiO14jiDWST3NNooX/E8wISfOhZ9eIjGTQ== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" @@ -3246,9 +3220,9 @@ postcss-value-parser@^4.1.0: integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss@^8.3.5: - version "8.4.4" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.4.tgz#d53d4ec6a75fd62557a66bb41978bf47ff0c2869" - integrity sha512-joU6fBsN6EIer28Lj6GDFoC/5yOZzLCfn0zHAn/MYXI7aPt4m4hK5KC5ovEZXy+lnCjmYIbQWngvju2ddyEr8Q== + version "8.4.5" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95" + integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg== dependencies: nanoid "^3.1.30" picocolors "^1.0.0" @@ -3451,12 +3425,13 @@ resolve-from@^5.0.0: integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + version "1.21.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f" + integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA== dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" + is-core-module "^2.8.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" responselike@^2.0.0: version "2.0.0" @@ -3500,9 +3475,9 @@ rollup-plugin-terser@7: terser "^5.0.0" rollup@2, rollup@^2.23.0: - version "2.61.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.61.0.tgz#ccd927bcd6cc0c78a4689c918627a717977208f4" - integrity sha512-teQ+T1mUYbyvGyUavCodiyA9hD4DxwYZJwr/qehZGhs1Z49vsmzelMVYMxGU4ZhGRKxYPupHuz5yzm/wj7VpWA== + version "2.63.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.63.0.tgz#fe2f7fec2133f3fab9e022b9ac245628d817c6bb" + integrity sha512-nps0idjmD+NXl6OREfyYXMn/dar3WGcyKn+KBzPdaLecub3x/LrId0wUcthcr8oZUAcZAR8NKcfGGFlNgGL1kQ== optionalDependencies: fsevents "~2.3.2" @@ -3853,6 +3828,11 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -4002,9 +3982,9 @@ uri-js@^4.2.2: punycode "^2.1.0" utf-8-validate@^5.0.3: - version "5.0.7" - resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.7.tgz#c15a19a6af1f7ad9ec7ddc425747ca28c3644922" - integrity sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q== + version "5.0.8" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.8.tgz#4a735a61661dbb1c59a0868c397d2fe263f14e58" + integrity sha512-k4dW/Qja1BYDl2qD4tOMB9PFVha/UJtxTc1cXYOe3WwA/2m0Yn4qB7wLMpJyLJ/7DR0XnTut3HsCSzDT4ZvKgA== dependencies: node-gyp-build "^4.3.0" @@ -4162,9 +4142,9 @@ ws@^7.3.0: integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== ws@^8.2.3: - version "8.3.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.3.0.tgz#7185e252c8973a60d57170175ff55fdbd116070d" - integrity sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw== + version "8.4.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.4.0.tgz#f05e982a0a88c604080e8581576e2a063802bed6" + integrity sha512-IHVsKe2pjajSUIl4KYMQOdlyliovpEPquKkqbwswulszzI7r0SfQrxnXdWAEqOlDCLrVSJzo+O1hAwdog2sKSQ== xml-name-validator@^4.0.0: version "4.0.0"