`'average'` mode will place the tooltip at the average position of the items displayed in the tooltip. `'nearest'` will place the tooltip at the position of the element closest to the event position.
-New modes can be defined by adding functions to the `Chart.Tooltip.positioners` map.
-
-Example:
-
-```javascript
-/**
- * Custom positioner
- * @function Tooltip.positioners.myCustomPositioner
- * @param elements {Chart.Element[]} the tooltip elements
- * @param eventPosition {Point} the position of the event in canvas coordinates
- * @returns {Point} the tooltip position
- */
-const tooltipPlugin = Chart.registry.getPlugin('tooltip');
-tooltipPlugin.positioners.myCustomPositioner = function(elements, eventPosition) {
- /** @type {Tooltip} */
- const tooltip = this;
-
- /* ... */
-
- return {
- x: 0,
- y: 0
- };
-};
-```
+You can also define [custom position modes](#custom-position-modes).
### Tooltip Alignment
```javascript
{
+ chart: Chart,
+
// The items that we are rendering in the tooltip. See Tooltip Item Interface section
dataPoints: TooltipItem[],
opacity: number,
// tooltip options
- options : Object
+ options: Object
}
```
+
+## Custom Position Modes
+
+New modes can be defined by adding functions to the `Chart.Tooltip.positioners` map.
+
+Example:
+
+```javascript
+import { Tooltip } from 'chart.js';
+
+/**
+ * Custom positioner
+ * @function Tooltip.positioners.myCustomPositioner
+ * @param elements {Chart.Element[]} the tooltip elements
+ * @param eventPosition {Point} the position of the event in canvas coordinates
+ * @returns {TooltipPosition} the tooltip position
+ */
+Tooltip.positioners.myCustomPositioner = function(elements, eventPosition) {
+ // A reference to the tooltip model
+ const tooltip = this;
+
+ /* ... */
+
+ return {
+ x: 0,
+ y: 0
+ // You may also include xAlign and yAlign to override those tooltip options.
+ };
+};
+
+// Then, to use it...
+new Chart.js(ctx, {
+ data,
+ options: {
+ plugins: {
+ tooltip: {
+ position: 'myCustomPositioner'
+ }
+ }
+ }
+})
+```
+
+See [samples](/samples/tooltip/position.md) for a more detailed example.
+
+If you're using TypeScript, you'll also need to register the new mode:
+
+```typescript
+declare module 'chart.js' {
+ interface TooltipPositionerMap {
+ myCustomPositioner: TooltipPositionerFunction<ChartType>;
+ }
+}
+```
\ No newline at end of file
import {createContext, drawPoint} from '../helpers';
/**
+ * @typedef { import("../platform/platform.base").Chart } Chart
* @typedef { import("../platform/platform.base").ChartEvent } ChartEvent
+ * @typedef { import("../../types/index.esm").ActiveElement } ActiveElement
*/
const positioners = {
/**
* Private helper to create a tooltip item model
- * @param item - {element, index, datasetIndex} to create the tooltip item for
+ * @param {Chart} chart
+ * @param {ActiveElement} item - {element, index, datasetIndex} to create the tooltip item for
* @return new tooltip item
*/
function createTooltipItem(chart, item) {
* Get the size of the tooltip
*/
function getTooltipSize(tooltip, options) {
- const ctx = tooltip._chart.ctx;
+ const ctx = tooltip.chart.ctx;
const {body, footer, title} = tooltip;
const {boxWidth, boxHeight} = options;
const bodyFont = toFont(options.bodyFont);
* Helper to get the alignment of a tooltip given the size
*/
function determineAlignment(chart, options, size) {
- const yAlign = options.yAlign || determineYAlign(chart, size);
+ const yAlign = size.yAlign || options.yAlign || determineYAlign(chart, size);
return {
- xAlign: options.xAlign || determineXAlign(chart, options, size, yAlign),
+ xAlign: size.xAlign || options.xAlign || determineXAlign(chart, options, size, yAlign),
yAlign
};
}
this.opacity = 0;
this._active = [];
- this._chart = config._chart;
this._eventPosition = undefined;
this._size = undefined;
this._cachedAnimations = undefined;
this._tooltipItems = [];
this.$animations = undefined;
this.$context = undefined;
+ // TODO: V4, remove config._chart and this._chart backward compatibility aliases
+ this.chart = config.chart || config._chart;
+ this._chart = this.chart;
this.options = config.options;
this.dataPoints = undefined;
this.title = undefined;
return cached;
}
- const chart = this._chart;
+ const chart = this.chart;
const options = this.options.setContext(this.getContext());
const opts = options.enabled && chart.options.animation && options.animations;
- const animations = new Animations(this._chart, opts);
+ const animations = new Animations(this.chart, opts);
if (opts._cacheable) {
this._cachedAnimations = Object.freeze(animations);
}
*/
getContext() {
return this.$context ||
- (this.$context = createTooltipContext(this._chart.getContext(), this, this._tooltipItems));
+ (this.$context = createTooltipContext(this.chart.getContext(), this, this._tooltipItems));
}
getTitle(context, options) {
*/
_createItems(options) {
const active = this._active;
- const data = this._chart.data;
+ const data = this.chart.data;
const labelColors = [];
const labelPointStyles = [];
const labelTextColors = [];
let i, len;
for (i = 0, len = active.length; i < len; ++i) {
- tooltipItems.push(createTooltipItem(this._chart, active[i]));
+ tooltipItems.push(createTooltipItem(this.chart, active[i]));
}
// If the user provided a filter function, use it to modify the tooltip items
const size = this._size = getTooltipSize(this, options);
const positionAndSize = Object.assign({}, position, size);
- const alignment = determineAlignment(this._chart, options, positionAndSize);
- const backgroundPoint = getBackgroundPoint(options, positionAndSize, alignment, this._chart);
+ const alignment = determineAlignment(this.chart, options, positionAndSize);
+ const backgroundPoint = getBackgroundPoint(options, positionAndSize, alignment, this.chart);
this.xAlign = alignment.xAlign;
this.yAlign = alignment.yAlign;
}
if (changed && options.external) {
- options.external.call(this, {chart: this._chart, tooltip: this, replay});
+ options.external.call(this, {chart: this.chart, tooltip: this, replay});
}
}
* @private
*/
_updateAnimationTarget(options) {
- const chart = this._chart;
+ const chart = this.chart;
const anims = this.$animations;
const animX = anims && anims.x;
const animY = anims && anims.y;
setActiveElements(activeElements, eventPosition) {
const lastActive = this._active;
const active = activeElements.map(({datasetIndex, index}) => {
- const meta = this._chart.getDatasetMeta(datasetIndex);
+ const meta = this.chart.getDatasetMeta(datasetIndex);
if (!meta) {
throw new Error('Cannot find a dataset at index ' + datasetIndex);
// Find Active Elements for tooltips
if (e.type !== 'mouseout') {
- active = this._chart.getElementsAtEventForMode(e, options.mode, options, replay);
+ active = this.chart.getElementsAtEventForMode(e, options.mode, options, replay);
if (options.reverse) {
active.reverse();
}
afterInit(chart, _args, options) {
if (options) {
- chart.tooltip = new Tooltip({_chart: chart, options});
+ chart.tooltip = new Tooltip({chart, options});
}
},
*/
borderRadius?: number | BorderRadius;
}
-export interface TooltipModel<TType extends ChartType> {
+export interface TooltipModel<TType extends ChartType> extends Element<AnyObject, TooltipOptions<TType>> {
+ readonly chart: Chart<TType>;
+
// The items that we are rendering in the tooltip. See Tooltip Item Interface section
dataPoints: TooltipItem<TType>[];
options: TooltipOptions<TType>;
getActiveElements(): ActiveElement[];
- setActiveElements(active: ActiveDataPoint[], eventPosition: { x: number, y: number }): void;
+ setActiveElements(active: ActiveDataPoint[], eventPosition: Point): void;
}
-export const Tooltip: Plugin & {
- readonly positioners: {
- [key: string]: (items: readonly ActiveElement[], eventPosition: { x: number; y: number }) => { x: number; y: number } | false;
- };
-};
+export interface TooltipPosition {
+ x: number;
+ y: number;
+ xAlign?: TooltipXAlignment;
+ yAlign?: TooltipYAlignment;
+}
+
+export type TooltipPositionerFunction<TType extends ChartType> = (
+ this: TooltipModel<TType>,
+ items: readonly ActiveElement[],
+ eventPosition: Point
+) => TooltipPosition | false;
+
+export interface TooltipPositionerMap {
+ average: TooltipPositionerFunction<ChartType>;
+ nearest: TooltipPositionerFunction<ChartType>;
+}
+
+export type TooltipPositioner = keyof TooltipPositionerMap;
+
+export interface Tooltip extends Plugin {
+ readonly positioners: TooltipPositionerMap;
+}
+
+export const Tooltip: Tooltip;
export interface TooltipCallbacks<
TType extends ChartType,
/**
* The mode for positioning the tooltip
*/
- position: Scriptable<'average' | 'nearest', ScriptableTooltipContext<TType>>
+ position: Scriptable<TooltipPositioner, ScriptableTooltipContext<TType>>
/**
* Override the tooltip alignment calculations
*/
bodyColor: Scriptable<Color, ScriptableTooltipContext<TType>>;
/**
- * See Fonts.
+ * See Fonts.
* @default {}
*/
bodyFont: Scriptable<FontSpec, ScriptableTooltipContext<TType>>;
*/
padding: Scriptable<number | ChartArea, ScriptableTooltipContext<TType>>;
/**
- * Extra distance to move the end of the tooltip arrow away from the tooltip point.
+ * Extra distance to move the end of the tooltip arrow away from the tooltip point.
* @default 2
*/
caretPadding: Scriptable<number, ScriptableTooltipContext<TType>>;