Skip to content

Commit b6e91ba

Browse files
committed
document option
1 parent 3371256 commit b6e91ba

27 files changed

+200
-116
lines changed

src/axis.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import {axisTop, axisBottom, axisRight, axisLeft, create, format, utcFormat} from "d3";
2-
import {boolean, take, number, string, keyword, maybeKeyword, constant, isTemporal} from "./options.js";
1+
import {axisTop, axisBottom, axisRight, axisLeft, format, utcFormat} from "d3";
2+
import {create} from "./create.js";
33
import {formatIsoDate} from "./format.js";
44
import {radians} from "./math.js";
5+
import {boolean, take, number, string, keyword, maybeKeyword, constant, isTemporal} from "./options.js";
56
import {applyAttr, impliedString} from "./style.js";
67

78
export class AxisX {
@@ -53,7 +54,8 @@ export class AxisX {
5354
facetMarginBottom,
5455
labelMarginLeft = 0,
5556
labelMarginRight = 0
56-
}
57+
},
58+
context
5759
) {
5860
const {
5961
axis,
@@ -69,7 +71,7 @@ export class AxisX {
6971
const offset = name === "x" ? 0 : axis === "top" ? marginTop - facetMarginTop : marginBottom - facetMarginBottom;
7072
const offsetSign = axis === "top" ? -1 : 1;
7173
const ty = offsetSign * offset + (axis === "top" ? marginTop : height - marginBottom);
72-
return create("svg:g")
74+
return create("svg:g", context)
7375
.call(applyAria, this)
7476
.attr("transform", `translate(${offsetLeft},${ty})`)
7577
.call(createAxis(axis === "top" ? axisTop : axisBottom, x, this))
@@ -144,7 +146,8 @@ export class AxisY {
144146
offsetTop = 0,
145147
facetMarginLeft,
146148
facetMarginRight
147-
}
149+
},
150+
context
148151
) {
149152
const {
150153
axis,
@@ -160,7 +163,7 @@ export class AxisY {
160163
const offset = name === "y" ? 0 : axis === "left" ? marginLeft - facetMarginLeft : marginRight - facetMarginRight;
161164
const offsetSign = axis === "left" ? -1 : 1;
162165
const tx = offsetSign * offset + (axis === "right" ? width - marginRight : marginLeft);
163-
return create("svg:g")
166+
return create("svg:g", context)
164167
.call(applyAria, this)
165168
.attr("transform", `translate(${tx},${offsetTop})`)
166169
.call(createAxis(axis === "right" ? axisRight : axisLeft, y, this))

src/create.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import {creator, select} from "d3";
2+
3+
export function create(name, {document}) {
4+
return select(creator(name).call(document.documentElement));
5+
}

src/legends.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {rgb} from "d3";
2-
import {isScaleOptions} from "./options.js";
2+
import {inherit, isScaleOptions} from "./options.js";
33
import {normalizeScale} from "./scales.js";
44
import {legendRamp} from "./legends/ramp.js";
55
import {legendSwatches, legendSymbols} from "./legends/swatches.js";
@@ -14,6 +14,8 @@ export function legend(options = {}) {
1414
for (const [key, value] of legendRegistry) {
1515
const scale = options[key];
1616
if (isScaleOptions(scale)) { // e.g., ignore {color: "red"}
17+
const {document = window.document} = options;
18+
const context = {document};
1719
let hint;
1820
// For symbol legends, pass a hint to the symbol scale.
1921
if (key === "symbol") {
@@ -22,24 +24,24 @@ export function legend(options = {}) {
2224
}
2325
return value(
2426
normalizeScale(key, scale, hint),
25-
legendOptions(scale, options),
27+
legendOptions(context, scale, options),
2628
key => isScaleOptions(options[key]) ? normalizeScale(key, options[key]) : null
2729
);
2830
}
2931
}
3032
throw new Error("unknown legend type; no scale found");
3133
}
3234

33-
export function exposeLegends(scales, defaults = {}) {
35+
export function exposeLegends(scales, context, defaults = {}) {
3436
return (key, options) => {
3537
if (!legendRegistry.has(key)) throw new Error(`unknown legend type: ${key}`);
3638
if (!(key in scales)) return;
37-
return legendRegistry.get(key)(scales[key], legendOptions(defaults[key], options), key => scales[key]);
39+
return legendRegistry.get(key)(scales[key], legendOptions(context, defaults[key], options), key => scales[key]);
3840
};
3941
}
4042

41-
function legendOptions({label, ticks, tickFormat} = {}, options = {}) {
42-
return {label, ticks, tickFormat, ...options};
43+
function legendOptions({document}, {label, ticks, tickFormat} = {}, options) {
44+
return inherit(options, {document, label, ticks, tickFormat});
4345
}
4446

4547
function legendColor(color, {
@@ -71,12 +73,12 @@ function interpolateOpacity(color) {
7173
return t => `rgba(${r},${g},${b},${t})`;
7274
}
7375

74-
export function Legends(scales, options) {
76+
export function Legends(scales, context, options) {
7577
const legends = [];
7678
for (const [key, value] of legendRegistry) {
7779
const o = options[key];
7880
if (o?.legend && (key in scales)) {
79-
const legend = value(scales[key], legendOptions(scales[key], o), key => scales[key]);
81+
const legend = value(scales[key], legendOptions(context, scales[key], o), key => scales[key]);
8082
if (legend != null) legends.push(legend);
8183
}
8284
}

src/legends/ramp.js

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import {create, quantize, interpolateNumber, piecewise, format, scaleBand, scaleLinear, axisBottom} from "d3";
1+
import {quantize, interpolateNumber, piecewise, format, scaleBand, scaleLinear, axisBottom} from "d3";
22
import {inferFontVariant} from "../axes.js";
3+
import {create} from "../create.js";
34
import {map} from "../options.js";
45
import {interpolatePiecewise} from "../scales/quantitative.js";
56
import {applyInlineStyles, impliedString, maybeClassName} from "../style.js";
@@ -18,12 +19,14 @@ export function legendRamp(color, {
1819
tickFormat,
1920
fontVariant = inferFontVariant(color),
2021
round = true,
21-
className
22+
className,
23+
document = window.document
2224
}) {
25+
const context = {document};
2326
className = maybeClassName(className);
2427
if (tickFormat === null) tickFormat = () => null;
2528

26-
const svg = create("svg")
29+
const svg = create("svg", context)
2730
.attr("class", className)
2831
.attr("font-family", "system-ui, sans-serif")
2932
.attr("font-size", 10)
@@ -83,13 +86,24 @@ export function legendRamp(color, {
8386
)
8487
);
8588

89+
// Construct a 256×1 canvas, filling each pixel using the interpolator.
90+
const n = 256;
91+
const canvas = document.createElement("canvas");
92+
canvas.width = n;
93+
canvas.height = 1;
94+
const context2 = canvas.getContext("2d");
95+
for (let i = 0, j = n - 1; i < n; ++i) {
96+
context2.fillStyle = interpolator(i / j);
97+
context2.fillRect(i, 0, 1, 1);
98+
}
99+
86100
svg.append("image")
87101
.attr("x", marginLeft)
88102
.attr("y", marginTop)
89103
.attr("width", width - marginLeft - marginRight)
90104
.attr("height", height - marginTop - marginBottom)
91105
.attr("preserveAspectRatio", "none")
92-
.attr("xlink:href", ramp(interpolator).toDataURL());
106+
.attr("xlink:href", canvas.toDataURL());
93107
}
94108

95109
// Threshold
@@ -162,13 +176,3 @@ export function legendRamp(color, {
162176

163177
return svg.node();
164178
}
165-
166-
function ramp(color, n = 256) {
167-
const canvas = create("canvas").attr("width", n).attr("height", 1).node();
168-
const context = canvas.getContext("2d");
169-
for (let i = 0; i < n; ++i) {
170-
context.fillStyle = color(i / (n - 1));
171-
context.fillRect(i, 0, 1, 1);
172-
}
173-
return canvas;
174-
}

src/legends/swatches.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import {create, path} from "d3";
1+
import {path} from "d3";
22
import {inferFontVariant} from "../axes.js";
33
import {maybeAutoTickFormat} from "../axis.js";
4+
import {create} from "../create.js";
45
import {isNoneish, maybeColorChannel, maybeNumberChannel} from "../options.js";
56
import {applyInlineStyles, impliedString, maybeClassName} from "../style.js";
67

@@ -81,12 +82,14 @@ function legendItems(scale, {
8182
marginLeft = 0,
8283
className,
8384
style,
84-
width
85+
width,
86+
document = window.document
8587
} = {}, swatch, swatchStyle) {
88+
const context = {document};
8689
className = maybeClassName(className);
8790
tickFormat = maybeAutoTickFormat(tickFormat, scale.domain);
8891

89-
const swatches = create("div")
92+
const swatches = create("div", context)
9093
.attr("class", className)
9194
.attr("style", `
9295
--swatchWidth: ${+swatchWidth}px;

src/marks/area.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import {area as shapeArea, create} from "d3";
1+
import {area as shapeArea} from "d3";
2+
import {create} from "../create.js";
23
import {Curve} from "../curve.js";
3-
import {Mark} from "../plot.js";
44
import {first, indexOf, maybeZ, second} from "../options.js";
5+
import {Mark} from "../plot.js";
56
import {applyDirectStyles, applyIndirectStyles, applyTransform, applyGroupedChannelStyles, groupIndex} from "../style.js";
67
import {maybeDenseIntervalX, maybeDenseIntervalY} from "../transforms/bin.js";
78
import {maybeIdentityX, maybeIdentityY} from "../transforms/identity.js";
@@ -36,9 +37,9 @@ export class Area extends Mark {
3637
filter(index) {
3738
return index;
3839
}
39-
render(index, scales, channels, dimensions) {
40+
render(index, scales, channels, dimensions, context) {
4041
const {x1: X1, y1: Y1, x2: X2 = X1, y2: Y2 = Y1} = channels;
41-
return create("svg:g")
42+
return create("svg:g", context)
4243
.call(applyIndirectStyles, this, scales, dimensions)
4344
.call(applyTransform, this, scales, 0, 0)
4445
.call(g => g.selectAll()

src/marks/arrow.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import {create} from "d3";
1+
import {create} from "../create.js";
22
import {radians} from "../math.js";
3+
import {constant} from "../options.js";
34
import {Mark} from "../plot.js";
45
import {applyChannelStyles, applyDirectStyles, applyIndirectStyles, applyTransform} from "../style.js";
56
import {maybeSameValue} from "./link.js";
6-
import {constant} from "../options.js";
77

88
const defaults = {
99
ariaLabel: "arrow",
@@ -45,7 +45,7 @@ export class Arrow extends Mark {
4545
this.insetStart = +insetStart;
4646
this.insetEnd = +insetEnd;
4747
}
48-
render(index, scales, channels, dimensions) {
48+
render(index, scales, channels, dimensions, context) {
4949
const {x1: X1, y1: Y1, x2: X2 = X1, y2: Y2 = Y1, SW} = channels;
5050
const {strokeWidth, bend, headAngle, headLength, insetStart, insetEnd} = this;
5151
const sw = SW ? i => SW[i] : constant(strokeWidth === undefined ? 1 : strokeWidth);
@@ -65,7 +65,7 @@ export class Arrow extends Mark {
6565
// the end point) relative to the stroke width.
6666
const wingScale = headLength / 1.5;
6767

68-
return create("svg:g")
68+
return create("svg:g", context)
6969
.call(applyIndirectStyles, this, scales, dimensions)
7070
.call(applyTransform, this, scales)
7171
.call(g => g.selectAll()

src/marks/bar.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import {create} from "d3";
2-
import {Mark} from "../plot.js";
1+
import {create} from "../create.js";
32
import {identity, indexOf, number} from "../options.js";
3+
import {Mark} from "../plot.js";
44
import {isCollapsed} from "../scales.js";
55
import {applyDirectStyles, applyIndirectStyles, applyTransform, impliedString, applyAttr, applyChannelStyles} from "../style.js";
66
import {maybeIdentityX, maybeIdentityY} from "../transforms/identity.js";
@@ -18,9 +18,9 @@ export class AbstractBar extends Mark {
1818
this.rx = impliedString(rx, "auto"); // number or percentage
1919
this.ry = impliedString(ry, "auto");
2020
}
21-
render(index, scales, channels, dimensions) {
21+
render(index, scales, channels, dimensions, context) {
2222
const {rx, ry} = this;
23-
return create("svg:g")
23+
return create("svg:g", context)
2424
.call(applyIndirectStyles, this, scales, dimensions)
2525
.call(this._transform, this, scales)
2626
.call(g => g.selectAll()

src/marks/delaunay.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import {create, group, path, select, Delaunay} from "d3";
1+
import {group, path, select, Delaunay} from "d3";
2+
import {create} from "../create.js";
23
import {Curve} from "../curve.js";
34
import {constant, maybeTuple, maybeZ} from "../options.js";
45
import {Mark} from "../plot.js";
@@ -57,7 +58,7 @@ class DelaunayLink extends Mark {
5758
this.curve = Curve(curve, tension);
5859
markers(this, options);
5960
}
60-
render(index, scales, channels, dimensions) {
61+
render(index, scales, channels, dimensions, context) {
6162
const {x: X, y: Y, z: Z} = channels;
6263
const {curve} = this;
6364
const [cx, cy] = applyFrameAnchor(this, dimensions);
@@ -113,7 +114,7 @@ class DelaunayLink extends Mark {
113114
.call(applyMarkers, mark, newChannels);
114115
}
115116

116-
return create("svg:g")
117+
return create("svg:g", context)
117118
.call(applyIndirectStyles, this, scales, dimensions)
118119
.call(applyTransform, this, scales)
119120
.call(Z
@@ -137,7 +138,7 @@ class AbstractDelaunayMark extends Mark {
137138
defaults
138139
);
139140
}
140-
render(index, scales, channels, dimensions) {
141+
render(index, scales, channels, dimensions, context) {
141142
const {x: X, y: Y, z: Z} = channels;
142143
const [cx, cy] = applyFrameAnchor(this, dimensions);
143144
const xi = X ? i => X[i] : constant(cx);
@@ -153,7 +154,7 @@ class AbstractDelaunayMark extends Mark {
153154
.call(applyChannelStyles, mark, channels);
154155
}
155156

156-
return create("svg:g")
157+
return create("svg:g", context)
157158
.call(applyIndirectStyles, this, scales, dimensions)
158159
.call(applyTransform, this, scales)
159160
.call(Z
@@ -196,7 +197,7 @@ class Voronoi extends Mark {
196197
voronoiDefaults
197198
);
198199
}
199-
render(index, scales, channels, dimensions) {
200+
render(index, scales, channels, dimensions, context) {
200201
const {x: X, y: Y, z: Z} = channels;
201202
const [cx, cy] = applyFrameAnchor(this, dimensions);
202203
const xi = X ? i => X[i] : constant(cx);
@@ -215,7 +216,7 @@ class Voronoi extends Mark {
215216
.call(applyChannelStyles, this, channels);
216217
}
217218

218-
return create("svg:g")
219+
return create("svg:g", context)
219220
.call(applyIndirectStyles, this, scales, dimensions)
220221
.call(applyTransform, this, scales)
221222
.call(Z

src/marks/density.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ export class Density extends Mark {
3838
filter(index) {
3939
return index; // don’t filter contours constructed by initializer
4040
}
41-
render(index, scales, channels, dimensions) {
41+
render(index, scales, channels, dimensions, context) {
4242
const {contours} = channels;
4343
const path = geoPath();
44-
return create("svg:g")
44+
return create("svg:g", context)
4545
.call(applyIndirectStyles, this, scales, dimensions)
4646
.call(applyTransform, this, scales)
4747
.call(g => g.selectAll()

0 commit comments

Comments
 (0)