# Plugins

Built-in and extensible plugin system for additional functionality. Plugins provide lifecycle hooks and configurable features that extend chart behavior.

## Capabilities

### Title Plugin

Displays chart titles and subtitles.

```javascript { .api }
const Title: Plugin = {
  id: 'title',
  defaults: object,
  // Plugin implementation
};

interface TitleOptions {
  display?: boolean;
  position?: 'top' | 'left' | 'bottom' | 'right';
  align?: 'start' | 'center' | 'end';
  color?: Color;
  font?: FontSpec;
  fullSize?: boolean;
  padding?: number | Padding;
  text?: string | string[];
}
```

**Usage Example:**

```javascript
const config = {
  type: 'bar',
  data: { /* data */ },
  options: {
    plugins: {
      title: {
        display: true,
        text: 'Chart Title',
        position: 'top',
        align: 'center',
        color: '#333',
        font: {
          size: 18,
          weight: 'bold'
        },
        padding: {
          top: 10,
          bottom: 30
        }
      }
    }
  }
};
```

### Legend Plugin

Displays chart legend for datasets.

```javascript { .api }
const Legend: Plugin = {
  id: 'legend',
  defaults: object,
  // Plugin implementation
};

interface LegendOptions {
  display?: boolean;
  position?: 'top' | 'left' | 'bottom' | 'right' | 'chartArea';
  align?: 'start' | 'center' | 'end';
  maxHeight?: number;
  maxWidth?: number;
  fullSize?: boolean;
  onClick?: (event: ChartEvent, legendItem: LegendItem, legend: LegendElement) => void;
  onHover?: (event: ChartEvent, legendItem: LegendItem, legend: LegendElement) => void;
  onLeave?: (event: ChartEvent, legendItem: LegendItem, legend: LegendElement) => void;
  reverse?: boolean;
  labels?: LegendLabelOptions;
  rtl?: boolean;
  textDirection?: string;
  title?: LegendTitleOptions;
}

interface LegendLabelOptions {
  boxWidth?: number;
  boxHeight?: number;
  color?: Color;
  font?: FontSpec;
  padding?: number;
  generateLabels?: (chart: Chart) => LegendItem[];
  filter?: (item: LegendItem, data: ChartData) => boolean;
  sort?: (a: LegendItem, b: LegendItem, data: ChartData) => number;
  pointStyle?: PointStyle | HTMLImageElement | HTMLCanvasElement;
  textAlign?: 'left' | 'right' | 'center';
  usePointStyle?: boolean;
  useBorderRadius?: boolean;
  borderRadius?: number;
}

interface LegendItem {
  text: string;
  lineCap?: CanvasLineCap;
  lineDash?: number[];
  lineDashOffset?: number;
  lineJoin?: CanvasLineJoin;
  lineWidth?: number;
  strokeStyle?: Color;
  pointStyle?: PointStyle | HTMLImageElement | HTMLCanvasElement;
  rotation?: number;
  boxWidth?: number;
  boxHeight?: number;
  color?: Color;
  fillStyle?: Color;
  fontColor?: Color;
  hidden?: boolean;
  index?: number;
  datasetIndex?: number;
}
```

**Usage Example:**

```javascript
const config = {
  type: 'line',
  data: { /* data */ },
  options: {
    plugins: {
      legend: {
        display: true,
        position: 'bottom',
        align: 'center',
        labels: {
          usePointStyle: true,
          padding: 20,
          font: {
            size: 14
          },
          generateLabels: (chart) => {
            // Custom label generation
            return chart.data.datasets.map((dataset, i) => ({
              text: dataset.label,
              fillStyle: dataset.backgroundColor,
              strokeStyle: dataset.borderColor,
              hidden: !chart.isDatasetVisible(i),
              index: i
            }));
          }
        }
      }
    }
  }
};
```

### Tooltip Plugin

Interactive tooltips on hover.

