{"version":3,"file":"index.d.ts","sources":["../src/tooltip.ts","../src/elements/base.ts","../src/elements/BoxAndWiskers.ts","../src/data.ts","../src/elements/Violin.ts","../src/controllers/StatsBase.ts","../src/controllers/BoxPlotController.ts","../src/controllers/ViolinController.ts"],"sourcesContent":["import { InteractionItem, TooltipItem, Tooltip, TooltipModel } from 'chart.js';\n\nexport interface ExtendedTooltip extends TooltipModel<'boxplot' | 'violin'> {\n  _tooltipOutlier?: {\n    index: number;\n    datasetIndex: number;\n  };\n  _tooltipItem?: {\n    index: number;\n    datasetIndex: number;\n  };\n}\n\n/**\n * @hidden\n */\nexport function patchInHoveredOutlier(\n  this: TooltipModel<'boxplot' | 'violin'>,\n  item: TooltipItem<'boxplot' | 'violin'>\n): void {\n  const value = item.formattedValue as any;\n  const that = this as ExtendedTooltip;\n  if (value && that._tooltipOutlier != null && item.datasetIndex === that._tooltipOutlier.datasetIndex) {\n    value.hoveredOutlierIndex = that._tooltipOutlier.index;\n  }\n  if (value && that._tooltipItem != null && item.datasetIndex === that._tooltipItem.datasetIndex) {\n    value.hoveredItemIndex = that._tooltipItem.index;\n  }\n}\n\n/**\n * based on average positioner but allow access to the tooltip instance\n * @hidden\n */\nexport function outlierPositioner(\n  this: TooltipModel<'boxplot' | 'violin'>,\n  items: readonly InteractionItem[],\n  eventPosition: { x: number; y: number }\n): false | { x: number; y: number } {\n  if (!items.length) {\n    return false;\n  }\n  let x = 0;\n  let y = 0;\n  let count = 0;\n  for (let i = 0; i < items.length; i += 1) {\n    const el = items[i].element;\n    if (el && el.hasValue()) {\n      const pos = (el as any).tooltipPosition(eventPosition, this);\n      x += pos.x;\n      y += pos.y;\n      count += 1;\n    }\n  }\n  return {\n    x: x / count,\n    y: y / count,\n  };\n}\n\noutlierPositioner.id = 'average';\noutlierPositioner.register = () => {\n  Tooltip.positioners.average = outlierPositioner as any;\n  return outlierPositioner;\n};\n","import { Element } from 'chart.js';\nimport { drawPoint } from 'chart.js/helpers';\nimport { rnd } from '../data';\nimport type { ExtendedTooltip } from '../tooltip';\n\nexport interface IStatsBaseOptions {\n  /**\n   * @default see rectangle\n   * scriptable\n   * indexable\n   */\n  backgroundColor: string;\n\n  /**\n   * @default see rectangle\n   * scriptable\n   * indexable\n   */\n  borderColor: string;\n\n  /**\n   * @default 1\n   * scriptable\n   * indexable\n   */\n  borderWidth: number;\n\n  /**\n   * item style used to render outliers\n   * @default circle\n   */\n  outlierStyle:\n    | 'circle'\n    | 'triangle'\n    | 'rect'\n    | 'rectRounded'\n    | 'rectRot'\n    | 'cross'\n    | 'crossRot'\n    | 'star'\n    | 'line'\n    | 'dash';\n\n  /**\n   * radius used to render outliers\n   * @default 2\n   * scriptable\n   * indexable\n   */\n  outlierRadius: number;\n\n  /**\n   * @default see rectangle.backgroundColor\n   * scriptable\n   * indexable\n   */\n  outlierBackgroundColor: string;\n\n  /**\n   * @default see rectangle.borderColor\n   * scriptable\n   * indexable\n   */\n  outlierBorderColor: string;\n  /**\n   * @default 1\n   * scriptable\n   * indexable\n   */\n  outlierBorderWidth: number;\n\n  /**\n   * item style used to render items\n   * @default circle\n   */\n  itemStyle:\n    | 'circle'\n    | 'triangle'\n    | 'rect'\n    | 'rectRounded'\n    | 'rectRot'\n    | 'cross'\n    | 'crossRot'\n    | 'star'\n    | 'line'\n    | 'dash';\n\n  /**\n   * radius used to render items\n   * @default 0 so disabled\n   * scriptable\n   * indexable\n   */\n  itemRadius: number;\n\n  /**\n   * background color for items\n   * @default see rectangle.backgroundColor\n   * scriptable\n   * indexable\n   */\n  itemBackgroundColor: string;\n\n  /**\n   * border color for items\n   * @default see rectangle.borderColor\n   * scriptable\n   * indexable\n   */\n  itemBorderColor: string;\n\n  /**\n   * border width for items\n   * @default 0\n   * scriptable\n   * indexable\n   */\n  itemBorderWidth: number;\n  /**\n   * hit radius for hit test of items\n   * @default 0\n   * scriptable\n   * indexable\n   */\n  itemHitRadius: number;\n\n  /**\n   * padding that is added around the bounding box when computing a mouse hit\n   * @default 2\n   * scriptable\n   * indexable\n   */\n  hitPadding: number;\n\n  /**\n   * hit radius for hit test of outliers\n   * @default 4\n   * scriptable\n   * indexable\n   */\n  outlierHitRadius: number;\n\n  /**\n   * item style used to render mean dot\n   * @default circle\n   */\n  meanStyle:\n    | 'circle'\n    | 'triangle'\n    | 'rect'\n    | 'rectRounded'\n    | 'rectRot'\n    | 'cross'\n    | 'crossRot'\n    | 'star'\n    | 'line'\n    | 'dash';\n\n  /**\n   * radius used to mean dots\n   * @default 3\n   * scriptable\n   * indexable\n   */\n  meanRadius: number;\n\n  /**\n   * background color for mean dot\n   * @default see rectangle.backgroundColor\n   * scriptable\n   * indexable\n   */\n  meanBackgroundColor: string;\n\n  /**\n   * border color for mean dot\n   * @default see rectangle.borderColor\n   * scriptable\n   * indexable\n   */\n  meanBorderColor: string;\n\n  /**\n   * border width for mean dot\n   * @default 0\n   * scriptable\n   * indexable\n   */\n  meanBorderWidth: number;\n}\n\n/**\n * @hidden\n */\nexport const baseDefaults = {\n  borderWidth: 1,\n\n  outlierStyle: 'circle',\n  outlierRadius: 2,\n  outlierBorderWidth: 1,\n\n  itemStyle: 'circle',\n  itemRadius: 0,\n  itemBorderWidth: 0,\n  itemHitRadius: 0,\n\n  meanStyle: 'circle',\n  meanRadius: 3,\n  meanBorderWidth: 1,\n\n  hitPadding: 2,\n  outlierHitRadius: 4,\n};\n\n/**\n * @hidden\n */\nexport const baseRoutes = {\n  outlierBackgroundColor: 'backgroundColor',\n  outlierBorderColor: 'borderColor',\n  itemBackgroundColor: 'backgroundColor',\n  itemBorderColor: 'borderColor',\n  meanBackgroundColor: 'backgroundColor',\n  meanBorderColor: 'borderColor',\n};\n\n/**\n * @hidden\n */\nexport const baseOptionKeys = /* #__PURE__ */ (() => Object.keys(baseDefaults).concat(Object.keys(baseRoutes)))();\n\nexport interface IStatsBaseProps {\n  x: number;\n  y: number;\n  width: number;\n  height: number;\n  items: number[];\n  outliers: number[];\n}\n\nexport class StatsBase<T extends IStatsBaseProps & { mean?: number }, O extends IStatsBaseOptions> extends Element<\n  T,\n  O\n> {\n  /**\n   * @hidden\n   */\n  declare _datasetIndex: number;\n\n  /**\n   * @hidden\n   */\n  declare horizontal: boolean;\n\n  /**\n   * @hidden\n   */\n  declare _index: number;\n\n  /**\n   * @hidden\n   */\n  isVertical(): boolean {\n    return !this.horizontal;\n  }\n\n  /**\n   * @hidden\n   */\n  protected _drawItems(ctx: CanvasRenderingContext2D): void {\n    const vert = this.isVertical();\n    const props = this.getProps(['x', 'y', 'items', 'width', 'height', 'outliers']);\n    const { options } = this;\n\n    if (options.itemRadius <= 0 || !props.items || props.items.length <= 0) {\n      return;\n    }\n    ctx.save();\n    ctx.strokeStyle = options.itemBorderColor;\n    ctx.fillStyle = options.itemBackgroundColor;\n    ctx.lineWidth = options.itemBorderWidth;\n    // jitter based on random data\n    // use the dataset index and index to initialize the random number generator\n    const random = rnd(this._datasetIndex * 1000 + this._index);\n\n    const pointOptions = {\n      pointStyle: options.itemStyle,\n      radius: options.itemRadius,\n      borderWidth: options.itemBorderWidth,\n    };\n    const outliers = new Set(props.outliers || []);\n\n    if (vert) {\n      props.items.forEach((v) => {\n        if (!outliers.has(v)) {\n          drawPoint(ctx, pointOptions, props.x - props.width / 2 + random() * props.width, v);\n        }\n      });\n    } else {\n      props.items.forEach((v) => {\n        if (!outliers.has(v)) {\n          drawPoint(ctx, pointOptions, v, props.y - props.height / 2 + random() * props.height);\n        }\n      });\n    }\n    ctx.restore();\n  }\n\n  /**\n   * @hidden\n   */\n  protected _drawOutliers(ctx: CanvasRenderingContext2D): void {\n    const vert = this.isVertical();\n    const props = this.getProps(['x', 'y', 'outliers']);\n    const { options } = this;\n    if (options.outlierRadius <= 0 || !props.outliers || props.outliers.length === 0) {\n      return;\n    }\n    ctx.save();\n    ctx.fillStyle = options.outlierBackgroundColor;\n    ctx.strokeStyle = options.outlierBorderColor;\n    ctx.lineWidth = options.outlierBorderWidth;\n\n    const pointOptions = {\n      pointStyle: options.outlierStyle,\n      radius: options.outlierRadius,\n      borderWidth: options.outlierBorderWidth,\n    };\n\n    if (vert) {\n      props.outliers.forEach((v) => {\n        drawPoint(ctx, pointOptions, props.x, v);\n      });\n    } else {\n      props.outliers.forEach((v) => {\n        drawPoint(ctx, pointOptions, v, props.y);\n      });\n    }\n\n    ctx.restore();\n  }\n\n  /**\n   * @hidden\n   */\n  protected _drawMeanDot(ctx: CanvasRenderingContext2D): void {\n    const vert = this.isVertical();\n    const props = this.getProps(['x', 'y', 'mean']);\n    const { options } = this;\n    if (options.meanRadius <= 0 || props.mean == null || Number.isNaN(props.mean)) {\n      return;\n    }\n    ctx.save();\n    ctx.fillStyle = options.meanBackgroundColor;\n    ctx.strokeStyle = options.meanBorderColor;\n    ctx.lineWidth = options.meanBorderWidth;\n\n    const pointOptions = {\n      pointStyle: options.meanStyle,\n      radius: options.meanRadius,\n      borderWidth: options.meanBorderWidth,\n    };\n\n    if (vert) {\n      drawPoint(ctx, pointOptions, props.x, props.mean);\n    } else {\n      drawPoint(ctx, pointOptions, props.mean, props.y);\n    }\n\n    ctx.restore();\n  }\n\n  /**\n   * @hidden\n   */\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  _getBounds(_useFinalPosition?: boolean): { left: number; top: number; right: number; bottom: number } {\n    // abstract\n    return {\n      left: 0,\n      top: 0,\n      right: 0,\n      bottom: 0,\n    };\n  }\n\n  /**\n   * @hidden\n   */\n  _getHitBounds(useFinalPosition?: boolean): { left: number; top: number; right: number; bottom: number } {\n    const padding = this.options.hitPadding;\n    const b = this._getBounds(useFinalPosition);\n    return {\n      left: b.left - padding,\n      top: b.top - padding,\n      right: b.right + padding,\n      bottom: b.bottom + padding,\n    };\n  }\n\n  /**\n   * @hidden\n   */\n  inRange(mouseX: number, mouseY: number, useFinalPosition?: boolean): boolean {\n    if (Number.isNaN(this.x) && Number.isNaN(this.y)) {\n      return false;\n    }\n    return (\n      this._boxInRange(mouseX, mouseY, useFinalPosition) ||\n      this._outlierIndexInRange(mouseX, mouseY, useFinalPosition) != null ||\n      this._itemIndexInRange(mouseX, mouseY, useFinalPosition) != null\n    );\n  }\n\n  /**\n   * @hidden\n   */\n  inXRange(mouseX: number, useFinalPosition?: boolean): boolean {\n    const bounds = this._getHitBounds(useFinalPosition);\n    return mouseX >= bounds.left && mouseX <= bounds.right;\n  }\n\n  /**\n   * @hidden\n   */\n  inYRange(mouseY: number, useFinalPosition?: boolean): boolean {\n    const bounds = this._getHitBounds(useFinalPosition);\n    return mouseY >= bounds.top && mouseY <= bounds.bottom;\n  }\n\n  /**\n   * @hidden\n   */\n  protected _outlierIndexInRange(\n    mouseX: number,\n    mouseY: number,\n    useFinalPosition?: boolean\n  ): { index: number; x: number; y: number } | null {\n    const props = this.getProps(['x', 'y'], useFinalPosition);\n    const hitRadius = this.options.outlierHitRadius;\n    const outliers = this._getOutliers(useFinalPosition);\n    const vertical = this.isVertical();\n\n    // check if along the outlier line\n    if ((vertical && Math.abs(mouseX - props.x) > hitRadius) || (!vertical && Math.abs(mouseY - props.y) > hitRadius)) {\n      return null;\n    }\n    const toCompare = vertical ? mouseY : mouseX;\n    for (let i = 0; i < outliers.length; i += 1) {\n      if (Math.abs(outliers[i] - toCompare) <= hitRadius) {\n        return vertical ? { index: i, x: props.x, y: outliers[i] } : { index: i, x: outliers[i], y: props.y };\n      }\n    }\n    return null;\n  }\n\n  /**\n   * @hidden\n   */\n  protected _itemIndexInRange(\n    mouseX: number,\n    mouseY: number,\n    useFinalPosition?: boolean\n  ): { index: number; x: number; y: number } | null {\n    const hitRadius = this.options.itemHitRadius;\n    if (hitRadius <= 0) {\n      return null;\n    }\n    const props = this.getProps(['x', 'y', 'items', 'width', 'height', 'outliers'], useFinalPosition);\n    const vert = this.isVertical();\n    const { options } = this;\n\n    if (options.itemRadius <= 0 || !props.items || props.items.length <= 0) {\n      return null;\n    }\n    // jitter based on random data\n    // use the dataset index and index to initialize the random number generator\n    const random = rnd(this._datasetIndex * 1000 + this._index);\n    const outliers = new Set(props.outliers || []);\n\n    if (vert) {\n      for (let i = 0; i < props.items.length; i++) {\n        const y = props.items[i];\n        if (!outliers.has(y)) {\n          const x = props.x - props.width / 2 + random() * props.width;\n          if (Math.abs(x - mouseX) <= hitRadius && Math.abs(y - mouseY) <= hitRadius) {\n            return { index: i, x, y };\n          }\n        }\n      }\n    } else {\n      for (let i = 0; i < props.items.length; i++) {\n        const x = props.items[i];\n        if (!outliers.has(x)) {\n          const y = props.y - props.height / 2 + random() * props.height;\n          if (Math.abs(x - mouseX) <= hitRadius && Math.abs(y - mouseY) <= hitRadius) {\n            return { index: i, x, y };\n          }\n        }\n      }\n    }\n    return null;\n  }\n\n  /**\n   * @hidden\n   */\n  protected _boxInRange(mouseX: number, mouseY: number, useFinalPosition?: boolean): boolean {\n    const bounds = this._getHitBounds(useFinalPosition);\n    return mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom;\n  }\n\n  /**\n   * @hidden\n   */\n  getCenterPoint(useFinalPosition?: boolean): { x: number; y: number } {\n    const props = this.getProps(['x', 'y'], useFinalPosition);\n    return {\n      x: props.x,\n      y: props.y,\n    };\n  }\n\n  /**\n   * @hidden\n   */\n  protected _getOutliers(useFinalPosition?: boolean): number[] {\n    const props = this.getProps(['outliers'], useFinalPosition);\n    return props.outliers || [];\n  }\n\n  /**\n   * @hidden\n   */\n  tooltipPosition(\n    eventPosition?: { x: number; y: number } | boolean,\n    tooltip?: ExtendedTooltip\n  ): { x: number; y: number } {\n    if (!eventPosition || typeof eventPosition === 'boolean') {\n      // fallback\n      return this.getCenterPoint();\n    }\n    if (tooltip) {\n      delete tooltip._tooltipOutlier;\n\n      delete tooltip._tooltipItem;\n    }\n\n    //outlier\n    const info = this._outlierIndexInRange(eventPosition.x, eventPosition.y);\n    if (info != null && tooltip) {\n      // hack in the data of the hovered outlier\n\n      tooltip._tooltipOutlier = {\n        index: info.index,\n        datasetIndex: this._datasetIndex,\n      };\n      return {\n        x: info.x,\n        y: info.y,\n      };\n    }\n    // items\n    const itemInfo = this._itemIndexInRange(eventPosition.x, eventPosition.y);\n    if (itemInfo != null && tooltip) {\n      // hack in the data of the hovered outlier\n\n      tooltip._tooltipItem = {\n        index: itemInfo.index,\n        datasetIndex: this._datasetIndex,\n      };\n      return {\n        x: itemInfo.x,\n        y: itemInfo.y,\n      };\n    }\n\n    // fallback\n    return this.getCenterPoint();\n  }\n}\n","import { BarElement, ChartType, CommonHoverOptions, ScriptableAndArrayOptions, ScriptableContext } from 'chart.js';\nimport {\n  StatsBase,\n  baseDefaults,\n  baseOptionKeys,\n  baseRoutes,\n  type IStatsBaseOptions,\n  type IStatsBaseProps,\n} from './base';\n/**\n * @hidden\n */\nexport const boxOptionsKeys = baseOptionKeys.concat(['medianColor', 'lowerBackgroundColor']);\n\nexport interface IBoxAndWhiskersOptions extends IStatsBaseOptions {\n  /**\n   * separate color for the median line\n   * @default 'transparent' takes the current borderColor\n   * scriptable\n   * indexable\n   */\n  medianColor: string;\n\n  /**\n   * color the lower half (median-q3) of the box in a different color\n   * @default 'transparent' takes the current borderColor\n   * scriptable\n   * indexable\n   */\n  lowerBackgroundColor: string;\n}\n\nexport interface IBoxAndWhiskerProps extends IStatsBaseProps {\n  q1: number;\n  q3: number;\n  median: number;\n  whiskerMin: number;\n  whiskerMax: number;\n  mean: number;\n}\n\nexport class BoxAndWiskers extends StatsBase<IBoxAndWhiskerProps, IBoxAndWhiskersOptions> {\n  /**\n   * @hidden\n   */\n  draw(ctx: CanvasRenderingContext2D): void {\n    ctx.save();\n\n    ctx.fillStyle = this.options.backgroundColor;\n    ctx.strokeStyle = this.options.borderColor;\n    ctx.lineWidth = this.options.borderWidth;\n\n    this._drawBoxPlot(ctx);\n    this._drawOutliers(ctx);\n    this._drawMeanDot(ctx);\n\n    ctx.restore();\n\n    this._drawItems(ctx);\n  }\n\n  /**\n   * @hidden\n   */\n  protected _drawBoxPlot(ctx: CanvasRenderingContext2D): void {\n    if (this.isVertical()) {\n      this._drawBoxPlotVertical(ctx);\n    } else {\n      this._drawBoxPlotHorizontal(ctx);\n    }\n  }\n\n  /**\n   * @hidden\n   */\n  protected _drawBoxPlotVertical(ctx: CanvasRenderingContext2D): void {\n    const { options } = this;\n    const props = this.getProps(['x', 'width', 'q1', 'q3', 'median', 'whiskerMin', 'whiskerMax']);\n\n    const { x } = props;\n    const { width } = props;\n    const x0 = x - width / 2;\n    // Draw the q1>q3 box\n    if (props.q3 > props.q1) {\n      ctx.fillRect(x0, props.q1, width, props.q3 - props.q1);\n    } else {\n      ctx.fillRect(x0, props.q3, width, props.q1 - props.q3);\n    }\n\n    // Draw the median line\n    ctx.save();\n    if (options.medianColor && options.medianColor !== 'transparent' && options.medianColor !== '#0000') {\n      ctx.strokeStyle = options.medianColor;\n    }\n    ctx.beginPath();\n    ctx.moveTo(x0, props.median);\n    ctx.lineTo(x0 + width, props.median);\n    ctx.closePath();\n    ctx.stroke();\n    ctx.restore();\n\n    ctx.save();\n    // fill the part below the median with lowerColor\n    if (\n      options.lowerBackgroundColor &&\n      options.lowerBackgroundColor !== 'transparent' &&\n      options.lowerBackgroundColor !== '#0000'\n    ) {\n      ctx.fillStyle = options.lowerBackgroundColor;\n      if (props.q3 > props.q1) {\n        ctx.fillRect(x0, props.median, width, props.q3 - props.median);\n      } else {\n        ctx.fillRect(x0, props.median, width, props.q1 - props.median);\n      }\n    }\n    ctx.restore();\n\n    // Draw the border around the main q1>q3 box\n    if (props.q3 > props.q1) {\n      ctx.strokeRect(x0, props.q1, width, props.q3 - props.q1);\n    } else {\n      ctx.strokeRect(x0, props.q3, width, props.q1 - props.q3);\n    }\n\n    // Draw the whiskers\n    ctx.beginPath();\n    ctx.moveTo(x0, props.whiskerMin);\n    ctx.lineTo(x0 + width, props.whiskerMin);\n    ctx.moveTo(x, props.whiskerMin);\n    ctx.lineTo(x, props.q1);\n    ctx.moveTo(x0, props.whiskerMax);\n    ctx.lineTo(x0 + width, props.whiskerMax);\n    ctx.moveTo(x, props.whiskerMax);\n    ctx.lineTo(x, props.q3);\n    ctx.closePath();\n    ctx.stroke();\n  }\n\n  /**\n   * @hidden\n   */\n  protected _drawBoxPlotHorizontal(ctx: CanvasRenderingContext2D): void {\n    const { options } = this;\n    const props = this.getProps(['y', 'height', 'q1', 'q3', 'median', 'whiskerMin', 'whiskerMax']);\n\n    const { y } = props;\n    const { height } = props;\n    const y0 = y - height / 2;\n\n    // Draw the q1>q3 box\n    if (props.q3 > props.q1) {\n      ctx.fillRect(props.q1, y0, props.q3 - props.q1, height);\n    } else {\n      ctx.fillRect(props.q3, y0, props.q1 - props.q3, height);\n    }\n\n    // Draw the median line\n    ctx.save();\n    if (options.medianColor && options.medianColor !== 'transparent') {\n      ctx.strokeStyle = options.medianColor;\n    }\n    ctx.beginPath();\n    ctx.moveTo(props.median, y0);\n    ctx.lineTo(props.median, y0 + height);\n    ctx.closePath();\n    ctx.stroke();\n    ctx.restore();\n\n    ctx.save();\n    // fill the part below the median with lowerColor\n    if (options.lowerBackgroundColor && options.lowerBackgroundColor !== 'transparent') {\n      ctx.fillStyle = options.lowerBackgroundColor;\n      if (props.q3 > props.q1) {\n        ctx.fillRect(props.median, y0, props.q3 - props.median, height);\n      } else {\n        ctx.fillRect(props.median, y0, props.q1 - props.median, height);\n      }\n    }\n    ctx.restore();\n\n    // Draw the border around the main q1>q3 box\n    if (props.q3 > props.q1) {\n      ctx.strokeRect(props.q1, y0, props.q3 - props.q1, height);\n    } else {\n      ctx.strokeRect(props.q3, y0, props.q1 - props.q3, height);\n    }\n\n    // Draw the whiskers\n    ctx.beginPath();\n    ctx.moveTo(props.whiskerMin, y0);\n    ctx.lineTo(props.whiskerMin, y0 + height);\n    ctx.moveTo(props.whiskerMin, y);\n    ctx.lineTo(props.q1, y);\n    ctx.moveTo(props.whiskerMax, y0);\n    ctx.lineTo(props.whiskerMax, y0 + height);\n    ctx.moveTo(props.whiskerMax, y);\n    ctx.lineTo(props.q3, y);\n    ctx.closePath();\n    ctx.stroke();\n  }\n\n  /**\n   * @hidden\n   */\n  _getBounds(useFinalPosition?: boolean): { left: number; top: number; right: number; bottom: number } {\n    const vert = this.isVertical();\n    if (this.x == null) {\n      return {\n        left: 0,\n        top: 0,\n        right: 0,\n        bottom: 0,\n      };\n    }\n\n    if (vert) {\n      const { x, width, whiskerMax, whiskerMin } = this.getProps(\n        ['x', 'width', 'whiskerMin', 'whiskerMax'],\n        useFinalPosition\n      );\n      const x0 = x - width / 2;\n      return {\n        left: x0,\n        top: whiskerMax,\n        right: x0 + width,\n        bottom: whiskerMin,\n      };\n    }\n    const { y, height, whiskerMax, whiskerMin } = this.getProps(\n      ['y', 'height', 'whiskerMin', 'whiskerMax'],\n      useFinalPosition\n    );\n    const y0 = y - height / 2;\n    return {\n      left: whiskerMin,\n      top: y0,\n      right: whiskerMax,\n      bottom: y0 + height,\n    };\n  }\n\n  static id = 'boxandwhiskers';\n\n  /**\n   * @hidden\n   */\n  static defaults = /* #__PURE__ */ {\n    ...BarElement.defaults,\n    ...baseDefaults,\n    medianColor: 'transparent',\n    lowerBackgroundColor: 'transparent',\n  };\n\n  /**\n   * @hidden\n   */\n  static defaultRoutes = /* #__PURE__ */ { ...BarElement.defaultRoutes, ...baseRoutes };\n}\n\ndeclare module 'chart.js' {\n  export interface ElementOptionsByType<TType extends ChartType> {\n    boxandwhiskers: ScriptableAndArrayOptions<IBoxAndWhiskersOptions & CommonHoverOptions, ScriptableContext<TType>>;\n  }\n}\n","import {\n  boxplot as boxplots,\n  quantilesFivenum,\n  quantilesHigher,\n  quantilesHinges,\n  quantilesLinear,\n  quantilesLower,\n  quantilesMidpoint,\n  quantilesNearest,\n  quantilesType7,\n} from '@sgratzl/boxplots';\n\nexport {\n  quantilesFivenum,\n  quantilesHigher,\n  quantilesHinges,\n  quantilesLinear,\n  quantilesLower,\n  quantilesMidpoint,\n  quantilesNearest,\n  quantilesType7,\n} from '@sgratzl/boxplots';\n\nexport interface IBaseStats {\n  min: number;\n  max: number;\n  q1: number;\n  q3: number;\n  median: number;\n  mean: number;\n  items: readonly number[];\n  outliers: readonly number[];\n}\n\nexport interface IBoxPlot extends IBaseStats {\n  whiskerMax: number;\n  whiskerMin: number;\n}\n\nexport interface IKDEPoint {\n  v: number;\n  estimate: number;\n}\n\nexport interface IViolin extends IBaseStats {\n  maxEstimate: number;\n  coords: IKDEPoint[];\n}\n\n/**\n * compute the whiskers\n * @param boxplot\n * @param {number[]} arr sorted array\n * @param {number} coef\n */\nexport function whiskers(\n  boxplot: IBoxPlot,\n  arr: number[] | null,\n  coef = 1.5\n): { whiskerMin: number; whiskerMax: number } {\n  const iqr = boxplot.q3 - boxplot.q1;\n  // since top left is max\n  const coefValid = typeof coef === 'number' && coef > 0;\n  let whiskerMin = coefValid ? Math.max(boxplot.min, boxplot.q1 - coef * iqr) : boxplot.min;\n  let whiskerMax = coefValid ? Math.min(boxplot.max, boxplot.q3 + coef * iqr) : boxplot.max;\n\n  if (Array.isArray(arr)) {\n    // compute the closest real element\n    for (let i = 0; i < arr.length; i += 1) {\n      const v = arr[i];\n      if (v >= whiskerMin) {\n        whiskerMin = v;\n        break;\n      }\n    }\n    for (let i = arr.length - 1; i >= 0; i -= 1) {\n      const v = arr[i];\n      if (v <= whiskerMax) {\n        whiskerMax = v;\n        break;\n      }\n    }\n  }\n\n  return {\n    whiskerMin,\n    whiskerMax,\n  };\n}\n\nexport type QuantileMethod =\n  | 7\n  | 'quantiles'\n  | 'hinges'\n  | 'fivenum'\n  | 'linear'\n  | 'lower'\n  | 'higher'\n  | 'nearest'\n  | 'midpoint'\n  | ((arr: ArrayLike<number>, length?: number | undefined) => { q1: number; median: number; q3: number });\n\nexport interface IBaseOptions {\n  /**\n   * statistic measure that should be used for computing the minimal data limit\n   * @default 'min'\n   */\n  minStats?: 'min' | 'q1' | 'whiskerMin';\n\n  /**\n   * statistic measure that should be used for computing the maximal data limit\n   * @default 'max'\n   */\n  maxStats?: 'max' | 'q3' | 'whiskerMax';\n\n  /**\n   * from the R doc: this determines how far the plot ‘whiskers’ extend out from\n   * the box. If coef is positive, the whiskers extend to the most extreme data\n   * point which is no more than coef times the length of the box away from the\n   * box. A value of zero causes the whiskers to extend to the data extremes\n   * @default 1.5\n   */\n  coef?: number;\n\n  /**\n   * the method to compute the quantiles.\n   *\n   * 7, 'quantiles': the type-7 method as used by R 'quantiles' method.\n   * 'hinges' and 'fivenum': the method used by R 'boxplot.stats' method.\n   * 'linear': the interpolation method 'linear' as used by 'numpy.percentile' function\n   * 'lower': the interpolation method 'lower' as used by 'numpy.percentile' function\n   * 'higher': the interpolation method 'higher' as used by 'numpy.percentile' function\n   * 'nearest': the interpolation method 'nearest' as used by 'numpy.percentile' function\n   * 'midpoint': the interpolation method 'midpoint' as used by 'numpy.percentile' function\n   * @default 7\n   */\n  quantiles?: QuantileMethod;\n\n  /**\n   * the method to compute the whiskers.\n   *\n   * 'nearest': with this mode computed whisker values will be replaced with nearest real data points\n   * 'exact': with this mode exact computed whisker values will be displayed on chart\n   * @default 'nearest'\n   */\n  whiskersMode?: 'nearest' | 'exact';\n}\n\nexport type IBoxplotOptions = IBaseOptions;\n\nexport interface IViolinOptions extends IBaseOptions {\n  /**\n   * number of points that should be samples of the KDE\n   * @default 100\n   */\n  points: number;\n}\n\n/**\n * @hidden\n */\nexport const defaultStatsOptions: Required<Omit<IBaseOptions, 'minStats' | 'maxStats'>> = {\n  coef: 1.5,\n  quantiles: 7,\n  whiskersMode: 'nearest',\n};\n\nfunction determineQuantiles(q: QuantileMethod) {\n  if (typeof q === 'function') {\n    return q;\n  }\n  const lookup = {\n    hinges: quantilesHinges,\n    fivenum: quantilesFivenum,\n    7: quantilesType7,\n    quantiles: quantilesType7,\n    linear: quantilesLinear,\n    lower: quantilesLower,\n    higher: quantilesHigher,\n    nearest: quantilesNearest,\n    midpoint: quantilesMidpoint,\n  };\n  return lookup[q] || quantilesType7;\n}\n\nfunction determineStatsOptions(options?: IBaseOptions) {\n  const coef = options == null || typeof options.coef !== 'number' ? defaultStatsOptions.coef : options.coef;\n  const q = options == null || options.quantiles == null ? quantilesType7 : options.quantiles;\n  const quantiles = determineQuantiles(q);\n  const whiskersMode =\n    options == null || typeof options.whiskersMode !== 'string'\n      ? defaultStatsOptions.whiskersMode\n      : options.whiskersMode;\n  return {\n    coef,\n    quantiles,\n    whiskersMode,\n  };\n}\n\n/**\n * @hidden\n */\nexport function boxplotStats(arr: readonly number[] | Float32Array | Float64Array, options: IBaseOptions): IBoxPlot {\n  const vs =\n    typeof Float64Array !== 'undefined' && !(arr instanceof Float32Array || arr instanceof Float64Array)\n      ? Float64Array.from(arr)\n      : arr;\n  const r = boxplots(vs, determineStatsOptions(options));\n  return {\n    items: Array.from(r.items),\n    outliers: r.outlier,\n    whiskerMax: r.whiskerHigh,\n    whiskerMin: r.whiskerLow,\n    max: r.max,\n    median: r.median,\n    mean: r.mean,\n    min: r.min,\n    q1: r.q1,\n    q3: r.q3,\n  };\n}\n\nfunction computeSamples(min: number, max: number, points: number) {\n  // generate coordinates\n  const range = max - min;\n  const samples: number[] = [];\n  const inc = range / points;\n  for (let v = min; v <= max && inc > 0; v += inc) {\n    samples.push(v);\n  }\n  if (samples[samples.length - 1] !== max) {\n    samples.push(max);\n  }\n  return samples;\n}\n\n/**\n * @hidden\n */\nexport function violinStats(arr: readonly number[], options: IViolinOptions): IViolin | undefined {\n  // console.assert(Array.isArray(arr));\n  if (arr.length === 0) {\n    return undefined;\n  }\n  const vs =\n    typeof Float64Array !== 'undefined' && !(arr instanceof Float32Array || arr instanceof Float64Array)\n      ? Float64Array.from(arr)\n      : arr;\n  const stats = boxplots(vs, determineStatsOptions(options));\n\n  // generate coordinates\n  const samples = computeSamples(stats.min, stats.max, options.points);\n  const coords = samples.map((v) => ({ v, estimate: stats.kde(v) }));\n  const maxEstimate = coords.reduce((a, d) => Math.max(a, d.estimate), Number.NEGATIVE_INFINITY);\n\n  return {\n    max: stats.max,\n    min: stats.min,\n    mean: stats.mean,\n    median: stats.median,\n    q1: stats.q1,\n    q3: stats.q3,\n    items: Array.from(stats.items),\n    coords,\n    outliers: [], // items.filter((d) => d < stats.q1 || d > stats.q3),\n    maxEstimate,\n  };\n}\n\n/**\n * @hidden\n */\nexport function asBoxPlotStats(value: any, options: IBoxplotOptions): IBoxPlot | undefined {\n  if (!value) {\n    return undefined;\n  }\n  if (typeof value.median === 'number' && typeof value.q1 === 'number' && typeof value.q3 === 'number') {\n    // sounds good, check for helper\n    if (typeof value.whiskerMin === 'undefined') {\n      const { coef } = determineStatsOptions(options);\n      const { whiskerMin, whiskerMax } = whiskers(\n        value,\n        Array.isArray(value.items) ? (value.items as number[]).slice().sort((a, b) => a - b) : null,\n        coef\n      );\n      value.whiskerMin = whiskerMin;\n      value.whiskerMax = whiskerMax;\n    }\n    return value;\n  }\n  if (!Array.isArray(value)) {\n    return undefined;\n  }\n  return boxplotStats(value, options);\n}\n\n/**\n * @hidden\n */\n\nexport function asViolinStats(value: any, options: IViolinOptions): IViolin | undefined {\n  if (!value) {\n    return undefined;\n  }\n  if (typeof value.median === 'number' && Array.isArray(value.coords)) {\n    return value;\n  }\n  if (!Array.isArray(value)) {\n    return undefined;\n  }\n  return violinStats(value, options);\n}\n\n/**\n * @hidden\n */\nexport function rnd(seed = Date.now()): () => number {\n  // Adapted from http://indiegamr.com/generate-repeatable-random-numbers-in-js/\n  let s = seed;\n  return () => {\n    s = (s * 9301 + 49297) % 233280;\n    return s / 233280;\n  };\n}\n","import {\n  BarElement,\n  type ChartType,\n  type CommonHoverOptions,\n  type ScriptableAndArrayOptions,\n  type ScriptableContext,\n} from 'chart.js';\nimport { drawPoint } from 'chart.js/helpers';\nimport type { IKDEPoint } from '../data';\nimport { StatsBase, baseDefaults, baseRoutes, type IStatsBaseOptions, type IStatsBaseProps } from './base';\n\nexport type IViolinElementOptions = IStatsBaseOptions;\n\nexport interface IViolinElementProps extends IStatsBaseProps {\n  min: number;\n  max: number;\n  median: number;\n  coords: IKDEPoint[];\n  maxEstimate?: number;\n}\n\nexport class Violin extends StatsBase<IViolinElementProps, IViolinElementOptions> {\n  /**\n   * @hidden\n   */\n  draw(ctx: CanvasRenderingContext2D): void {\n    ctx.save();\n\n    ctx.fillStyle = this.options.backgroundColor;\n    ctx.strokeStyle = this.options.borderColor;\n    ctx.lineWidth = this.options.borderWidth;\n\n    const props = this.getProps(['x', 'y', 'median', 'width', 'height', 'min', 'max', 'coords', 'maxEstimate']);\n\n    if (props.median != null) {\n      // draw median dot\n      drawPoint(\n        ctx,\n        {\n          pointStyle: 'rectRot',\n          radius: 5,\n          borderWidth: this.options.borderWidth,\n        },\n        props.x,\n        props.y\n      );\n    }\n\n    if (props.coords && props.coords.length > 0) {\n      this._drawCoords(ctx, props);\n    }\n    this._drawOutliers(ctx);\n    this._drawMeanDot(ctx);\n\n    ctx.restore();\n\n    this._drawItems(ctx);\n  }\n\n  /**\n   * @hidden\n   */\n  protected _drawCoords(\n    ctx: CanvasRenderingContext2D,\n    props: Pick<IViolinElementProps, 'x' | 'coords' | 'y' | 'maxEstimate' | 'width' | 'height' | 'min' | 'max'>\n  ): void {\n    let maxEstimate: number;\n    if (props.maxEstimate == null) {\n      maxEstimate = props.coords.reduce((a, d) => Math.max(a, d.estimate), Number.NEGATIVE_INFINITY);\n    } else {\n      maxEstimate = props.maxEstimate;\n    }\n\n    ctx.beginPath();\n    if (this.isVertical()) {\n      const { x, width } = props;\n      const factor = width / 2 / maxEstimate;\n\n      props.coords.forEach((c) => {\n        ctx.lineTo(x - c.estimate * factor, c.v);\n      });\n\n      props.coords\n        .slice()\n        .reverse()\n        .forEach((c) => {\n          ctx.lineTo(x + c.estimate * factor, c.v);\n        });\n    } else {\n      const { y, height } = props;\n      const factor = height / 2 / maxEstimate;\n\n      props.coords.forEach((c) => {\n        ctx.lineTo(c.v, y - c.estimate * factor);\n      });\n\n      props.coords\n        .slice()\n        .reverse()\n        .forEach((c) => {\n          ctx.lineTo(c.v, y + c.estimate * factor);\n        });\n    }\n    ctx.closePath();\n    ctx.stroke();\n    ctx.fill();\n  }\n\n  /**\n   * @hidden\n   */\n  _getBounds(useFinalPosition?: boolean): { left: number; top: number; right: number; bottom: number } {\n    if (this.isVertical()) {\n      const { x, width, min, max } = this.getProps(['x', 'width', 'min', 'max'], useFinalPosition);\n      const x0 = x - width / 2;\n      return {\n        left: x0,\n        top: max,\n        right: x0 + width,\n        bottom: min,\n      };\n    }\n    const { y, height, min, max } = this.getProps(['y', 'height', 'min', 'max'], useFinalPosition);\n    const y0 = y - height / 2;\n    return {\n      left: min,\n      top: y0,\n      right: max,\n      bottom: y0 + height,\n    };\n  }\n\n  static id = 'violin';\n\n  /**\n   * @hidden\n   */\n  static defaults = /* #__PURE__ */ { ...BarElement.defaults, ...baseDefaults };\n\n  /**\n   * @hidden\n   */\n  static defaultRoutes = /* #__PURE__ */ { ...BarElement.defaultRoutes, ...baseRoutes };\n}\n\ndeclare module 'chart.js' {\n  export interface ElementOptionsByType<TType extends ChartType> {\n    violin: ScriptableAndArrayOptions<IViolinElementOptions & CommonHoverOptions, ScriptableContext<TType>>;\n  }\n}\n","import { BarController, Element, ChartMeta, LinearScale, Scale, UpdateMode } from 'chart.js';\nimport { formatNumber } from 'chart.js/helpers';\nimport { interpolateNumberArray } from '../animation';\nimport { outlierPositioner, patchInHoveredOutlier } from '../tooltip';\nimport { defaultStatsOptions, IBaseOptions, IBaseStats } from '../data';\n/**\n * @hidden\n */\nexport /* #__PURE__ */ function baseDefaults(keys: string[]): Record<string, unknown> {\n  const colorKeys = ['borderColor', 'backgroundColor'].concat(keys.filter((c) => c.endsWith('Color')));\n  return {\n    animations: {\n      numberArray: {\n        fn: interpolateNumberArray,\n        properties: ['outliers', 'items'],\n      },\n      colors: {\n        type: 'color',\n        properties: colorKeys,\n      },\n    },\n    transitions: {\n      show: {\n        animations: {\n          colors: {\n            type: 'color',\n            properties: colorKeys,\n            from: 'transparent',\n          },\n        },\n      },\n      hide: {\n        animations: {\n          colors: {\n            type: 'color',\n            properties: colorKeys,\n            to: 'transparent',\n          },\n        },\n      },\n    },\n    minStats: 'min',\n    maxStats: 'max',\n    ...defaultStatsOptions,\n  };\n}\n\nexport function defaultOverrides(): Record<string, unknown> {\n  return {\n    plugins: {\n      tooltip: {\n        position: outlierPositioner.register().id,\n        callbacks: {\n          beforeLabel: patchInHoveredOutlier,\n        },\n      },\n    },\n  };\n}\n\nexport abstract class StatsBase<S extends IBaseStats, C extends Required<IBaseOptions>> extends BarController {\n  /**\n   * @hidden\n   */\n  declare options: C;\n\n  /**\n   * @hidden\n   */\n\n  protected _transformStats<T>(target: any, source: S, mapper: (v: number) => T): void {\n    for (const key of ['min', 'max', 'median', 'q3', 'q1', 'mean']) {\n      const v = source[key as keyof IBaseStats];\n      if (typeof v === 'number') {\n        target[key] = mapper(v);\n      }\n    }\n    for (const key of ['outliers', 'items']) {\n      if (Array.isArray(source[key as keyof IBaseStats])) {\n        target[key] = source[key as 'outliers' | 'items'].map(mapper);\n      }\n    }\n  }\n\n  /**\n   * @hidden\n   */\n  getMinMax(scale: Scale, canStack?: boolean | undefined): { min: number; max: number } {\n    const bak = scale.axis;\n    const config = this.options;\n\n    scale.axis = config.minStats;\n    const { min } = super.getMinMax(scale, canStack);\n\n    scale.axis = config.maxStats;\n    const { max } = super.getMinMax(scale, canStack);\n\n    scale.axis = bak;\n    return { min, max };\n  }\n\n  /**\n   * @hidden\n   */\n  parsePrimitiveData(meta: ChartMeta, data: any[], start: number, count: number): Record<string, unknown>[] {\n    const vScale = meta.vScale!;\n\n    const iScale = meta.iScale!;\n    const labels = iScale.getLabels();\n    const r = [];\n    for (let i = 0; i < count; i += 1) {\n      const index = i + start;\n      const parsed: any = {};\n      parsed[iScale.axis] = iScale.parse(labels[index], index);\n      const stats = this._parseStats(data == null ? null : data[index], this.options);\n      if (stats) {\n        Object.assign(parsed, stats);\n        parsed[vScale.axis] = stats.median;\n      }\n      r.push(parsed);\n    }\n    return r;\n  }\n\n  /**\n   * @hidden\n   */\n  parseArrayData(meta: ChartMeta, data: any[], start: number, count: number): Record<string, unknown>[] {\n    return this.parsePrimitiveData(meta, data, start, count);\n  }\n\n  /**\n   * @hidden\n   */\n  parseObjectData(meta: ChartMeta, data: any[], start: number, count: number): Record<string, unknown>[] {\n    return this.parsePrimitiveData(meta, data, start, count);\n  }\n\n  /**\n   * @hidden\n   */\n\n  protected abstract _parseStats(value: any, options: C): S | undefined;\n  /**\n   * @hidden\n   */\n  getLabelAndValue(index: number): {\n    label: string;\n    value: string & { raw: S; hoveredOutlierIndex: number; hoveredItemIndex: number } & S;\n  } {\n    const r = super.getLabelAndValue(index) as any;\n    const { vScale } = this._cachedMeta;\n    const parsed = this.getParsed(index) as unknown as S;\n    if (!vScale || !parsed || r.value === 'NaN') {\n      return r;\n    }\n    r.value = {\n      raw: parsed,\n      hoveredOutlierIndex: -1,\n      hoveredItemIndex: -1,\n    };\n    this._transformStats(r.value, parsed, (v) => vScale.getLabelForValue(v));\n    const s = this._toStringStats(r.value.raw);\n    r.value.toString = function toString() {\n      // custom to string function for the 'value'\n      if (this.hoveredOutlierIndex >= 0) {\n        // TODO formatter\n        return `(outlier: ${this.outliers[this.hoveredOutlierIndex]})`;\n      }\n      if (this.hoveredItemIndex >= 0) {\n        // TODO formatter\n        return `(item: ${this.items[this.hoveredItemIndex]})`;\n      }\n      return s;\n    };\n    return r;\n  }\n\n  /**\n   * @hidden\n   */\n\n  protected _toStringStats(b: S): string {\n    const f = (v: number) => (v == null ? 'NaN' : formatNumber(v, this.chart.options.locale!, {}));\n    return `(min: ${f(b.min)}, 25% quantile: ${f(b.q1)}, median: ${f(b.median)}, mean: ${f(b.mean)}, 75% quantile: ${f(\n      b.q3\n    )}, max: ${f(b.max)})`;\n  }\n\n  /**\n   * @hidden\n   */\n\n  updateElement(rectangle: Element, index: number, properties: any, mode: UpdateMode): void {\n    const reset = mode === 'reset';\n    const scale = this._cachedMeta.vScale as LinearScale;\n    const parsed = this.getParsed(index) as unknown as S;\n    const base = scale.getBasePixel();\n\n    properties._datasetIndex = this.index;\n\n    properties._index = index;\n    this._transformStats(properties, parsed, (v) => (reset ? base : scale.getPixelForValue(v, index)));\n    super.updateElement(rectangle, index, properties, mode);\n  }\n}\n","import {\n  Chart,\n  BarController,\n  ControllerDatasetOptions,\n  ScriptableAndArrayOptions,\n  CommonHoverOptions,\n  ChartItem,\n  ChartConfiguration,\n  LinearScale,\n  CategoryScale,\n  AnimationOptions,\n  ScriptableContext,\n  CartesianScaleTypeRegistry,\n  BarControllerDatasetOptions,\n} from 'chart.js';\nimport { merge } from 'chart.js/helpers';\nimport { asBoxPlotStats, IBoxPlot, IBoxplotOptions } from '../data';\nimport { baseDefaults, StatsBase, defaultOverrides } from './StatsBase';\nimport { BoxAndWiskers, IBoxAndWhiskersOptions } from '../elements';\nimport patchController from './patchController';\nimport { boxOptionsKeys } from '../elements/BoxAndWiskers';\n\nexport class BoxPlotController extends StatsBase<IBoxPlot, Required<IBoxplotOptions>> {\n  /**\n   * @hidden\n   */\n\n  protected _parseStats(value: unknown, config: IBoxplotOptions): IBoxPlot | undefined {\n    return asBoxPlotStats(value, config);\n  }\n\n  /**\n   * @hidden\n   */\n\n  protected _transformStats<T>(target: any, source: IBoxPlot, mapper: (v: number) => T): void {\n    super._transformStats(target, source, mapper);\n    for (const key of ['whiskerMin', 'whiskerMax']) {\n      target[key] = mapper(source[key as 'whiskerMin' | 'whiskerMax']);\n    }\n  }\n\n  static readonly id = 'boxplot';\n\n  /**\n   * @hidden\n   */\n  static readonly defaults: any = /* #__PURE__ */ merge({}, [\n    BarController.defaults,\n    baseDefaults(boxOptionsKeys),\n    {\n      animations: {\n        numbers: {\n          type: 'number',\n          properties: (BarController.defaults as any).animations.numbers.properties.concat(\n            ['q1', 'q3', 'min', 'max', 'median', 'whiskerMin', 'whiskerMax', 'mean'],\n            boxOptionsKeys.filter((c) => !c.endsWith('Color'))\n          ),\n        },\n      },\n      dataElementType: BoxAndWiskers.id,\n    },\n  ]);\n\n  /**\n   * @hidden\n   */\n  static readonly overrides: any = /* #__PURE__ */ merge({}, [(BarController as any).overrides, defaultOverrides()]);\n}\n\nexport interface BoxPlotControllerDatasetOptions\n  extends ControllerDatasetOptions,\n    Pick<\n      BarControllerDatasetOptions,\n      'barPercentage' | 'barThickness' | 'categoryPercentage' | 'maxBarThickness' | 'minBarLength'\n    >,\n    IBoxplotOptions,\n    ScriptableAndArrayOptions<IBoxAndWhiskersOptions, ScriptableContext<'boxplot'>>,\n    ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext<'boxplot'>>,\n    AnimationOptions<'boxplot'> {}\n\nexport type BoxPlotDataPoint = number[] | (Partial<IBoxPlot> & Pick<IBoxPlot, 'min' | 'max' | 'median' | 'q1' | 'q3'>);\n\nexport type IBoxPlotChartOptions = IBoxplotOptions;\n\ndeclare module 'chart.js' {\n  export interface ChartTypeRegistry {\n    boxplot: {\n      chartOptions: IBoxPlotChartOptions;\n      datasetOptions: BoxPlotControllerDatasetOptions;\n      defaultDataPoint: BoxPlotDataPoint;\n      scales: keyof CartesianScaleTypeRegistry;\n      metaExtensions: object;\n      parsedDataType: IBoxPlot & ChartTypeRegistry['bar']['parsedDataType'];\n    };\n  }\n}\n\nexport class BoxPlotChart<DATA extends unknown[] = BoxPlotDataPoint[], LABEL = string> extends Chart<\n  'boxplot',\n  DATA,\n  LABEL\n> {\n  static id = BoxPlotController.id;\n\n  constructor(item: ChartItem, config: Omit<ChartConfiguration<'boxplot', DATA, LABEL>, 'type'>) {\n    super(item, patchController('boxplot', config, BoxPlotController, BoxAndWiskers, [LinearScale, CategoryScale]));\n  }\n}\n","import {\n  Chart,\n  BarController,\n  ChartItem,\n  ControllerDatasetOptions,\n  ScriptableAndArrayOptions,\n  CommonHoverOptions,\n  ChartConfiguration,\n  LinearScale,\n  CategoryScale,\n  AnimationOptions,\n  ScriptableContext,\n  CartesianScaleTypeRegistry,\n  BarControllerDatasetOptions,\n} from 'chart.js';\nimport { merge } from 'chart.js/helpers';\nimport { asViolinStats, IViolin, IViolinOptions } from '../data';\nimport { StatsBase, baseDefaults, defaultOverrides } from './StatsBase';\nimport { baseOptionKeys } from '../elements/base';\nimport { IViolinElementOptions, Violin } from '../elements';\nimport { interpolateKdeCoords } from '../animation';\nimport patchController from './patchController';\n\nexport class ViolinController extends StatsBase<IViolin, Required<IViolinOptions>> {\n  /**\n   * @hidden\n   */\n\n  protected _parseStats(value: any, config: IViolinOptions): IViolin | undefined {\n    return asViolinStats(value, config);\n  }\n\n  /**\n   * @hidden\n   */\n\n  protected _transformStats<T>(target: any, source: IViolin, mapper: (v: number) => T): void {\n    super._transformStats(target, source, mapper);\n\n    target.maxEstimate = source.maxEstimate;\n    if (Array.isArray(source.coords)) {\n      target.coords = source.coords.map((c) => ({ ...c, v: mapper(c.v) }));\n    }\n  }\n\n  static readonly id = 'violin';\n\n  /**\n   * @hidden\n   */\n  static readonly defaults: any = /* #__PURE__ */ merge({}, [\n    BarController.defaults,\n    baseDefaults(baseOptionKeys),\n    {\n      points: 100,\n      animations: {\n        numbers: {\n          type: 'number',\n          properties: (BarController.defaults as any).animations.numbers.properties.concat(\n            ['q1', 'q3', 'min', 'max', 'median', 'maxEstimate'],\n            baseOptionKeys.filter((c) => !c.endsWith('Color'))\n          ),\n        },\n        kdeCoords: {\n          fn: interpolateKdeCoords,\n          properties: ['coords'],\n        },\n      },\n      dataElementType: Violin.id,\n    },\n  ]);\n\n  /**\n   * @hidden\n   */\n  static readonly overrides: any = /* #__PURE__ */ merge({}, [(BarController as any).overrides, defaultOverrides()]);\n}\nexport type ViolinDataPoint = number[] | (Partial<IViolin> & Pick<IViolin, 'median' | 'coords'>);\n\nexport interface ViolinControllerDatasetOptions\n  extends ControllerDatasetOptions,\n    Pick<\n      BarControllerDatasetOptions,\n      'barPercentage' | 'barThickness' | 'categoryPercentage' | 'maxBarThickness' | 'minBarLength'\n    >,\n    IViolinOptions,\n    ScriptableAndArrayOptions<IViolinElementOptions, ScriptableContext<'violin'>>,\n    ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext<'violin'>>,\n    AnimationOptions<'violin'> {}\n\nexport type IViolinChartOptions = IViolinOptions;\n\ndeclare module 'chart.js' {\n  export interface ChartTypeRegistry {\n    violin: {\n      chartOptions: IViolinChartOptions;\n      datasetOptions: ViolinControllerDatasetOptions;\n      defaultDataPoint: ViolinDataPoint;\n      scales: keyof CartesianScaleTypeRegistry;\n      metaExtensions: object;\n      parsedDataType: IViolin & ChartTypeRegistry['bar']['parsedDataType'];\n    };\n  }\n}\n\nexport class ViolinChart<DATA extends unknown[] = ViolinDataPoint[], LABEL = string> extends Chart<\n  'violin',\n  DATA,\n  LABEL\n> {\n  static id = ViolinController.id;\n\n  constructor(item: ChartItem, config: Omit<ChartConfiguration<'violin', DATA, LABEL>, 'type'>) {\n    super(item, patchController('violin', config, ViolinController, Violin, [LinearScale, CategoryScale]));\n  }\n}\n"],"names":[],"mappings":";;;;;;;;;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACRO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAyBO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtGO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxDO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AAKO;AACP;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACA;AACP;AACA;;ACvCO;AACA;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7CO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrBO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACO;AACA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;;AC1BO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACA;AACP;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;;;"}