import { getHtmlStringOutput, stripTags } from '@slickgrid-universal/utils'; import { SlickEventHandler, type SlickEventData, type SlickGrid } from '../core/index.js'; import type { AutoTooltipOption, Column } from '../interfaces/index.js'; /** * AutoTooltips plugin to show/hide tooltips when columns are too narrow to fit content. * @constructor * @param {boolean} [options.enableForCells=true] - Enable tooltip for grid cells * @param {boolean} [options.enableForHeaderCells=false] - Enable tooltip for header cells * @param {number} [options.maxToolTipLength=null] - The maximum length for a tooltip */ export class SlickAutoTooltip { readonly pluginName = 'AutoTooltips'; protected _addonOptions?: AutoTooltipOption; protected _eventHandler: SlickEventHandler; protected _grid!: SlickGrid; protected _defaults = { enableForCells: true, enableForHeaderCells: false, maxToolTipLength: undefined, replaceExisting: true, } as AutoTooltipOption; /** Constructor of the SlickGrid 3rd party plugin, it can optionally receive options */ constructor(options?: AutoTooltipOption) { this._eventHandler = new SlickEventHandler(); this._addonOptions = options; } get addonOptions(): AutoTooltipOption { return this._addonOptions as AutoTooltipOption; } get eventHandler(): SlickEventHandler { return this._eventHandler; } /** Initialize plugin. */ init(grid: SlickGrid): void { this._addonOptions = { ...this._defaults, ...this.addonOptions }; this._grid = grid; if (this._addonOptions.enableForCells) { this._eventHandler.subscribe(this._grid.onMouseEnter, this.handleMouseEnter.bind(this)); } if (this._addonOptions.enableForHeaderCells) { this._eventHandler.subscribe(this._grid.onHeaderMouseEnter, this.handleHeaderMouseEnter.bind(this)); } } destroy(): void { this.dispose(); } /** Dispose (destroy) the SlickGrid 3rd party plugin */ dispose(): void { this._eventHandler?.unsubscribeAll(); } // -- // protected functions // ------------------ /** * Handle mouse entering grid cell to add/remove tooltip. * @param {SlickEventData} event - The event */ protected handleMouseEnter(event: SlickEventData): void { const cell = this._grid.getCellFromEvent(event); if (cell) { let node: HTMLElement | null = this._grid.getCellNode(cell.row, cell.cell); let text; if (this._addonOptions && node && (!node.title || this._addonOptions?.replaceExisting)) { if (node.clientWidth < node.scrollWidth) { text = node.textContent?.trim() ?? ''; if (this._addonOptions?.maxToolTipLength && text.length > this._addonOptions?.maxToolTipLength) { text = text.substring(0, this._addonOptions.maxToolTipLength - 3) + '...'; } } else { text = ''; } node.title = text; } node = null; } } /** * Handle mouse entering header cell to add/remove tooltip. * @param {SlickEventData} event - The event * @param {Object} args.column - The column definition */ protected handleHeaderMouseEnter(event: SlickEventData, args: { column: Column }): void { const column = args.column; let node: HTMLDivElement | null; const targetElm = event.target as HTMLDivElement; if (targetElm) { node = targetElm.closest('.slick-header-column'); if (node && !column?.toolTip) { const titleVal = targetElm.clientWidth < node.clientWidth ? (column?.name ?? '') : ''; node.title = stripTags(getHtmlStringOutput(titleVal, 'innerHTML')); } } node = null; } }