declare class uPlot {
  /** when passing a function for @targ, call init() after attaching self.root to the DOM */
  constructor(
    opts: uPlot.Options,
    data?: uPlot.AlignedData,
    targ?: HTMLElement | ((self: uPlot, init: Function) => void),
  );

  /** chart container */
  readonly root: HTMLElement;

  /** status */
  readonly status: 0 | 1;

  /** width of the plotting area + axes in CSS pixels */
  readonly width: number;

  /** height of the plotting area + axes in CSS pixels (excludes title & legend height) */
  readonly height: number;

  /** current devicePixelRatio */
  readonly pxRatio: number;

  /** context of canvas used for plotting area + axes */
  readonly ctx: CanvasRenderingContext2D;

  /** coords of plotting area in canvas pixels (relative to full canvas w/axes) */
  readonly bbox: uPlot.BBox;

  /** cached global DOMRect of plotting area in CSS pixels */
  get rect(): DOMRect;

  /** coords of selected region in CSS pixels (relative to plotting area) */
  readonly select: uPlot.BBox;

  /** cursor state & opts*/
  readonly cursor: uPlot.Cursor;

  readonly legend: uPlot.Legend;

  //	/** focus opts */
  //	readonly focus: uPlot.Focus;

  /** series state & opts */
  readonly series: uPlot.Series[];

  /** scales state & opts */
  readonly scales: {
    [key: string]: uPlot.Scale;
  };

  /** axes state & opts */
  readonly axes: uPlot.Axis[];

  /** hooks, including any added by plugins */
  readonly hooks: uPlot.Hooks.Arrays;

  /** current data */
  readonly data: uPlot.AlignedData;

  /** .u-over dom element */
  readonly over: HTMLDivElement;

  /** .u-under dom element */
  readonly under: HTMLDivElement;

  /** clears and redraws the canvas. if rebuildPaths = false, uses cached series' Path2D objects */
  redraw(rebuildPaths?: boolean, recalcAxes?: boolean): void;

  /** manual batching of multiple ops (aka immediate mode that skips implicit microtask queue), ops, e.g. setScale('x', ...) && setScale('y', ...) */
  batch(txn: Function, deferHooks?: boolean): void;

  /** destroys DOM, removes resize & scroll listeners, etc. */
  destroy(): void;

  /** sets the chart data & redraws. (default resetScales = true) */
  setData(data: uPlot.AlignedData, resetScales?: boolean): void;

  /** sets the limits of a scale & redraws (used for zooming) */
  setScale(scaleKey: string, limits: { min: number; max: number }): void;

  /** sets the cursor position (relative to plotting area) */
  setCursor(opts: { left: number; top: number }, fireHook?: boolean): void;

  /** sets the legend to the values of the specified idx */
  setLegend(
    opts: { idx?: number; idxs?: (number | null)[] },
    fireHook?: boolean,
  ): void;

  // TODO: include other series style opts which are dynamically pulled?
  /** toggles series visibility or focus */
  setSeries(
    seriesIdx: number | null,
    opts: { show?: boolean; focus?: boolean },
    fireHook?: boolean,
  ): void;

  /** adds a series */
  addSeries(opts: uPlot.Series, seriesIdx?: number): void;

  /** deletes a series */
  delSeries(seriesIdx: number): void;

  /** adds a band */
  addBand(opts: uPlot.Band, bandIdx?: number): void;

  /** modifies an existing band */
  setBand(bandIdx: number, opts: uPlot.Band): void;

  /** deletes a band. if null bandIdx, clears all bands */
  delBand(bandIdx?: number | null): void;

  /** sets visually selected region without triggering setScale (zoom). (default fireHook = true) */
  setSelect(
    opts: { left: number; top: number; width: number; height: number },
    fireHook?: boolean,
  ): void;

  /** sets the width & height of the plotting area + axes (excludes title & legend height) */
  setSize(opts: { width: number; height: number }): void;

  /** temporarily sets a user-defined devicePixelRatio that's different from window.devicePixelRatio */
  setPxRatio(pxRatio?: number | null): void;

  /** converts a CSS pixel position (relative to plotting area) to the closest data index */
  posToIdx(left: number, canvasPixels?: boolean): number;

  /** converts a CSS pixel position (relative to plotting area) to a value along the given scale */
  posToVal(leftTop: number, scaleKey: string, canvasPixels?: boolean): number;

  /** converts a value along the given scale to a CSS (default) or canvas pixel position. (default canvasPixels = false) */
  valToPos(val: number, scaleKey: string, canvasPixels?: boolean): number;

  /** converts a value along x to the closest data index */
  valToIdx(val: number): number;

  /** updates getBoundingClientRect() cache for cursor positioning. use when plot's position changes (excluding window scroll & resize) */
  syncRect(defer?: boolean): void;

  /** uPlot's path-builder factories */
  static paths: uPlot.Series.PathBuilderFactories;

  /** a deep merge util fn */
  static assign(targ: object, ...srcs: object[]): object;

  /** re-ranges a given min/max by a multiple of the range's magnitude (used internally to expand/snap/pad numeric y scales) */
  static rangeNum(
    min: number,
    max: number,
    mult: number,
    extra: boolean,
  ): uPlot.Range.MinMax;
  static rangeNum(
    min: number,
    max: number,
    cfg: uPlot.Range.Config,
  ): uPlot.Range.MinMax;

