Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multiline tick labels #83

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@ axis.ticks(10, ",f");

This has the advantage of setting the format precision automatically based on the tick interval.

Multiline tick labels are generated by returning a multiline string—unless the multiline option has been set to false. In multiline mode, each line of the string is placed in a tspan element 1.2em below the preceding line (or above in the case of a top-oriented axis). On each line, the label is suppressed when it repeats the label at the same level on the preceding tick—unless the tickNoRepeat option has been set to false. For example, the following tick format will display a line with the months’ short names, then a line with the year, without repeating the year for two consecutive months:

```js
axis.tickFormat(d3.utcFormat("%b\n%Y"));
```

<a name="axis_tickSize" href="#axis_tickSize">#</a> <i>axis</i>.<b>tickSize</b>([<i>size</i>]) · [Source](https://github.com/d3/d3-axis/blob/master/src/axis.js)

If *size* is specified, sets the [inner](#axis_tickSizeInner) and [outer](#axis_tickSizeOuter) tick size to the specified value and returns the axis. If *size* is not specified, returns the current inner tick size, which defaults to 6.
Expand All @@ -208,3 +214,21 @@ If *padding* is specified, sets the padding to the specified value in pixels and
<a name="axis_offset" href="#axis_offset">#</a> <i>axis</i>.<b>offset</b>([<i>offset</i>]) · [Source](https://github.com/d3/d3-axis/blob/master/src/axis.js)

If *offset* is specified, sets the offset to the specified value in pixels and returns the axis. If *offset* is not specified, returns the current offset which defaults to 0 on devices with a [devicePixelRatio](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) greater than 1, and 0.5px otherwise. This default offset ensures crisp edges on low-resolution devices.

<a name="axis_tickMultiline" href="#axis_tickMultiline">#</a> <i>axis</i>.<b>tickMultiline</b>([<i>multiline</i>]) · [Source](https://github.com/d3/d3-axis/blob/master/src/axis.js)

If *multiline* is specified, sets the multiline behavior to one of:
- *auto*: detects multiline tick labels and wraps each line in a tspan element;
- true: all the labels are wrapped in a tspan element, even when there is only one line
- false: use multiline labels as simple texts.

If *multiline* is not specified, returns the setting, which defaults to auto.

<a name="axis_tickNoRepeat" href="#axis_tickNoRepeat">#</a> <i>axis</i>.<b>tickNoRepeat</b>([<i>norepeat</i>]) · [Source](https://github.com/d3/d3-axis/blob/master/src/axis.js)

If *norepeat* is specified, sets the no-repeat behavior to one of:
- true: suppress a tick label that is repeated from the previous tick mark; detection is done line by line.
- false: do not suppress repeating tick labels.

If *norepeat* is not specified, returns the setting, which defaults to true.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"devDependencies": {
"d3-scale": "2 - 4",
"d3-selection": "1 - 3",
"d3-time-format": "4",
"eslint": "7",
"js-beautify": "1",
"jsdom": "16",
Expand Down
45 changes: 42 additions & 3 deletions src/axis.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ function axis(orient, scale) {
offset = typeof window !== "undefined" && window.devicePixelRatio > 1 ? 0 : 0.5,
k = orient === top || orient === left ? -1 : 1,
x = orient === left || orient === right ? "x" : "y",
transform = orient === top || orient === bottom ? translateX : translateY;
transform = orient === top || orient === bottom ? translateX : translateY,
multiline = "auto",
norepeat = true;

function axis(context) {
var values = tickValues == null ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain()) : tickValues,
Expand Down Expand Up @@ -101,8 +103,7 @@ function axis(orient, scale) {
.attr(x + "2", k * tickSizeInner);

text
.attr(x, k * spacing)
.text(format);
.call(maybeMultilineText(format, orient, x, k * spacing, multiline, norepeat));

selection.filter(entering)
.attr("fill", "none")
Expand Down Expand Up @@ -134,6 +135,14 @@ function axis(orient, scale) {
return arguments.length ? (tickFormat = _, axis) : tickFormat;
};

axis.tickMultiline = function(_) {
return arguments.length ? (multiline = _ === "auto" ? undefined : !!_, axis) : multiline;
}

axis.tickNoRepeat = function(_) {
return arguments.length ? (norepeat = !!_, axis) : norepeat;
};

axis.tickSize = function(_) {
return arguments.length ? (tickSizeInner = tickSizeOuter = +_, axis) : tickSizeInner;
};
Expand Down Expand Up @@ -172,3 +181,33 @@ export function axisBottom(scale) {
export function axisLeft(scale) {
return axis(left, scale);
}

function maybeMultilineText(format, orient, x, space, multiline, norepeat) {
const repeats = [];
const lines = [];
let multi = multiline === "auto" ? false : multiline;
return (text) => {
text
.attr(x, space)
.each(function(_, i) {
const label = format.apply(this, arguments);
const l = label == null
? []
: `${label}`.replace(/\s*$/m, "").split(/\r\n?|\n/);
if (l.length > 1 && multiline === "auto") multi = true;
lines[i] = l;
});
if (multi) {
text
.text("")
.selectAll("tspan")
.data((_, i) => lines[i])
.join("tspan")
.text((d, j) => norepeat && d === repeats[j] ? "\u00A0" : (repeats[j] = d))
.attr("x", orient === left || orient === right ? space : 0)
.attr("dy", (_, j) => j === 0 ? null : orient === top ? "-1.2em" : "1.2em");
} else {
text.text((d, i) => lines[i].join("\n"));
}
};
}
69 changes: 67 additions & 2 deletions test/snapshots.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {scaleLinear} from "d3-scale";
import {scaleLinear, scaleUtc} from "d3-scale";
import {create} from "d3-selection";
import {axisLeft} from "../src/index.js";
import {utcFormat} from "d3-time-format";
import {axisBottom, axisLeft, axisRight, axisTop} from "../src/index.js";

export function axisLeftScaleLinear() {
const svg = create("svg");
Expand All @@ -13,3 +14,67 @@ export function axisLeftScaleLinearNonNumericRange() {
svg.append("g").call(axisLeft(scaleLinear().range([0, "500"])));
return svg.node();
}

export function axisBottomScaleLinear() {
const x = scaleUtc([new Date(Date.UTC(2000, 4, 1)), new Date(Date.UTC(2002, 10, 1))], [10, 290]);
const axis = axisBottom(x).tickFormat(utcFormat("%b\n"));
const svg = create("svg");
svg.append("g").call(axis);
return svg.node();
}

export function axisBottomScaleLinearMultiLine() {
const x = scaleUtc([new Date(Date.UTC(2000, 4, 1)), new Date(Date.UTC(2002, 10, 1))], [10, 290]);
const axis = axisBottom(x).tickFormat(utcFormat("%b\n%Y"));
const svg = create("svg");
svg.append("g").call(axis);
return svg.node();
}

export function axisBottomScaleLinearQuarters() {
const x = scaleUtc([new Date(Date.UTC(2000, 4, 1)), new Date(Date.UTC(2001, 10, 1))], [10, 290]);
const axis = axisBottom(x).tickFormat(utcFormat("%b\n%Y\nQ%q"));
const svg = create("svg");
svg.append("g").call(axis);
return svg.node();
}

export function axisBottomScaleLinearRepeat() {
const x = scaleUtc([new Date(Date.UTC(2000, 4, 1)), new Date(Date.UTC(2002, 10, 1))], [10, 290]);
const axis = axisBottom(x).tickFormat(utcFormat("%b\n%Y")).tickNoRepeat(false);
const svg = create("svg");
svg.append("g").call(axis);
return svg.node();
}

export function axisBottomScaleLinearNotMultiLine() {
const x = scaleUtc([new Date(Date.UTC(2000, 4, 1)), new Date(Date.UTC(2002, 10, 1))], [10, 290]);
const axis = axisBottom(x).tickFormat(utcFormat("%b\n|")).tickMultiline(false);
const svg = create("svg");
svg.append("g").call(axis);
return svg.node();
}

export function axisTopScaleLinearMultiLine() {
const x = scaleUtc([new Date(Date.UTC(2000, 4, 1)), new Date(Date.UTC(2002, 10, 1))], [10, 290]);
const axis = axisTop(x).tickFormat(utcFormat("%b\n%Y"));
const svg = create("svg");
svg.append("g").attr("transform", "translate(0,40)").call(axis);
return svg.node();
}

export function axisLeftScaleLinearMultiLine() {
const x = scaleUtc([new Date(Date.UTC(2000, 4, 1)), new Date(Date.UTC(2002, 10, 1))], [10, 290]);
const axis = axisLeft(x).tickFormat(utcFormat("%b\n%Y"));
const svg = create("svg");
svg.append("g").attr("transform", "translate(40,0)").call(axis);
return svg.node();
}

export function axisRightScaleLinearMultiLine() {
const x = scaleUtc([new Date(Date.UTC(2000, 4, 1)), new Date(Date.UTC(2002, 10, 1))], [10, 290]);
const axis = axisRight(x).tickFormat(utcFormat("%b\n%Y"));
const svg = create("svg");
svg.append("g").attr("transform", "translate(40,0)").call(axis);
return svg.node();
}
36 changes: 36 additions & 0 deletions test/snapshots/axisBottomScaleLinear.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<svg>
<g fill="none" font-size="10" font-family="sans-serif" text-anchor="middle">
<path class="domain" stroke="currentColor" d="M10.5,6V0.5H290.5V6"></path>
<g class="tick" opacity="1" transform="translate(29.187089715536104,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Jul</text>
</g>
<g class="tick" opacity="1" transform="translate(57.37089715536105,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Oct</text>
</g>
<g class="tick" opacity="1" transform="translate(85.554704595186,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Jan</text>
</g>
<g class="tick" opacity="1" transform="translate(113.1258205689278,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Apr</text>
</g>
<g class="tick" opacity="1" transform="translate(141.00328227571117,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Jul</text>
</g>
<g class="tick" opacity="1" transform="translate(169.1870897155361,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Oct</text>
</g>
<g class="tick" opacity="1" transform="translate(197.37089715536106,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Jan</text>
</g>
<g class="tick" opacity="1" transform="translate(224.94201312910283,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Apr</text>
</g>
<g class="tick" opacity="1" transform="translate(252.81947483588624,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Jul</text>
</g>
<g class="tick" opacity="1" transform="translate(281.0032822757111,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Oct</text>
</g>
</g>
</svg>
66 changes: 66 additions & 0 deletions test/snapshots/axisBottomScaleLinearMultiLine.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<!DOCTYPE html>
<svg>
<g fill="none" font-size="10" font-family="sans-serif" text-anchor="middle">
<path class="domain" stroke="currentColor" d="M10.5,6V0.5H290.5V6"></path>
<g class="tick" opacity="1" transform="translate(29.187089715536104,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">
<tspan x="0">Jul</tspan>
<tspan x="0" dy="1.2em">2000</tspan>
</text>
</g>
<g class="tick" opacity="1" transform="translate(57.37089715536105,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">
<tspan x="0">Oct</tspan>
<tspan x="0" dy="1.2em">&nbsp;</tspan>
</text>
</g>
<g class="tick" opacity="1" transform="translate(85.554704595186,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">
<tspan x="0">Jan</tspan>
<tspan x="0" dy="1.2em">2001</tspan>
</text>
</g>
<g class="tick" opacity="1" transform="translate(113.1258205689278,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">
<tspan x="0">Apr</tspan>
<tspan x="0" dy="1.2em">&nbsp;</tspan>
</text>
</g>
<g class="tick" opacity="1" transform="translate(141.00328227571117,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">
<tspan x="0">Jul</tspan>
<tspan x="0" dy="1.2em">&nbsp;</tspan>
</text>
</g>
<g class="tick" opacity="1" transform="translate(169.1870897155361,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">
<tspan x="0">Oct</tspan>
<tspan x="0" dy="1.2em">&nbsp;</tspan>
</text>
</g>
<g class="tick" opacity="1" transform="translate(197.37089715536106,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">
<tspan x="0">Jan</tspan>
<tspan x="0" dy="1.2em">2002</tspan>
</text>
</g>
<g class="tick" opacity="1" transform="translate(224.94201312910283,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">
<tspan x="0">Apr</tspan>
<tspan x="0" dy="1.2em">&nbsp;</tspan>
</text>
</g>
<g class="tick" opacity="1" transform="translate(252.81947483588624,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">
<tspan x="0">Jul</tspan>
<tspan x="0" dy="1.2em">&nbsp;</tspan>
</text>
</g>
<g class="tick" opacity="1" transform="translate(281.0032822757111,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">
<tspan x="0">Oct</tspan>
<tspan x="0" dy="1.2em">&nbsp;</tspan>
</text>
</g>
</g>
</svg>
46 changes: 46 additions & 0 deletions test/snapshots/axisBottomScaleLinearNotMultiLine.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<svg>
<g fill="none" font-size="10" font-family="sans-serif" text-anchor="middle">
<path class="domain" stroke="currentColor" d="M10.5,6V0.5H290.5V6"></path>
<g class="tick" opacity="1" transform="translate(29.187089715536104,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Jul
|</text>
</g>
<g class="tick" opacity="1" transform="translate(57.37089715536105,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Oct
|</text>
</g>
<g class="tick" opacity="1" transform="translate(85.554704595186,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Jan
|</text>
</g>
<g class="tick" opacity="1" transform="translate(113.1258205689278,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Apr
|</text>
</g>
<g class="tick" opacity="1" transform="translate(141.00328227571117,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Jul
|</text>
</g>
<g class="tick" opacity="1" transform="translate(169.1870897155361,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Oct
|</text>
</g>
<g class="tick" opacity="1" transform="translate(197.37089715536106,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Jan
|</text>
</g>
<g class="tick" opacity="1" transform="translate(224.94201312910283,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Apr
|</text>
</g>
<g class="tick" opacity="1" transform="translate(252.81947483588624,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Jul
|</text>
</g>
<g class="tick" opacity="1" transform="translate(281.0032822757111,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">Oct
|</text>
</g>
</g>
</svg>
48 changes: 48 additions & 0 deletions test/snapshots/axisBottomScaleLinearQuarters.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<svg>
<g fill="none" font-size="10" font-family="sans-serif" text-anchor="middle">
<path class="domain" stroke="currentColor" d="M10.5,6V0.5H290.5V6"></path>
<g class="tick" opacity="1" transform="translate(41.611111111111114,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">
<tspan x="0">Jul</tspan>
<tspan x="0" dy="1.2em">2000</tspan>
<tspan x="0" dy="1.2em">Q3</tspan>
</text>
</g>
<g class="tick" opacity="1" transform="translate(88.53278688524588,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">
<tspan x="0">Oct</tspan>
<tspan x="0" dy="1.2em">&nbsp;</tspan>
<tspan x="0" dy="1.2em">Q4</tspan>
</text>
</g>
<g class="tick" opacity="1" transform="translate(135.45446265938068,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">
<tspan x="0">Jan</tspan>
<tspan x="0" dy="1.2em">2001</tspan>
<tspan x="0" dy="1.2em">Q1</tspan>
</text>
</g>
<g class="tick" opacity="1" transform="translate(181.35610200364297,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">
<tspan x="0">Apr</tspan>
<tspan x="0" dy="1.2em">&nbsp;</tspan>
<tspan x="0" dy="1.2em">Q2</tspan>
</text>
</g>
<g class="tick" opacity="1" transform="translate(227.76775956284152,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">
<tspan x="0">Jul</tspan>
<tspan x="0" dy="1.2em">&nbsp;</tspan>
<tspan x="0" dy="1.2em">Q3</tspan>
</text>
</g>
<g class="tick" opacity="1" transform="translate(274.6894353369763,0)">
<line stroke="currentColor" y2="6"></line><text fill="currentColor" y="9" dy="0.71em">
<tspan x="0">Oct</tspan>
<tspan x="0" dy="1.2em">&nbsp;</tspan>
<tspan x="0" dy="1.2em">Q4</tspan>
</text>
</g>
</g>
</svg>
Loading