```javascript { .api }
const Tooltip: Plugin = {
  id: 'tooltip',
  defaults: object,
  // Plugin implementation
};

interface TooltipOptions {
  enabled?: boolean;
  external?: (context: TooltipContext) => void;
  mode?: InteractionMode;
  intersect?: boolean;
  position?: 'average' | 'nearest' | TooltipPositioner;
  callbacks?: TooltipCallbacks;
  itemSort?: (a: TooltipItem, b: TooltipItem, data: ChartData) => number;
  filter?: (tooltipItem: TooltipItem, index: number, tooltipItems: TooltipItem[], data: ChartData) => boolean;
  backgroundColor?: Color;
  titleColor?: Color;
  titleFont?: FontSpec;
  titleAlign?: 'left' | 'center' | 'right';
  titleSpacing?: number;
  titleMarginBottom?: number;
  bodyColor?: Color;
  bodyFont?: FontSpec;
  bodyAlign?: 'left' | 'center' | 'right';
  bodySpacing?: number;
  footerColor?: Color;
  footerFont?: FontSpec;
  footerAlign?: 'left' | 'center' | 'right';
  footerSpacing?: number;
  footerMarginTop?: number;
  padding?: number | Padding;
  caretPadding?: number;
  caretSize?: number;
  cornerRadius?: number;
  multiKeyBackground?: Color;
  displayColors?: boolean;
  boxWidth?: number;
  boxHeight?: number;
  boxPadding?: number;
  usePointStyle?: boolean;
  borderColor?: Color;
  borderWidth?: number;
  rtl?: boolean;
  textDirection?: string;
  xAlign?: 'left' | 'center' | 'right';
  yAlign?: 'top' | 'center' | 'bottom';
}

interface TooltipCallbacks {
  beforeTitle?: (tooltipItems: TooltipItem[]) => string | string[];
  title?: (tooltipItems: TooltipItem[]) => string | string[];
  afterTitle?: (tooltipItems: TooltipItem[]) => string | string[];
  beforeBody?: (tooltipItems: TooltipItem[]) => string | string[];
  beforeLabel?: (tooltipItem: TooltipItem) => string | string[];
  label?: (tooltipItem: TooltipItem) => string | string[];
  labelColor?: (tooltipItem: TooltipItem) => TooltipLabelStyle;
  labelTextColor?: (tooltipItem: TooltipItem) => Color;
  labelPointStyle?: (tooltipItem: TooltipItem) => PointStyle | { pointStyle: PointStyle; rotation: number };
  afterLabel?: (tooltipItem: TooltipItem) => string | string[];
  afterBody?: (tooltipItems: TooltipItem[]) => string | string[];
  beforeFooter?: (tooltipItems: TooltipItem[]) => string | string[];
  footer?: (tooltipItems: TooltipItem[]) => string | string[];
  afterFooter?: (tooltipItems: TooltipItem[]) => string | string[];
}

interface TooltipItem {
  chart: Chart;
  label: string;
  parsed: ParsedDataType;
  raw: unknown;
  formattedValue: string;
  dataset: ChartDataset;
  datasetIndex: number;
  dataIndex: number;
  element: Element;
}
```

### Subtitle Plugin

Displays chart subtitles.

```javascript { .api }
const SubTitle: Plugin = {
  id: 'subtitle',
  defaults: object,
  // Plugin implementation
};

// SubTitleOptions extends TitleOptions
interface SubTitleOptions extends TitleOptions {
  // Inherits all title options
}
```

### Filler Plugin

Fills areas between datasets or to axes.

```javascript { .api }
const Filler: Plugin = {
  id: 'filler',
  defaults: object,
  // Plugin implementation
};

interface FillerOptions {
  propagate?: boolean;
  drawTime?: 'beforeDatasetDraw' | 'beforeDatasetsDraw';
}

// Fill configurations (used in dataset options)
type FillTarget = boolean | number | string | 'start' | 'end' | 'origin' | 'stack' | 'shape' | { target: FillTarget; above?: Color; below?: Color };
```

**Usage Example:**

```javascript
const config = {
  type: 'line',
  data: {
    datasets: [{
      label: 'Dataset 1',
      data: [10, 20, 30, 40],
      fill: 'start', // Fill to start of chart
      backgroundColor: 'rgba(75, 192, 192, 0.2)'
    }, {
      label: 'Dataset 2', 
      data: [15, 25, 35, 45],
      fill: '-1', // Fill to previous dataset
      backgroundColor: 'rgba(255, 99, 132, 0.2)'
    }]
  },
  options: {
    plugins: {
      filler: {
        propagate: true
      }
    }
  }
};
```

### Decimation Plugin

Reduces data points for performance with large datasets.

```javascript { .api }
const Decimation: Plugin = {
  id: 'decimation',
  defaults: object,
  // Plugin implementation
};

interface DecimationOptions {
  enabled?: boolean;
  algorithm?: 'lttb' | 'min-max';
  samples?: number;
  threshold?: number;
}
```

### Colors Plugin

Automatic color assignment for datasets.

```javascript { .api }
const Colors: Plugin = {
  id: 'colors',
  defaults: object,
  // Plugin implementation
};

interface ColorsOptions {
  enabled?: boolean;
  forceOverride?: boolean;
}
```

### Plugin Interface

Base interface for creating custom plugins.