  /** re-ranges a given min/max outwards to nearest 10% of given min/max's magnitudes, unless fullMags = true */
  static rangeLog(
    min: number,
    max: number,
    base: uPlot.Scale.LogBase,
    fullMags: boolean,
  ): uPlot.Range.MinMax;

  /** re-ranges a given min/max outwards to nearest 10% of given min/max's magnitudes, unless fullMags = true */
  static rangeAsinh(
    min: number,
    max: number,
    base: uPlot.Scale.LogBase,
    fullMags: boolean,
  ): uPlot.Range.MinMax;

  /** default numeric formatter using browser's locale: new Intl.NumberFormat(navigator.language).format */
  static fmtNum(val: number): string;

  /** creates an efficient formatter for Date objects from a template string, e.g. {YYYY}-{MM}-{DD} */
  static fmtDate(tpl: string, names?: uPlot.DateNames): (date: Date) => string;

  /** converts a Date into new Date that's time-adjusted for the given IANA Time Zone Name */
  static tzDate(date: Date, tzName: string): Date;

  /** outerJoins multiple data tables on table[0] values */
  static join(
    tables: uPlot.AlignedData[],
    nullModes?: uPlot.JoinNullMode[][],
  ): uPlot.AlignedData;

  static addGap: uPlot.Series.AddGap;

  static clipGaps: uPlot.Series.ClipPathBuilder;

  /** helper function for grabbing proper drawing orientation vars and fns for a plot instance (all dims in canvas pixels) */
  static orient(
    u: uPlot,
    seriesIdx: number,
    callback: uPlot.OrientCallback,
  ): any;

  /** returns a pub/sub instance shared by all plots using the provided key */
  static sync(key: string): uPlot.SyncPubSub;

  /** cached devicePixelRatio (faster than reading it from window.devicePixelRatio) */
  static pxRatio: number;
}

export = uPlot;

declare namespace uPlot {
  type OrientCallback = (
    series: Series,
    dataX: number[],
    dataY: (number | null)[],
    scaleX: Scale,
    scaleY: Scale,
    valToPosX: ValToPos,
    valToPosY: ValToPos,
    xOff: number,
    yOff: number,
    xDim: number,
    yDim: number,
    moveTo: MoveToH | MoveToV,
    lineTo: LineToH | LineToV,
    rect: RectH | RectV,
    arc: ArcH | ArcV,
    bezierCurveTo: BezierCurveToH | BezierCurveToV,
  ) => any;

  type ValToPos = (
    val: number,
    scale: Scale,
    fullDim: number,
    offset: number,
  ) => number;

  type Drawable = Path2D | CanvasRenderingContext2D;

  type MoveToH = (p: Drawable, x: number, y: number) => void;
  type MoveToV = (p: Drawable, y: number, x: number) => void;
  type LineToH = (p: Drawable, x: number, y: number) => void;
  type LineToV = (p: Drawable, y: number, x: number) => void;
  type RectH = (
    p: Drawable,
    x: number,
    y: number,
    w: number,
    h: number,
  ) => void;
  type RectV = (
    p: Drawable,
    y: number,
    x: number,
    h: number,
    w: number,
  ) => void;
  type ArcH = (
    p: Drawable,
    x: number,
    y: number,
    r: number,
    startAngle: number,
    endAngle: number,
  ) => void;
  type ArcV = (
    p: Drawable,
    y: number,
    x: number,
    r: number,
    startAngle: number,
    endAngle: number,
  ) => void;
  type BezierCurveToH = (
    p: Drawable,
    bp1x: number,
    bp1y: number,
    bp2x: number,
    bp2y: number,
    p2x: number,
    p2y: number,
  ) => void;
  type BezierCurveToV = (
    p: Drawable,
    bp1y: number,
    bp1x: number,
    bp2y: number,
    bp2x: number,
    p2y: number,
    p2x: number,
  ) => void;

  export const enum JoinNullMode {
    /** use for series with spanGaps: true */
    Remove = 0,
    /** retain explicit nulls gaps (default) */
    Retain = 1,
    /** expand explicit null gaps to include adjacent alignment artifacts (undefined values) */
    Expand = 2,
  }

  export const enum Orientation {
    Horizontal = 0,
    Vertical = 1,
  }

  export type TypedArray =
    | Int8Array
    | Uint8Array
    | Int16Array
    | Uint16Array
    | Int32Array
    | Uint32Array
    | Uint8ClampedArray
    | Float32Array
    | Float64Array;

  export type AlignedData =
    | TypedArray[]
    | [
        xValues: number[] | TypedArray,
        ...yValues: ((number | null | undefined)[] | TypedArray)[],
      ];

  export interface DateNames {
    /** long month names */
    MMMM: string[];

    /** short month names */
    MMM: string[];

    /** long weekday names (0: Sunday) */
    WWWW: string[];

    /** short weekday names (0: Sun) */
    WWW: string[];
  }

  export namespace Range {
    export type MinMax = [min: number | null, max: number | null];

    export type Function = (
      self: uPlot,
      initMin: number,
      initMax: number,
      scaleKey: string,
    ) => MinMax;

    export type SoftMode = 0 | 1 | 2 | 3;

    export interface Limit {
      /** initial multiplier for dataMax-dataMin delta */
      pad?: number; // 0.1

      /** soft limit */
      soft?: number; // 0

      /** soft limit active if... 0: never, 1: data <= limit, 2: data + padding <= limit, 3: data <= limit <= data + padding */
      mode?: SoftMode; // 3

      /** hard limit */
      hard?: number;
    }

