in packages/charts/src/chart_types/xy_chart/axes/timeslip/multilayer_ticks.ts [44:138]
export function multilayerAxisEntry(
xDomain: XDomain,
extendByOneBin: boolean,
range: [number, number],
timeAxisLayerCount: number,
scale: ScaleContinuous,
getMeasuredTicks: GetMeasuredTicks,
locale: string,
dow: number,
): Projection {
const rasterSelector = continuousTimeRasters(
{ minimumTickPixelDistance: MINIMUM_TICK_PIXEL_DISTANCE, locale, dow },
xDomain.timeZone,
);
const domainValues = xDomain.domain; // todo consider a property or object type rename
const domainFromS = Number(domainValues[0]) / 1000; // todo rely on a type guard or check rather than conversion
const binWidthMs = xDomain.minInterval;
const binWidth = binWidthMs / 1000; // seconds to milliseconds
const domainExtension = extendByOneBin ? binWidthMs : 0;
const domainToS = ((Number(domainValues.at(-1)) || NaN) + domainExtension) / 1000;
const cartesianWidth = Math.abs(range[1] - range[0]);
const layers = rasterSelector(notTooDense(domainFromS, domainToS, binWidth, cartesianWidth, MAX_TIME_TICK_COUNT));
let layerIndex = -1;
const fillLayerTimeslip = (
layer: number,
detailedLayer: number,
timeTicks: number[],
labelFormat: AxisLabelFormatter<number>,
showGrid: boolean,
) => {
return {
entry: getMeasuredTicks(scale, timeTicks, layer, detailedLayer, labelFormat as AxisLabelFormatter, showGrid),
fallbackAskedTickCount: NaN,
};
};
const binStartsFrom = domainFromS - binWidth;
const binStartsTo = domainToS + binWidth;
return layers.reduce<Projection>(
(combinedEntry, l, detailedLayerIndex) => {
if (l.labeled) layerIndex++; // we want three (or however many) _labeled_ axis layers; others are useful for minor ticks/gridlines, and for giving coarser structure eg. stronger gridline for every 6th hour of the day
if (layerIndex >= timeAxisLayerCount) return combinedEntry;
const timeTicks = [...l.intervals(binStartsFrom, binStartsTo)]
.filter((b) => {
if (b.labelSupremum !== b.supremum && b.minimum < domainFromS) return false;
return b.supremum > domainFromS && b.minimum <= domainToS;
})
.map((b) => 1000 * b.minimum);
if (timeTicks.length === 0) {
return combinedEntry;
}
const { entry } = fillLayerTimeslip(
layerIndex,
detailedLayerIndex,
timeTicks,
!l.labeled ? () => '' : layerIndex === timeAxisLayerCount - 1 ? l.detailedLabelFormat : l.minorTickLabelFormat,
notTooDense(domainFromS, domainToS, binWidth, cartesianWidth, MAX_TIME_GRID_COUNT)(l),
);
const minLabelGap = 4;
const lastTick = entry.ticks.at(-1);
if (lastTick && lastTick.position + entry.labelBox.maxLabelBboxWidth > range[1]) {
lastTick.label = '';
}
return {
...combinedEntry,
...entry,
ticks: (combinedEntry.ticks || []).concat(
entry.ticks.filter(
(tick, i, a) =>
i > 0 ||
!a[1] ||
a[1].domainClampedPosition - tick.domainClampedPosition >= entry.labelBox.maxLabelBboxWidth + minLabelGap,
),
),
};
},
{
ticks: [],
labelBox: {
maxLabelBboxWidth: 0,
maxLabelBboxHeight: 0,
maxLabelTextWidth: 0,
maxLabelTextHeight: 0,
isHidden: true,
},
scale,
},
);
}