Skip to content

Commit 64edc66

Browse files
authored
deterministic class name (#1446)
* deterministic class name * fix marginLeft * minimize diff
1 parent 0840d53 commit 64edc66

File tree

98 files changed

+3960
-5501
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+3960
-5501
lines changed

src/legends.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export function exposeLegends(scales, context, defaults = {}) {
4040
}
4141

4242
function legendOptions({className, ...context}, {label, ticks, tickFormat} = {}, options) {
43-
return inherit(options, {className: `${className}-legend`, ...context}, {label, ticks, tickFormat});
43+
return inherit(options, {className, ...context}, {label, ticks, tickFormat});
4444
}
4545

4646
function legendColor(color, {legend = true, ...options}) {

src/legends/ramp.js

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,27 @@ export function legendRamp(color, options) {
2929
if (tickFormat === null) tickFormat = () => null;
3030

3131
const svg = create("svg", context)
32-
.attr("class", className)
32+
.attr("class", `${className}-ramp`)
3333
.attr("font-family", "system-ui, sans-serif")
3434
.attr("font-size", 10)
3535
.attr("width", width)
3636
.attr("height", height)
3737
.attr("viewBox", `0 0 ${width} ${height}`)
3838
.call((svg) =>
39-
svg.append("style").text(`
40-
.${className} {
41-
display: block;
42-
background: white;
43-
height: auto;
44-
height: intrinsic;
45-
max-width: 100%;
46-
overflow: visible;
47-
}
48-
.${className} text {
49-
white-space: pre;
50-
}
51-
`)
39+
// Warning: if you edit this, change defaultClassName.
40+
svg.append("style").text(
41+
`.${className}-ramp {
42+
display: block;
43+
background: white;
44+
height: auto;
45+
height: intrinsic;
46+
max-width: 100%;
47+
overflow: visible;
48+
}
49+
.${className}-ramp text {
50+
white-space: pre;
51+
}`
52+
)
5253
)
5354
.call(applyInlineStyles, style);
5455

src/legends/swatches.js

Lines changed: 74 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,16 @@ function maybeScale(scale, key) {
1515
export function legendSwatches(color, {opacity, ...options} = {}) {
1616
if (!isOrdinalScale(color) && !isThresholdScale(color))
1717
throw new Error(`swatches legend requires ordinal or threshold color scale (not ${color.type})`);
18-
return legendItems(
19-
color,
20-
options,
21-
(selection, scale) =>
22-
selection
23-
.append("svg")
24-
.attr("fill", scale.scale)
25-
.attr("fill-opacity", maybeNumberChannel(opacity)[1])
26-
.append("rect")
27-
.attr("width", "100%")
28-
.attr("height", "100%"),
29-
(className) => `.${className}-swatch svg {
30-
width: var(--swatchWidth);
31-
height: var(--swatchHeight);
32-
margin-right: 0.5em;
33-
}`
18+
return legendItems(color, options, (selection, scale, width, height) =>
19+
selection
20+
.append("svg")
21+
.attr("width", width)
22+
.attr("height", height)
23+
.attr("fill", scale.scale)
24+
.attr("fill-opacity", maybeNumberChannel(opacity)[1])
25+
.append("rect")
26+
.attr("width", "100%")
27+
.attr("height", "100%")
3428
);
3529
}
3630

@@ -55,36 +49,27 @@ export function legendSymbols(
5549
fillOpacity = maybeNumberChannel(fillOpacity)[1];
5650
strokeOpacity = maybeNumberChannel(strokeOpacity)[1];
5751
strokeWidth = maybeNumberChannel(strokeWidth)[1];
58-
return legendItems(
59-
symbol,
60-
options,
61-
(selection) =>
62-
selection
63-
.append("svg")
64-
.attr("viewBox", "-8 -8 16 16")
65-
.attr("fill", vf === "color" ? (d) => sf.scale(d) : null)
66-
.attr("stroke", vs === "color" ? (d) => ss.scale(d) : null)
67-
.append("path")
68-
.attr("d", (d) => {
69-
const p = path();
70-
symbol.scale(d).draw(p, size);
71-
return p;
72-
}),
73-
(className) => `.${className}-swatch > svg {
74-
width: var(--swatchWidth);
75-
height: var(--swatchHeight);
76-
margin-right: 0.5em;
77-
overflow: visible;
78-
fill: ${cf};
79-
fill-opacity: ${fillOpacity};
80-
stroke: ${cs};
81-
stroke-width: ${strokeWidth}px;
82-
stroke-opacity: ${strokeOpacity};
83-
}`
52+
return legendItems(symbol, options, (selection, scale, width, height) =>
53+
selection
54+
.append("svg")
55+
.attr("viewBox", "-8 -8 16 16")
56+
.attr("width", width)
57+
.attr("height", height)
58+
.attr("fill", vf === "color" ? (d) => sf.scale(d) : cf)
59+
.attr("fill-opacity", fillOpacity)
60+
.attr("stroke", vs === "color" ? (d) => ss.scale(d) : cs)
61+
.attr("stroke-opacity", strokeOpacity)
62+
.attr("stroke-width", strokeWidth)
63+
.append("path")
64+
.attr("d", (d) => {
65+
const p = path();
66+
symbol.scale(d).draw(p, size);
67+
return p;
68+
})
8469
);
8570
}
8671

87-
function legendItems(scale, options = {}, swatch, swatchStyle) {
72+
function legendItems(scale, options = {}, swatch) {
8873
let {
8974
columns,
9075
tickFormat,
@@ -102,35 +87,28 @@ function legendItems(scale, options = {}, swatch, swatchStyle) {
10287
className = maybeClassName(className);
10388
tickFormat = maybeAutoTickFormat(tickFormat, scale.domain);
10489

105-
const swatches = create("div", context)
106-
.attr("class", className)
107-
.attr(
108-
"style",
109-
`
110-
--swatchWidth: ${+swatchWidth}px;
111-
--swatchHeight: ${+swatchHeight}px;
112-
`
113-
);
90+
const swatches = create("div", context).attr(
91+
"class",
92+
`${className}-swatches ${className}-swatches-${columns != null ? "columns" : "wrap"}`
93+
);
11494

11595
let extraStyle;
11696

11797
if (columns != null) {
118-
extraStyle = `
119-
.${className}-swatch {
120-
display: flex;
121-
align-items: center;
122-
break-inside: avoid;
123-
padding-bottom: 1px;
124-
}
125-
.${className}-swatch::before {
126-
flex-shrink: 0;
127-
}
128-
.${className}-label {
129-
white-space: nowrap;
130-
overflow: hidden;
131-
text-overflow: ellipsis;
132-
}
133-
`;
98+
extraStyle = `.${className}-swatches-columns .${className}-swatch {
99+
display: flex;
100+
align-items: center;
101+
break-inside: avoid;
102+
padding-bottom: 1px;
103+
}
104+
.${className}-swatches-columns .${className}-swatch::before {
105+
flex-shrink: 0;
106+
}
107+
.${className}-swatches-columns .${className}-swatch-label {
108+
white-space: nowrap;
109+
overflow: hidden;
110+
text-overflow: ellipsis;
111+
}`;
134112

135113
swatches
136114
.style("columns", columns)
@@ -139,59 +117,52 @@ function legendItems(scale, options = {}, swatch, swatchStyle) {
139117
.enter()
140118
.append("div")
141119
.attr("class", `${className}-swatch`)
142-
.call(swatch, scale)
120+
.call(swatch, scale, swatchWidth, swatchHeight)
143121
.call((item) =>
144-
item.append("div").attr("class", `${className}-label`).attr("title", tickFormat).text(tickFormat)
122+
item.append("div").attr("class", `${className}-swatch-label`).attr("title", tickFormat).text(tickFormat)
145123
);
146124
} else {
147-
extraStyle = `
148-
.${className} {
149-
display: flex;
150-
align-items: center;
151-
min-height: 33px;
152-
flex-wrap: wrap;
153-
}
154-
.${className}-swatch {
155-
display: inline-flex;
156-
align-items: center;
157-
margin-right: 1em;
158-
}
159-
`;
125+
extraStyle = `.${className}-swatches-wrap {
126+
display: flex;
127+
align-items: center;
128+
min-height: 33px;
129+
flex-wrap: wrap;
130+
}
131+
.${className}-swatches-wrap .${className}-swatch {
132+
display: inline-flex;
133+
align-items: center;
134+
margin-right: 1em;
135+
}`;
160136

161137
swatches
162138
.selectAll()
163139
.data(scale.domain)
164140
.enter()
165141
.append("span")
166142
.attr("class", `${className}-swatch`)
167-
.call(swatch, scale)
143+
.call(swatch, scale, swatchWidth, swatchHeight)
168144
.append(function () {
169145
return this.ownerDocument.createTextNode(tickFormat.apply(this, arguments));
170146
});
171147
}
172148

173149
return swatches
174150
.call((div) =>
175-
div.insert("style", "*").text(`
176-
.${className} {
177-
font-family: system-ui, sans-serif;
178-
font-size: 10px;
179-
margin-bottom: 0.5em;${
180-
marginLeft === undefined
181-
? ""
182-
: `
183-
margin-left: ${+marginLeft}px;`
184-
}${
185-
width === undefined
186-
? ""
187-
: `
188-
width: ${width}px;`
189-
}
190-
}
191-
${swatchStyle(className)}
192-
${extraStyle}
193-
`)
151+
div.insert("style", "*").text(
152+
`.${className}-swatches {
153+
font-family: system-ui, sans-serif;
154+
font-size: 10px;
155+
margin-bottom: 0.5em;
156+
}
157+
.${className}-swatch > svg {
158+
margin-right: 0.5em;
159+
overflow: visible;
160+
}
161+
${extraStyle}`
162+
)
194163
)
164+
.style("margin-left", marginLeft ? `${+marginLeft}px` : null)
165+
.style("width", width === undefined ? null : `${+width}px`)
195166
.style("font-variant", impliedString(fontVariant, "normal"))
196167
.call(applyInlineStyles, style)
197168
.node();

src/plot.js

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -216,19 +216,20 @@ export function plot(options = {}) {
216216
.attr("aria-label", ariaLabel)
217217
.attr("aria-description", ariaDescription)
218218
.call((svg) =>
219-
svg.append("style").text(`
220-
.${className} {
221-
display: block;
222-
background: white;
223-
height: auto;
224-
height: intrinsic;
225-
max-width: 100%;
226-
}
227-
.${className} text,
228-
.${className} tspan {
229-
white-space: pre;
230-
}
231-
`)
219+
// Warning: if you edit this, change defaultClassName.
220+
svg.append("style").text(
221+
`.${className} {
222+
display: block;
223+
background: white;
224+
height: auto;
225+
height: intrinsic;
226+
max-width: 100%;
227+
}
228+
.${className} text,
229+
.${className} tspan {
230+
white-space: pre;
231+
}`
232+
)
232233
)
233234
.call(applyInlineStyles, style)
234235
.node();

src/style.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,9 @@ const validClassName =
419419
/^-?([_a-z]|[\240-\377]|\\[0-9a-f]{1,6}(\r\n|[ \t\r\n\f])?|\\[^\r\n\f0-9a-f])([_a-z0-9-]|[\240-\377]|\\[0-9a-f]{1,6}(\r\n|[ \t\r\n\f])?|\\[^\r\n\f0-9a-f])*$/i;
420420

421421
export function maybeClassName(name) {
422-
if (name === undefined) return `plot-${Math.random().toString(16).slice(2)}`;
422+
// The default should be changed whenever the default styles are changed, so
423+
// as to avoid conflict when multiple versions of Plot are on the page.
424+
if (name === undefined) return "plot-d6a7b5";
423425
name = `${name}`;
424426
if (!validClassName.test(name)) throw new Error(`invalid class name: ${name}`);
425427
return name;

0 commit comments

Comments
 (0)