    export interface Config {
      min: Range.Limit;
      max: Range.Limit;
    }
  }

  export interface Scales {
    [key: string]: Scale;
  }

  type SidesWithAxes = [
    top: boolean,
    right: boolean,
    bottom: boolean,
    left: boolean,
  ];

  export type PaddingSide =
    | number
    | null
    | ((
        self: uPlot,
        side: Axis.Side,
        sidesWithAxes: SidesWithAxes,
        cycleNum: number,
      ) => number);

  export type Padding = [
    top: PaddingSide,
    right: PaddingSide,
    bottom: PaddingSide,
    left: PaddingSide,
  ];

  export interface Legend {
    show?: boolean; // true
    /** show series values at current cursor.idx */
    live?: boolean; // true
    /** switches primary interaction mode to toggle-one/toggle-all */
    isolate?: boolean; // false
    /** series indicators */
    markers?: Legend.Markers;

    /** callback for moving the legend elsewhere. e.g. external DOM container */
    mount?: (self: uPlot, el: HTMLElement) => void;

    /** current index (readback-only, not for init) */
    idx?: number | null;
    /** current indices (readback-only, not for init) */
    idxs?: (number | null)[];
    /** current values (readback-only, not for init) */
    values?: Legend.Values;
  }

  export namespace Legend {
    export type Width = number | ((self: uPlot, seriesIdx: number) => number);

    export type Stroke =
      | CSSStyleDeclaration["borderColor"]
      | ((
          self: uPlot,
          seriesIdx: number,
        ) => CSSStyleDeclaration["borderColor"]);

    export type Dash =
      | CSSStyleDeclaration["borderStyle"]
      | ((
          self: uPlot,
          seriesIdx: number,
        ) => CSSStyleDeclaration["borderStyle"]);

    export type Fill =
      | CSSStyleDeclaration["background"]
      | ((self: uPlot, seriesIdx: number) => CSSStyleDeclaration["background"]);

    export type Value = {
      [key: string]: string | number;
    };

    export type Values = Value[];

    export interface Markers {
      show?: boolean; // true
      /** series indicator line width */
      width?: Legend.Width;
      /** series indicator stroke (CSS borderColor) */
      stroke?: Legend.Stroke;
      /** series indicator fill */
      fill?: Legend.Fill;
      /** series indicator stroke style (CSS borderStyle) */
      dash?: Legend.Dash;
    }
  }

  export type DateFormatterFactory = (tpl: string) => (date: Date) => string;

  export type LocalDateFromUnix = (ts: number) => Date;

  export const enum DrawOrderKey {
    Axes = "axes",
    Series = "series",
  }

  export const enum Mode {
    Aligned = 1,
    Faceted = 2,
  }

  export interface Options {
    /** 1: aligned & ordered, single-x / y-per-series, 2: unordered & faceted, per-series/per-point x,y,size,label,color,shape,etc. */
    mode?: Mode;

    /** chart title */
    title?: string;

    /** id to set on chart div */
    id?: string;

    /** className to add to chart div */
    class?: string;

    /** width of plotting area + axes in CSS pixels */
    width: number;

    /** height of plotting area + axes in CSS pixels (excludes title & legend height) */
    height: number;

    /** initial devicePixelRatio, if different than window.devicePixelRatio */
    pxRatio?: number;

    /** data for chart, if none is provided as argument to constructor */
    data?: AlignedData;

    /** converts a unix timestamp to Date that's time-adjusted for the desired timezone */
    tzDate?: LocalDateFromUnix;

    /** creates an efficient formatter for Date objects from a template string, e.g. {YYYY}-{MM}-{DD} */
    fmtDate?: DateFormatterFactory;

    /** timestamp multiplier that yields 1 millisecond */
    ms?: 1e-3 | 1; // 1e-3

    /** drawing order for axes/grid & series (default: ["axes", "series"]) */
    drawOrder?: DrawOrderKey[];

    /** whether vt & hz lines of series/grid/ticks should be crisp/sharp or sub-px antialiased */
    pxAlign?: boolean | number; // true

    series: Series[];

    bands?: Band[];

    scales?: Scales;

    axes?: Axis[];

    /** padding per side, in CSS pixels (can prevent cross-axis labels at the plotting area limits from being chopped off) */
    padding?: Padding;

    select?: Select;

    legend?: Legend;

    cursor?: Cursor;

    focus?: Focus;

    hooks?: Hooks.Arrays;

    plugins?: Plugin[];
  }

  export interface Focus {
    /** alpha-transparancy of de-focused series */
    alpha: number;
  }

  export interface BBox {
    show?: boolean;
    left: number;
    top: number;
    width: number;
    height: number;
  }

  export interface Select extends BBox {
    /** div into which .u-select will be placed: .u-over or .u-under */
    over?: boolean; // true
  }

  export interface SyncPubSub {
    key: string;
    sub: (client: uPlot) => void;
    unsub: (client: uPlot) => void;
    pub: (
      type: string,
      client: uPlot,
      x: number,
      y: number,
      w: number,
      h: number,
      i: number,
    ) => void;
    plots: uPlot[];
  }

  export namespace Cursor {
    export type LeftTop = [left: number, top: number];

    export type MouseListener = (e: MouseEvent) => void;

    export type MouseListenerFactory = (
      self: uPlot,
      targ: HTMLElement,
      handler: MouseListener,
    ) => MouseListener | null;