```javascript { .api }
interface Plugin<TType extends ChartType = ChartType, O = object> {
  id: string;
  
  // Lifecycle hooks
  beforeInit?(chart: Chart<TType>, args: EmptyObject, options: O): void;
  afterInit?(chart: Chart<TType>, args: EmptyObject, options: O): void;
  beforeUpdate?(chart: Chart<TType>, args: UpdateArgs, options: O): boolean | void;
  afterUpdate?(chart: Chart<TType>, args: UpdateArgs, options: O): void;
  beforeElementsUpdate?(chart: Chart<TType>, args: EmptyObject, options: O): void;
  beforeLayout?(chart: Chart<TType>, args: EmptyObject, options: O): boolean | void;
  afterLayout?(chart: Chart<TType>, args: EmptyObject, options: O): void;
  beforeDatasetsUpdate?(chart: Chart<TType>, args: UpdateArgs, options: O): boolean | void;
  afterDatasetsUpdate?(chart: Chart<TType>, args: UpdateArgs, options: O): void;
  beforeDatasetUpdate?(chart: Chart<TType>, args: DatasetUpdateArgs, options: O): boolean | void;
  afterDatasetUpdate?(chart: Chart<TType>, args: DatasetUpdateArgs, options: O): void;
  beforeRender?(chart: Chart<TType>, args: EmptyObject, options: O): boolean | void;
  afterRender?(chart: Chart<TType>, args: EmptyObject, options: O): void;
  beforeDraw?(chart: Chart<TType>, args: EmptyObject, options: O): boolean | void;
  afterDraw?(chart: Chart<TType>, args: EmptyObject, options: O): void;
  beforeDatasetsDraw?(chart: Chart<TType>, args: EmptyObject, options: O): boolean | void;
  afterDatasetsDraw?(chart: Chart<TType>, args: EmptyObject, options: O): void;
  beforeDatasetDraw?(chart: Chart<TType>, args: DatasetDrawArgs, options: O): boolean | void;
  afterDatasetDraw?(chart: Chart<TType>, args: DatasetDrawArgs, options: O): void;
  beforeTooltipDraw?(chart: Chart<TType>, args: TooltipDrawArgs, options: O): boolean | void;
  afterTooltipDraw?(chart: Chart<TType>, args: TooltipDrawArgs, options: O): void;
  beforeEvent?(chart: Chart<TType>, args: EventArgs, options: O): boolean | void;
  afterEvent?(chart: Chart<TType>, args: EventArgs, options: O): void;
  resize?(chart: Chart<TType>, args: ResizeArgs, options: O): void;
  reset?(chart: Chart<TType>, args: EmptyObject, options: O): void;
  stop?(chart: Chart<TType>, args: EmptyObject, options: O): void;
  destroy?(chart: Chart<TType>, args: EmptyObject, options: O): void;
  
  // Optional properties
  defaults?: object;
  defaultRoutes?: { [property: string]: string };
  descriptors?: { [property: string]: boolean };
}
```

**Usage Example (Custom Plugin):**

```javascript
const customPlugin = {
  id: 'customPlugin',
  beforeDraw: (chart, args, options) => {
    const { ctx, chartArea } = chart;
    
    // Custom drawing logic
    ctx.fillStyle = options.backgroundColor || 'lightgray';
    ctx.fillRect(chartArea.left, chartArea.top, chartArea.width, chartArea.height);
  }
};

// Register and use
Chart.register(customPlugin);

const config = {
  type: 'bar',
  data: { /* data */ },
  plugins: [customPlugin],
  options: {
    plugins: {
      customPlugin: {
        backgroundColor: 'lightblue'
      }
    }
  }
};
```

## Plugin Registration

Plugins must be registered before use.

```javascript { .api }
import {
  Chart,
  Title,
  Tooltip,
  Legend,
  SubTitle,
  Filler,
  Decimation,
  Colors
} from 'chart.js';

// Register built-in plugins
Chart.register(
  Title,
  Tooltip,
  Legend,
  SubTitle,
  Filler,
  Decimation,
  Colors
);
```

## Types

```javascript { .api }
type InteractionMode = 'point' | 'nearest' | 'index' | 'dataset' | 'x' | 'y';

type PointStyle = 'circle' | 'cross' | 'crossRot' | 'dash' | 'line' | 'rect' | 'rectRounded' | 'rectRot' | 'star' | 'triangle';

interface FontSpec {
  family?: string;
  size?: number;
  style?: 'normal' | 'italic' | 'oblique';
  weight?: string | number;
  lineHeight?: number | string;
}

interface Padding {
  top?: number;
  right?: number;
  bottom?: number;
  left?: number;
}

interface EmptyObject {}

interface UpdateArgs {
  mode: UpdateMode;
}

interface DatasetUpdateArgs {
  index: number;
  mode: UpdateMode;
}

interface EventArgs {
  event: ChartEvent;
  replay: boolean;
  changed?: boolean;
  cancelable: boolean;
  inChartArea: boolean;
}

type Color = string | CanvasGradient | CanvasPattern;

interface ChartEvent {
  type: string;
  x?: number;
  y?: number;
  native?: Event;
}

interface TooltipContext<TType extends ChartType = ChartType> {
  chart: Chart<TType>;
  tooltip: TooltipModel<TType>;
  tooltipItems: TooltipItem<TType>[];
}

interface TooltipModel<TType extends ChartType = ChartType> {
  opacity: number;
  title: string[];
  body: TooltipBodyItem[];
  footer: string[];
  x: number;
  y: number;
  width: number;
  height: number;
  caretX: number;
  caretY: number;
}

interface TooltipBodyItem {
  before: string[];
  lines: string[];
  after: string[];
}

type TooltipPositioner = (elements: Element[], eventPosition: Point) => Point;

type ParsedDataType = { [key: string]: unknown };

interface LegendElement {
  chart: Chart;
  options: LegendOptions;
  legendItems?: LegendItem[];
}
```