-
Notifications
You must be signed in to change notification settings - Fork 172
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* axis mark * deferred channels * vector tick * tickPadding * grid * grid * no rounded stroke * fix half-pixel offset * more conditional axes * grid mark * DRY tick * tick[XY] * explicit ticks * axisTick[XY] * axis mark data * axis insets * tickFormat * better default tick format * simpler inferTickFormat * auto ticks; expose scale.interval * anchor * manual axis label, for now * tickRotate * fix crash in sparse excludeIndex * facetAnchor; fix mark-level missing facet * simpler facet sorting * fix #522; better facetAnchor * fix #1198; better empty facets * fix for ordinal scales * soft hyphen * test label wrapping * facet grid * better facet grid * better facet grid, again * better grid * better facet axes; fix font variant * extract facetSkip * empty facets * destructure options * remove redundant defaults * fix tickPadding, tickRotate * better grid defaults * more empty facets * simpler facet warning * decoration * better empty facets * better facet sort * extract facetAnchor * extract more facet logic * const facets * unify code * more facet anchors * invert facetAnchor * checkpoint axis label * checkpoint axis mark integration * fix minimum offset margin * fix for null marks and scales * better implicit axes * denote side-effect * better axis options * implicit facet axes * more implicit axis options * inferAxes * delete old axes * minimize diff * fix default margins * remove mark.decoration * facet margin options * move side effect * inheritScaleLabels * remove comment * better ARIA labels; restore prior axis order * regenerate tests; smarter partial implicit axes * more smarter partial implicit axes * axis line * text stroke options * don’t inline text elements * do inline title elements * axis label * fix axes for interval scales * gridDasharray * better implicit axis * x-axis label * another fancy axis example * polish example * fancy multi-line time ticks * another fancy axis example * remove secondary grid shorthand * tweak y-axis label position * first stab at documentation * the grid doesn't follow anchor * better deferred channels * better explicit built-in axes * fix metroUnemploymentRidgeline test * use insetTop for better semantics * fix label option inheritance * axis = both * fix comment * a few fixes to axis label position * labelAnchor * smarter default labelAnchor * fix googleTrendsRidgeline * fix longLabels * non-fix for named time intervals * fix projectionHeightGeometry * non-fix for x-axis label position * better axis label position * inset axis label * fix #375; respect facet margins * fix industryUnemploymentTrack * restore grid in penguinCulmen * facet margin collapse * better facet margin collapse * drop comment * labelOffset * fix auto labels and arrows * named exports for tests * enable facet axis labels * add non-faceted athletesSample test * better facet axis label position * cleaner * enforce constant tick options * shorter athletesSportWeight * remove dead code * checkpoint cross-facet marks * checkpoint supermarks * super option * better super marks * drop unused mid facet anchors * disallow superposition, for now * facet = super * minimize diff * fix axis label position, finally? * better hexbin tests * polish tests * fix named time intervals for ticks * grid xy shorthand * consistent scale checks * better axis options * tickSpacing option * propagate tickSpacing option * fix off-by-one with interval ticks * typo (thanks, @yurivish!) * only one label for both axes * a bit of documentation for axisX * given data, don’t defer channels * reduce aaplCloseDataTicks test * implicit grid with explicit axis * more grid shorthand * Update README * fix default facet axis anchor * axis plot shorthand * Update README --------- Co-authored-by: Philippe Rivière <fil@rezo.net>
- Loading branch information
Showing
400 changed files
with
80,012 additions
and
83,679 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,172 +1,22 @@ | ||
import {extent} from "d3"; | ||
import {AxisX, AxisY} from "./axis.js"; | ||
import {formatDefault} from "./format.js"; | ||
import {isOrdinalScale, isTemporalScale, scaleOrder} from "./scales.js"; | ||
import {position, registry} from "./scales/index.js"; | ||
|
||
export function Axes( | ||
{x: xScale, y: yScale, fx: fxScale, fy: fyScale}, | ||
{ | ||
x = {}, | ||
y = {}, | ||
fx = {}, | ||
fy = {}, | ||
axis = true, | ||
grid, | ||
line, | ||
label, | ||
facet: {axis: facetAxis = axis, grid: facetGrid, label: facetLabel = label} = {} | ||
} = {} | ||
) { | ||
let {axis: xAxis = axis} = x; | ||
let {axis: yAxis = axis} = y; | ||
let {axis: fxAxis = facetAxis} = fx; | ||
let {axis: fyAxis = facetAxis} = fy; | ||
if (!xScale) xAxis = null; | ||
else if (xAxis === true) xAxis = "bottom"; | ||
if (!yScale) yAxis = null; | ||
else if (yAxis === true) yAxis = "left"; | ||
if (!fxScale) fxAxis = null; | ||
else if (fxAxis === true) fxAxis = xAxis === "bottom" ? "top" : "bottom"; | ||
if (!fyScale) fyAxis = null; | ||
else if (fyAxis === true) fyAxis = yAxis === "left" ? "right" : "left"; | ||
return { | ||
...(xAxis && {x: new AxisX(xScale, {grid, line, label, ...x, axis: xAxis})}), | ||
...(yAxis && {y: new AxisY(yScale, {grid, line, label, ...y, axis: yAxis})}), | ||
...(fxAxis && {fx: new AxisX(fxScale, {name: "fx", grid: facetGrid, label: facetLabel, ...fx, axis: fxAxis})}), | ||
...(fyAxis && {fy: new AxisY(fyScale, {name: "fy", grid: facetGrid, label: facetLabel, ...fy, axis: fyAxis})}) | ||
}; | ||
} | ||
|
||
// Mutates axis.ticks! | ||
// TODO Populate tickFormat if undefined, too? | ||
export function autoAxisTicks({x, y, fx, fy}, {x: xAxis, y: yAxis, fx: fxAxis, fy: fyAxis}) { | ||
if (fxAxis) autoAxisTicksK(fx, fxAxis, 80); | ||
if (fyAxis) autoAxisTicksK(fy, fyAxis, 35); | ||
if (xAxis) autoAxisTicksK(x, xAxis, 80); | ||
if (yAxis) autoAxisTicksK(y, yAxis, 35); | ||
} | ||
|
||
function autoAxisTicksK(scale, axis, k) { | ||
if (axis.ticks === undefined) { | ||
const interval = scale.interval; | ||
if (interval !== undefined) { | ||
const [min, max] = extent(scale.scale.domain()); | ||
axis.ticks = interval.range(interval.floor(min), interval.offset(interval.floor(max))); | ||
} else { | ||
const [min, max] = extent(scale.scale.range()); | ||
axis.ticks = (max - min) / k; | ||
} | ||
} | ||
// D3’s ordinal scales simply use toString by default, but if the ordinal | ||
// scale domain (or ticks) are numbers or dates (say because we’re applying a | ||
// time interval to the ordinal scale), we want Plot’s default formatter. | ||
if (axis.tickFormat === undefined && isOrdinalScale(scale)) { | ||
axis.tickFormat = formatDefault; | ||
} | ||
} | ||
|
||
// Mutates axis.{label,labelAnchor,labelOffset} and scale.label! | ||
export function autoScaleLabels(channels, scales, {x, y, fx, fy}, dimensions, options) { | ||
if (fx) { | ||
autoAxisLabelsX(fx, scales.fx, channels.get("fx")); | ||
if (fx.labelOffset === undefined) { | ||
const {facetMarginTop, facetMarginBottom} = dimensions; | ||
fx.labelOffset = fx.axis === "top" ? facetMarginTop : facetMarginBottom; | ||
} | ||
} | ||
if (fy) { | ||
autoAxisLabelsY(fy, fx, scales.fy, channels.get("fy")); | ||
if (fy.labelOffset === undefined) { | ||
const {facetMarginLeft, facetMarginRight} = dimensions; | ||
fy.labelOffset = fy.axis === "left" ? facetMarginLeft : facetMarginRight; | ||
} | ||
} | ||
if (x) { | ||
autoAxisLabelsX(x, scales.x, channels.get("x")); | ||
if (x.labelOffset === undefined) { | ||
const {marginTop, marginBottom, facetMarginTop, facetMarginBottom} = dimensions; | ||
x.labelOffset = x.axis === "top" ? marginTop - facetMarginTop : marginBottom - facetMarginBottom; | ||
} | ||
} | ||
if (y) { | ||
autoAxisLabelsY(y, x, scales.y, channels.get("y")); | ||
if (y.labelOffset === undefined) { | ||
const {marginRight, marginLeft, facetMarginLeft, facetMarginRight} = dimensions; | ||
y.labelOffset = y.axis === "left" ? marginLeft - facetMarginLeft : marginRight - facetMarginRight; | ||
} | ||
} | ||
for (const [key, type] of registry) { | ||
if (type !== position && scales[key]) { | ||
// not already handled above | ||
autoScaleLabel(key, scales[key], channels.get(key), options[key]); | ||
} | ||
} | ||
} | ||
|
||
// Mutates axis.labelAnchor, axis.label, scale.label! | ||
function autoAxisLabelsX(axis, scale, channels) { | ||
if (axis.labelAnchor === undefined) { | ||
axis.labelAnchor = isOrdinalScale(scale) ? "center" : scaleOrder(scale) < 0 ? "left" : "right"; | ||
} | ||
if (axis.label === undefined) { | ||
axis.label = inferLabel(channels, scale, axis, "x"); | ||
} | ||
scale.label = axis.label; | ||
} | ||
|
||
// Mutates axis.labelAnchor, axis.label, scale.label! | ||
function autoAxisLabelsY(axis, opposite, scale, channels) { | ||
if (axis.labelAnchor === undefined) { | ||
axis.labelAnchor = isOrdinalScale(scale) | ||
? "center" | ||
: opposite && opposite.axis === "top" | ||
? "bottom" // TODO scaleOrder? | ||
: "top"; | ||
} | ||
if (axis.label === undefined) { | ||
axis.label = inferLabel(channels, scale, axis, "y"); | ||
} | ||
scale.label = axis.label; | ||
} | ||
|
||
// Mutates scale.label! | ||
function autoScaleLabel(key, scale, channels, options) { | ||
if (options) { | ||
scale.label = options.label; | ||
} | ||
if (scale.label === undefined) { | ||
scale.label = inferLabel(channels, scale, null, key); | ||
} | ||
} | ||
|
||
// Channels can have labels; if all the channels for a given scale are | ||
// consistently labeled (i.e., have the same value if not undefined), and the | ||
// corresponding axis doesn’t already have an explicit label, then the channels’ | ||
// label is promoted to the corresponding axis. | ||
function inferLabel(channels = [], scale, axis, key) { | ||
let candidate; | ||
for (const {label} of channels) { | ||
if (label === undefined) continue; | ||
if (candidate === undefined) candidate = label; | ||
else if (candidate !== label) return; | ||
} | ||
if (candidate !== undefined) { | ||
// Ignore the implicit label for temporal scales if it’s simply “date”. | ||
if (isTemporalScale(scale) && /^(date|time|year)$/i.test(candidate)) return; | ||
if (!isOrdinalScale(scale)) { | ||
if (scale.percent) candidate = `${candidate} (%)`; | ||
if (key === "x" || key === "y") { | ||
const order = scaleOrder(scale); | ||
if (order) { | ||
if (key === "x" || (axis && axis.labelAnchor === "center")) { | ||
candidate = (key === "x") === order < 0 ? `← ${candidate}` : `${candidate} →`; | ||
} else { | ||
candidate = `${order < 0 ? "↑ " : "↓ "}${candidate}`; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return candidate; | ||
import {format, utcFormat} from "d3"; | ||
import {formatIsoDate} from "./format.js"; | ||
import {constant, isTemporal, string} from "./options.js"; | ||
import {isOrdinalScale} from "./scales.js"; | ||
|
||
export function inferFontVariant(scale) { | ||
return isOrdinalScale(scale) && scale.interval === undefined ? undefined : "tabular-nums"; | ||
} | ||
|
||
// D3 doesn’t provide a tick format for ordinal scales; we want shorthand when | ||
// an ordinal domain is numbers or dates, and we want null to mean the empty | ||
// string, not the default identity format. TODO Remove this in favor of the | ||
// axis mark’s inferTickFormat. | ||
export function maybeAutoTickFormat(tickFormat, domain) { | ||
return tickFormat === undefined | ||
? isTemporal(domain) | ||
? formatIsoDate | ||
: string | ||
: typeof tickFormat === "function" | ||
? tickFormat | ||
: (typeof tickFormat === "string" ? (isTemporal(domain) ? utcFormat : format) : constant)(tickFormat); | ||
} |
Oops, something went wrong.