    export type DataIdxRefiner = (
      self: uPlot,
      seriesIdx: number,
      closestIdx: number,
      xValue: number,
    ) => number | null;

    export type MousePosRefiner = (
      self: uPlot,
      mouseLeft: number,
      mouseTop: number,
    ) => LeftTop;

    export interface Bind {
      mousedown?: MouseListenerFactory;
      mouseup?: MouseListenerFactory;
      click?: MouseListenerFactory;
      dblclick?: MouseListenerFactory;

      mousemove?: MouseListenerFactory;
      mouseleave?: MouseListenerFactory;
      mouseenter?: MouseListenerFactory;
    }

    export namespace Points {
      export type Show =
        | boolean
        | ((self: uPlot, seriesIdx: number) => HTMLElement);
      export type Size = number | ((self: uPlot, seriesIdx: number) => number);
      export type BBox = (self: uPlot, seriesIdx: number) => uPlot.BBox;
      export type Width =
        | number
        | ((self: uPlot, seriesIdx: number, size: number) => number);
      export type Stroke =
        | CanvasRenderingContext2D["strokeStyle"]
        | ((
            self: uPlot,
            seriesIdx: number,
          ) => CanvasRenderingContext2D["strokeStyle"]);
      export type Fill =
        | CanvasRenderingContext2D["fillStyle"]
        | ((
            self: uPlot,
            seriesIdx: number,
          ) => CanvasRenderingContext2D["fillStyle"]);
    }

    export interface Points {
      show?: Points.Show;
      /** only show single y-closest point on hover (only works when cursor.focus.prox >= 0) */
      one?: boolean;
      /** hover point diameter in CSS pixels */
      size?: Points.Size;
      /** hover point bbox in CSS pixels (will be used instead of size) */
      bbox?: Points.BBox;
      /** hover point outline width in CSS pixels */
      width?: Points.Width;
      /** hover point outline color, pattern or gradient */
      stroke?: Points.Stroke;
      /** hover point fill color, pattern or gradient */
      fill?: Points.Fill;
    }

    export interface Drag {
      setScale?: boolean; // true
      /** toggles dragging along x */
      x?: boolean; // true
      /** toggles dragging along y */
      y?: boolean; // false
      /** min drag distance threshold */
      dist?: number; // 0
      /** when x & y are true, sets an upper drag limit in CSS px for adaptive/unidirectional behavior */
      uni?: number; // null
      /** post-drag "click" event proxy, default is to prevent these click events */
      click?: (self: uPlot, e: MouseEvent) => void;
    }

    export namespace Sync {
      export type Scales = [xScaleKey: string | null, yScaleKey: string | null];

      export type Filter = (
        type: string,
        client: uPlot,
        x: number,
        y: number,
        w: number,
        h: number,
        i: number,
      ) => boolean;

      export interface Filters {
        /** filters emitted events */
        pub?: Filter;
        /** filters received events */
        sub?: Filter;
      }

      export type ScaleKeyMatcher = (
        subScaleKey: string | null,
        pubScaleKey: string | null,
      ) => boolean;

      export type SeriesIdxMatcher = (
        sub: uPlot,
        pub: uPlot,
        pubSeriesIdx: number,
      ) => number | null;

      export type Match = [
        matchX: ScaleKeyMatcher,
        matchY: ScaleKeyMatcher,
        matchSeriesIdx?: SeriesIdxMatcher,
      ];

      export type Values = [xScaleValue: number, yScaleValue: number];
    }

    export interface Sync {
      /** sync key must match between all charts in a synced group */
      key: string;
      /** determines if series toggling and focus via cursor is synced across charts */
      setSeries?: boolean; // true
      /** sets the x and y scales to sync by values. null will sync by relative (%) position */
      scales?: Sync.Scales; // [xScaleKey, null]
      /** fns that match x and y scale keys and seriesIdxs between publisher and subscriber */
      match?: Sync.Match;
      /** event filters */
      filters?: Sync.Filters;
      /** sync scales' values at the cursor position (exposed for read-back by subscribers) */
      values?: Sync.Values;
    }

    // options that compile the cursor.dataIdx callback (the index scanner)
    export interface Hover {
      /** minimum cursor proximity to datapoint in CSS pixels for point hover */
      prox?:
        | number
        | null
        | ((
            self: uPlot,
            seriesIdx: number,
            closestIdx: number,
            xValue: number,
          ) => number | null); // null/Infinity
      /** when non-zero, will only proximity-test indices forward or backward */
      bias?: HoverBias; // 0
      /** what values to treat as non-hoverable and trigger scanning to another index */
      skip?: any[]; // [undefined]
    }

    export interface Focus {
      /** minimum cursor proximity to datapoint in CSS pixels for focus activation, disabled: < 0, enabled: <= 1e6 */
      prox: number;
      /** when non-zero, will only focus next series towards or away from zero */
      bias?: FocusBias; // 0
      /** measures cursor y distance to a series in CSS pixels (for triggering setSeries hook with closest) */
      dist?: (
        self: uPlot,
        seriesIdx: number,
        dataIdx: number,
        valPos: number,
        curPos: number,
      ) => number;
    }

    export const enum FocusBias {
      None = 0,
      AwayFromZero = 1,
      TowardsZero = -1,
    }

    export const enum HoverBias {
      None = 0,
      Forward = 1,
      Backward = -1,
    }
  }

  export interface Cursor {
    /** cursor on/off */
    show?: boolean;

    /** vertical crosshair on/off */
    x?: boolean;

