Skip to the content

Capsid menu

src/render/series.ts

54 lines and 6 functions in DrDustinEdwards/enarratio, at commit 4c5695a on 2026-10-24. This file on GitHub.

Functions

The functions of series.ts
FunctionLinesComplexityCoveredCRAPMutation
slotColor17 to 191100%OkCRAP 1Ok100%
planSeries26 to 585100%OkCRAP 5Ok87.5%
<anonymous:36>36 to 361100%OkCRAP 1Ok100%
<anonymous:49>49 to 491100%OkCRAP 1Ok100%
<anonymous:50>50 to 501100%OkCRAP 1Ok100%
slot52 to 56275%OkCRAP 2.1Watch60%

Imports

It imports src/render/figure.ts. Imported by src/plot/scatter.ts, src/render/bar-model.ts, src/render/line-model.ts, src/science/genome.ts, src/science/network.ts, src/science/titer.ts.

Source

import type { LegendItem, SeriesColor } from "./figure.js";

/** The number of palette slots every theme defines, and so the most series one chart can show. */
const MAX_SERIES = 8;

/** How a chart's series map onto palette slots. */
export interface SeriesPlan {
  readonly names: readonly string[];
  /** The CSS color reference for each series, in order, for Plot's color scale range. */
  readonly range: readonly string[];
  readonly legend: readonly LegendItem[];
  readonly slotColors: ReadonlyMap<number, SeriesColor>;
  slot(name: string): number;
}

/** A CSS reference to a palette slot. */
function slotColor(slot: number): string {
  return `var(--enarratio-series-${slot})`;
}

/**
 * Assigns series to palette slots in order, and applies per-series color overrides to their
 * slots. Throws when there are more series than slots or an override names no series, because
 * both would otherwise draw a chart that is quietly wrong.
 */
export function planSeries(
  kind: string,
  names: readonly string[],
  colors: Readonly<Record<string, SeriesColor>> | undefined,
): SeriesPlan {
  if (names.length > MAX_SERIES) {
    throw new Error(
      `${kind}: ${names.length} series, but a theme has ${MAX_SERIES} colors; group the smallest series into "Other"`,
    );
  }
  const slots = new Map(names.map((name, i) => [name, i + 1]));
  const slotColors = new Map<number, SeriesColor>();
  for (const [name, color] of Object.entries(colors ?? {})) {
    const slot = slots.get(name);
    if (slot === undefined) {
      throw new Error(
        `${kind}: colors names "${name}", which is not a series (${names.join(", ")})`,
      );
    }
    slotColors.set(slot, color);
  }
  return {
    names,
    range: names.map((_, i) => slotColor(i + 1)),
    legend: names.map((label, i) => ({ label, slot: i + 1 })),
    slotColors,
    slot(name) {
      const slot = slots.get(name);
      if (slot === undefined) throw new Error(`${kind}: unknown series "${name}"`);
      return slot;
    },
  };
}