export function multilayerAxisEntry()

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,
    },
  );
}