    /** horizontal crosshair on/off */
    y?: boolean;

    /** cursor position left offset in CSS pixels (relative to plotting area) */
    left?: number;

    /** cursor position top offset in CSS pixels (relative to plotting area) */
    top?: number;

    /** closest data index to cursor (closestIdx) */
    idx?: number | null;

    /** returns data idx used for hover points & legend display (defaults to closestIdx) */
    dataIdx?: Cursor.DataIdxRefiner;

    /** a series-matched array of indices returned by dataIdx() */
    idxs?: (number | null)[];

    /** fires on debounced mousemove events; returns refined [left, top] tuple to snap cursor position */
    move?: Cursor.MousePosRefiner;

    /** series hover points */
    points?: Cursor.Points;

    /** event listener proxies (can be overridden to tweak interaction behavior) */
    bind?: Cursor.Bind;

    /** determines vt/hz cursor dragging to set selection & setScale (zoom) */
    drag?: Cursor.Drag;

    /** sync cursor between multiple charts */
    sync?: Cursor.Sync;

    /** focus series closest to cursor (y) */
    focus?: Cursor.Focus;

    /** hover data points closest to cursor (x) */
    hover?: Cursor.Hover;

    /** lock cursor on mouse click in plotting area */
    lock?: boolean; // false

    /** the most recent mouse event */
    event?: MouseEvent;
  }

  export namespace Scale {
    export type Auto =
      | boolean
      | ((self: uPlot, resetScales: boolean) => boolean);

    export type Range = Range.MinMax | Range.Function | Range.Config;

    export const enum Distr {
      Linear = 1,
      Ordinal = 2,
      Logarithmic = 3,
      ArcSinh = 4,
      Custom = 100,
    }

    export type LogBase = 10 | 2;

    export type Clamp =
      | number
      | ((
          self: uPlot,
          val: number,
          scaleMin: number,
          scaleMax: number,
          scaleKey: string,
        ) => number);
  }

  export interface Scale {
    /** is this scale temporal, with series' data in UNIX timestamps? */
    time?: boolean;

    /** determines whether all series' data on this scale will be scanned to find the full min/max range */
    auto?: Scale.Auto;

    /** can define a static scale range or re-range an initially-determined range from series data */
    range?: Scale.Range;

    /** scale key from which this scale is derived */
    from?: string;

    /** scale distribution. 1: linear, 2: ordinal, 3: logarithmic, 4: arcsinh */
    distr?: Scale.Distr; // 1

    /** logarithmic base */
    log?: Scale.LogBase; // 10;

    /** clamps log scale values <= 0 (default = scaleMin / 10) */
    clamp?: Scale.Clamp;

    /** arcsinh linear threshold */
    asinh?: number; // 1

    /** forward transform fn, with custom distr: 100 */
    fwd?: (v: number) => number;
    /** backward transform fn, with custom distr: 100 */
    bwd?: (v: number) => number;

    /** current min scale value */
    min?: number;

    /** current max scale value */
    max?: number;

    /** scale direction */
    dir?: 1 | -1;

    /** scale orientation - 0: hz, 1: vt */
    ori?: 0 | 1;

    /** own key (for read-back) */
    key?: string;
  }

  export namespace Series {
    export interface Paths {
      /** path to stroke */
      stroke?:
        | Path2D
        | Map<CanvasRenderingContext2D["strokeStyle"], Path2D>
        | null;

      /** path to fill */
      fill?: Path2D | Map<CanvasRenderingContext2D["fillStyle"], Path2D> | null;

      /** path for clipping fill & stroke (used for gaps) */
      clip?: Path2D | null;

      /** yMin-ward (dir: -1) and/or yMax-ward (dir: 1) clips built using the stroke path (inverted dirs from band.dir fills) */
      band?: Path2D | null | [yMinClip: Path2D, yMaxClip: Path2D];

      /** tuples of canvas pixel coordinates that were used to construct the gaps clip */
      gaps?: [from: number, to: number][];

      /** line width in CSS pixels, if differs from series.width (for dynamic rendering optimization) */
      width?: number;

      /** fill style, if differs from series.fill (for dynamic rendering optimization) */
      _fill?: CanvasRenderingContext2D["fillStyle"];

      /** stroke style, if differs from series.stroke (for dynamic rendering optimization) */
      _stroke?: CanvasRenderingContext2D["strokeStyle"];

      /** bitmap of whether the band clip should be applied to stroke, fill, or both */
      flags?: number;
    }

    export interface SteppedPathBuilderOpts {
      align?: -1 | 1; // 1

      alignGaps?: -1 | 0 | 1; // 0

      // whether to draw ascenders/descenders at null/gap boundaries
      ascDesc?: boolean; // false

      // extend hz lines to plot edges when x scale is beyond x data limits?
      extend?: boolean; // false
    }

    export const enum BarsPathBuilderFacetUnit {
      ScaleValue = 1,
      PixelPercent = 2,
      Color = 3,
    }

    export const enum BarsPathBuilderFacetKind {
      Unary = 1,
      Discrete = 2,
      Continuous = 3,
    }

    export type BarsPathBuilderFacetValue =
      | string
      | number
      | boolean
      | null
      | undefined;

    export interface BarsPathBuilderFacet {
      /** unit of measure for output of values() */
      unit: BarsPathBuilderFacetUnit;
      /** are the values unary, discrete, or continuous */
      kind?: BarsPathBuilderFacetKind;
      /** values to use for this facet */
      values: (
        self: uPlot,
        seriesIdx: number,
        idx0: number,
        idx1: number,
      ) => BarsPathBuilderFacetValue[];
    }

    /** custom per-datapoint styling and positioning */
    export interface BarsPathBuilderDisplay {
      x0?: BarsPathBuilderFacet;
      //	x1?: BarsPathBuilderFacet;
      y0?: BarsPathBuilderFacet;
      y1?: BarsPathBuilderFacet;
      size?: BarsPathBuilderFacet;
      fill?: BarsPathBuilderFacet;
      stroke?: BarsPathBuilderFacet;
    }

    /** radii for bar end (at bar's value) and bar start (baseline, zero)  */
    export type BarsPathBuilderRadii = [endRadius: number, baseRadius: number];

    export type BarsPathBuilderRadius =
      | number
      | BarsPathBuilderRadii
      | ((self: uPlot, seriesIdx: number) => BarsPathBuilderRadii);

    export interface BarsPathBuilderOpts {
      align?: -1 | 0 | 1; // 0

      size?: [factor?: number, max?: number, min?: number];

      // corner radius factor of bar size (0 - 0.5)
      radius?: BarsPathBuilderRadius; // 0

      /** fixed-size gap between bars in CSS pixels (reduces bar width) */
      gap?: number;

      /** should return a custom [cached] layout for bars in % of plotting area (0..1) */
      disp?: BarsPathBuilderDisplay;

      /** called with bbox geometry of each drawn bar in canvas pixels. useful for spatial index, etc. */
      each?: (
        self: uPlot,
        seriesIdx: number,
        idx: number,
        left: number,
        top: number,
        width: number,
        height: number,
      ) => void;
    }

    export interface LinearPathBuilderOpts {
      alignGaps?: -1 | 0 | 1; // 0
    }

    export interface SplinePathBuilderOpts {
      alignGaps?: -1 | 0 | 1; // 0
    }

    export type PointsPathBuilderFactory = () => Points.PathBuilder;
    export type LinearPathBuilderFactory = (
      opts?: LinearPathBuilderOpts,
    ) => Series.PathBuilder;
    export type SplinePathBuilderFactory = (
      opts?: SplinePathBuilderOpts,
    ) => Series.PathBuilder;
    export type SteppedPathBuilderFactory = (
      opts?: SteppedPathBuilderOpts,
    ) => Series.PathBuilder;
    export type BarsPathBuilderFactory = (
      opts?: BarsPathBuilderOpts,
    ) => Series.PathBuilder;

    export interface PathBuilderFactories {
      linear?: LinearPathBuilderFactory;
      spline?: SplinePathBuilderFactory;
      stepped?: SteppedPathBuilderFactory;
      bars?: BarsPathBuilderFactory;
      points?: PointsPathBuilderFactory;
    }

    export type Stroke =
      | CanvasRenderingContext2D["strokeStyle"]
      | ((
          self: uPlot,
          seriesIdx: number,
        ) => CanvasRenderingContext2D["strokeStyle"]);

    export type Fill =
      | CanvasRenderingContext2D["fillStyle"]
      | ((
          self: uPlot,
          seriesIdx: number,
        ) => CanvasRenderingContext2D["fillStyle"]);

    export type Cap = CanvasRenderingContext2D["lineCap"];

    export namespace Points {
      export interface Paths {
        /** path to stroke */
        stroke?: Path2D | null;

        /** path to fill */
        fill?: Path2D | null;

        /** path for clipping fill & stroke */
        clip?: Path2D | null;

        /** bitmap of whether the clip should be applied to stroke, fill, or both */
        flags?: number;
      }

      export type Show =
        | boolean
        | ((
            self: uPlot,
            seriesIdx: number,
            idx0: number,
            idx1: number,
            gaps?: null | number[][],
          ) => boolean | undefined);

      export type Filter =
        | number[]
        | null
        | ((
            self: uPlot,
            seriesIdx: number,
            show: boolean,
            gaps?: null | number[][],
          ) => number[] | null);

      export type PathBuilder = (
        self: uPlot,
        seriesIdx: number,
        idx0: number,
        idx1: number,
        filtIdxs?: number[] | null,
      ) => Paths | null;
    }

    export interface Points {
      /** if boolean or returns boolean, round points are drawn with defined options, else fn should draw own custom points via self.ctx */
      show?: Points.Show;

      paths?: Points.PathBuilder;

      /** may return an array of points indices to draw */
      filter?: Points.Filter;

      /** diameter of point in CSS pixels */
      size?: number;

      /** minimum avg space between point centers before they're shown (default: size * 2) */
      space?: number;

      /** line width of circle outline in CSS pixels */
      width?: number;

      /** line color of circle outline (defaults to series.stroke) */
      stroke?: Stroke;

      /** line dash segment array */
      dash?: number[];

      /** line cap */
      cap?: Series.Cap;

      /** fill color of circle (defaults to #fff) */
      fill?: Fill;
    }

    export interface Facet {
      scale: string;

      auto?: boolean;

      sorted?: Sorted;
    }

    export type Gap = [from: number, to: number];

    export type Gaps = Gap[];

    export type GapsRefiner =
      | Gaps
      | ((
          self: uPlot,
          seriesIdx: number,
          idx0: number,
          idx1: number,
          nullGaps: Gaps,
        ) => Gaps);

    export type AddGap = (gaps: Gaps, from: number, to: number) => void;

    export type ClipPathBuilder = (
      gaps: Gaps,
      ori: Orientation,
      left: number,
      top: number,
      width: number,
      height: number,
    ) => Path2D | null;

    export type PathBuilder = (
      self: uPlot,
      seriesIdx: number,
      idx0: number,
      idx1: number,
    ) => Paths | null;

    export type MinMaxIdxs = [minIdx: number, maxIdx: number];

    export type Value =
      | string
      | ((
          self: uPlot,
          rawValue: number,
          seriesIdx: number,
          idx: number | null,
        ) => string | number);

    export type Values = (
      self: uPlot,
      seriesIdx: number,
      idx: number | null,
    ) => object;

    export type FillTo =
      | number
      | ((
          self: uPlot,
          seriesIdx: number,
          dataMin: number,
          dataMax: number,
        ) => number);

    export const enum Sorted {
      Unsorted = 0,
      Ascending = 1,
      Descending = -1,
    }
  }

  export interface Series {
    /** series on/off. when off, it will not affect its scale */
    show?: boolean;

    /** className to add to legend parts and cursor hover points */
    class?: string;

    /** scale key */
    scale?: string;

    /** whether this series' data is scanned during auto-ranging of its scale */
    auto?: boolean; // true

    /** if & how the data is pre-sorted (scale.auto optimization) */
    sorted?: Series.Sorted;

    /** when true, null data values will not cause line breaks */
    spanGaps?: boolean;

    /** may mutate and/or augment gaps array found from null values */
    gaps?: Series.GapsRefiner;

    /** whether path and point drawing should offset canvas to try drawing crisp lines */
    pxAlign?: number | boolean; // 1

    /** legend label */
    label?: string | HTMLElement;

    /** inline-legend value formatter. can be an fmtDate formatting string when scale.time: true */
    value?: Series.Value;

    /** table-legend multi-values formatter */
    values?: Series.Values;

    paths?: Series.PathBuilder;

    /** rendered datapoints */
    points?: Series.Points;

    /** facets */
    facets?: Series.Facet[];

    /** line width in CSS pixels */
    width?: number;

    /** line & legend color */
    stroke?: Series.Stroke;

    /** area fill & legend color */
    fill?: Series.Fill;

    /** area fill baseline (default: 0) */
    fillTo?: Series.FillTo;

    /** line dash segment array */
    dash?: number[];

    /** line cap */
    cap?: Series.Cap;

    /** alpha-transparancy */
    alpha?: number;

    /** current min and max data indices rendered */
    idxs?: Series.MinMaxIdxs;

    /** current min rendered value */
    min?: number;

    /** current max rendered value */
    max?: number;
  }

  export namespace Band {
    export type Fill =
      | CanvasRenderingContext2D["fillStyle"]
      | ((
          self: uPlot,
          bandIdx: number,
          highSeriesFill: CanvasRenderingContext2D["fillStyle"],
        ) => CanvasRenderingContext2D["fillStyle"]);

    export type Bounds = [fromSeriesIdx: number, toSeriesIdx: number];
  }

  export interface Band {
    /** band on/off */
    //	show?: boolean;

    /** series indices of upper and lower band edges */
    series: Band.Bounds;

    /** area fill style */
    fill?: Band.Fill;

    /** whether to fill towards yMin (-1) or yMax (+1) between "from" & "to" series */
    dir?: 1 | -1; // -1
  }

  export namespace Axis {
    /** must return an array of same length as splits, e.g. via splits.map() */
    export type Filter = (
      self: uPlot,
      splits: number[],
      axisIdx: number,
      foundSpace: number,
      foundIncr: number,
    ) => (number | null)[];

    export type Size =
      | number
      | ((
          self: uPlot,
          values: string[],
          axisIdx: number,
          cycleNum: number,
        ) => number);

    export type Space =
      | number
      | ((
          self: uPlot,
          axisIdx: number,
          scaleMin: number,
          scaleMax: number,
          plotDim: number,
        ) => number);

    export type Incrs =
      | number[]
      | ((
          self: uPlot,
          axisIdx: number,
          scaleMin: number,
          scaleMax: number,
          fullDim: number,
          minSpace: number,
        ) => number[]);

    export type Splits =
      | number[]
      | ((
          self: uPlot,
          axisIdx: number,
          scaleMin: number,
          scaleMax: number,
          foundIncr: number,
          foundSpace: number,
        ) => number[]);

    export type StaticValues = (string | number | null)[];

    export type DynamicValues = (
      self: uPlot,
      splits: number[],
      axisIdx: number,
      foundSpace: number,
      foundIncr: number,
    ) => StaticValues;

    export type TimeValuesConfig = (string | number | null)[][];

    export type TimeValuesTpl = string;

    export type Values =
      | StaticValues
      | DynamicValues
      | TimeValuesTpl
      | TimeValuesConfig;

    export type Label =
      | string
      | ((
          self: uPlot,
          axisIdx: number,
          foundIncr: number,
          foundSpace: number,
        ) => string);

    export type Stroke =
      | CanvasRenderingContext2D["strokeStyle"]
      | ((
          self: uPlot,
          axisIdx: number,
        ) => CanvasRenderingContext2D["strokeStyle"]);

    export const enum Side {
      Top = 0,
      Right = 1,
      Bottom = 2,
      Left = 3,
    }

    export const enum Align {
      Left = 1,
      Right = 2,
    }

    export const enum AlignTo {
      /** axis gap is on the inside; text is placed at outer gap boundary */
      Inside = 1,
      /** axis gap is on the outside; text is placed at inner gap boundary */
      Outside = 2,
    }

    export type Rotate =
      | number
      | ((
          self: uPlot,
          values: (string | number)[],
          axisIdx: number,
          foundSpace: number,
        ) => number);

    interface OrthoLines {
      /** on/off */
      show?: boolean; // true

      /** line color */
      stroke?: Stroke;

      /** line width in CSS pixels */
      width?: number;

      /** line dash segment array */
      dash?: number[];

      /** line cap */
      cap?: Series.Cap;
    }

    export interface Border extends OrthoLines {}

    interface FilterableOrthoLines extends OrthoLines {
      /** can filter which splits render lines. e.g splits.map(v => v % 2 == 0 ? v : null) */
      filter?: Filter;
    }

    export interface Grid extends FilterableOrthoLines {}

    export interface Ticks extends FilterableOrthoLines {
      /** length of tick in CSS pixels */
      size?: number;
    }
  }

  export interface Axis {
    /** axis on/off */
    show?: boolean;

    /** scale key */
    scale?: string;

    /** side of chart - 0: top, 1: rgt, 2: btm, 3: lft */
    side?: Axis.Side;

    /** height of x axis or width of y axis in CSS pixels alloted for values, gap & ticks, but excluding axis label */
    size?: Axis.Size;

    /** gap between axis values and axis baseline (or ticks, if enabled) in CSS pixels */
    gap?: number;

    /** font used for axis values */
    font?: CanvasRenderingContext2D["font"];

    /** font-size multiplier for multi-line axis values (similar to CSS line-height: 1.5em) */
    lineGap?: number; // 1.5

    /** color of axis label & values */
    stroke?: Axis.Stroke;

    /** axis label text */
    label?: Axis.Label;

    /** height of x axis label or width of y axis label in CSS pixels alloted for label text + labelGap */
    labelSize?: number;

    /** gap between label baseline and tick values in CSS pixels */
    labelGap?: number;

    /** font used for axis label */
    labelFont?: CanvasRenderingContext2D["font"];

    /** minimum grid & tick spacing in CSS pixels */
    space?: Axis.Space;

    /** available divisors for axis ticks, values, grid */
    incrs?: Axis.Incrs;

    /** determines how and where the axis must be split for placing ticks, values, grid */
    splits?: Axis.Splits;

    /** can filter which splits are passed to axis.values() for rendering. e.g splits.map(v => v % 2 == 0 ? v : null) */
    filter?: Axis.Filter;

    /** formats values for rendering */
    values?: Axis.Values;

    /** values rotation in degrees off horizontal (only bottom axes w/ side: 2) */
    rotate?: Axis.Rotate;

    /** text alignment of axis values - 1: left, 2: right */
    align?: Axis.Align;

    /** baseline for text alignment of axis values - 1: inside, 2: outside */
    alignTo?: Axis.AlignTo;

    /** gridlines to draw from this axis' splits */
    grid?: Axis.Grid;

    /** ticks to draw from this axis' splits */
    ticks?: Axis.Ticks;

    /** axis border/edge rendering */
    border?: Axis.Border;
  }

  export namespace Hooks {
    export interface Defs {
      /** fires after opts are defaulted & merged but data has not been set and scales have not been ranged */
      init?: (self: uPlot, opts: Options, data: AlignedData) => void;

      /** fires after each initial and subsequent series addition (discern via self.status == 0 or 1) */
      addSeries?: (self: uPlot, seriesIdx: number) => void;

      /** fires after each series deletion */
      delSeries?: (self: uPlot, seriesIdx: number) => void;

      /** fires after any scale has changed */
      setScale?: (self: uPlot, scaleKey: string) => void;

      /** fires after the cursor is moved */
      setCursor?: (self: uPlot) => void;

      /** fires when cursor changes idx and legend updates (or should update) */
      setLegend?: (self: uPlot) => void;

      /** fires after a selection is completed */
      setSelect?: (self: uPlot) => void;

      /** fires after a series is toggled or focused */
      setSeries?: (self: uPlot, seriesIdx: number | null, opts: Series) => void;

      /** fires after data is updated updated */
      setData?: (self: uPlot) => void;

      /** fires after the chart is resized */
      setSize?: (self: uPlot) => void;

      /** fires at start of every redraw */
      drawClear?: (self: uPlot) => void;

      /** fires after all axes are drawn */
      drawAxes?: (self: uPlot) => void;

      /** fires after each series is drawn */
      drawSeries?: (self: uPlot, seriesIdx: number) => void;

      /** fires after everything is drawn */
      draw?: (self: uPlot) => void;

      /** fires after the chart is fully initialized and in the DOM */
      ready?: (self: uPlot) => void;

      /** fires after the chart is destroyed */
      destroy?: (self: uPlot) => void;

      /** fires after .u-over's getBoundingClientRect() is called (due to scroll or resize events) */
      syncRect?: (self: uPlot, rect: DOMRect) => void;
    }

    export type Arrays = {
      [P in keyof Defs]: Defs[P][];
    };

    export type ArraysOrFuncs = {
      [P in keyof Defs]: Defs[P][] | Defs[P];
    };
  }

  export interface Plugin {
    /** can mutate provided opts as necessary */
    opts?: (self: uPlot, opts: Options) => void | Options;
    hooks: Hooks.ArraysOrFuncs;
  }
}

export as namespace uPlot;
