Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 0 additions & 19 deletions packages/ag-grid-community/src/rendering/cell/cellCtrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ export class CellCtrl extends BeanStub {

public tooltipFeature: TooltipFeature | undefined = undefined;
public editorTooltipFeature: TooltipFeature | undefined = undefined;
private formulaTooltipFeature: TooltipFeature | undefined = undefined;

constructor(
public readonly column: AgColumn,
Expand Down Expand Up @@ -166,7 +165,6 @@ export class CellCtrl extends BeanStub {
this.keyboardListener = new CellKeyboardListenerFeature(this, beans, this.rowNode, this.rowCtrl);

this.enableTooltipFeature();
this.enableFormulaTooltipFeature();

const { rangeSvc } = beans;
const cellSelectionEnabled = rangeSvc && _isCellSelectionEnabled(beans.gos);
Expand Down Expand Up @@ -199,25 +197,16 @@ export class CellCtrl extends BeanStub {
this.rowResizeFeature = context.destroyBean(this.rowResizeFeature);

this.disableTooltipFeature();
this.disableFormulaTooltipFeature();
}

private enableTooltipFeature(value?: string, shouldDisplayTooltip?: () => boolean): void {
this.tooltipFeature = this.beans.tooltipSvc?.enableCellTooltipFeature(this, value, shouldDisplayTooltip);
}

private enableFormulaTooltipFeature() {
this.formulaTooltipFeature = this.beans.tooltipSvc?.setupFormulaTooltip(this);
}

private disableTooltipFeature() {
this.tooltipFeature = this.beans.context.destroyBean(this.tooltipFeature);
}

private disableFormulaTooltipFeature() {
this.formulaTooltipFeature = this.beans.context.destroyBean(this.formulaTooltipFeature);
}

public enableEditorTooltipFeature(editor: ICellEditor): void {
if (this.editorTooltipFeature) {
this.disableEditorTooltipFeature();
Expand Down Expand Up @@ -301,14 +290,6 @@ export class CellCtrl extends BeanStub {
private checkFormulaError() {
const isFormulaError = !!this.beans.formula?.getFormulaError(this.column, this.rowNode);
this.eGui.classList.toggle('formula-error', isFormulaError);
this.formulaTooltipFeature?.refreshTooltip();

// don't allow multiple tooltips simultaneously
if (isFormulaError) {
this.disableTooltipFeature();
} else {
this.enableTooltipFeature();
}
}

private setupAutoHeight(eCellWrapper: HTMLElement | undefined, compBean: BeanStub): void {
Expand Down
266 changes: 162 additions & 104 deletions packages/ag-grid-community/src/tooltip/tooltipService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ import type { RowCtrl } from '../rendering/row/rowCtrl';
import type { ITooltipCtrl, ITooltipCtrlParams, TooltipFeature } from './tooltipFeature';
import { _isShowTooltipWhenTruncated } from './tooltipFeature';

type CellTooltipLocation = 'cell' | 'cellEditor' | 'cellFormula';

type ResolvedCellTooltip = {
value: string | null | undefined;
location: CellTooltipLocation;
shouldDisplay?: () => boolean;
};

type CellTooltipDisplayFunctions = {
shouldDisplayDefault: () => boolean;
shouldDisplayColumnTooltip: () => boolean;
shouldDisplayCustomTooltip: () => boolean;
};

const getEditErrorsForPosition = (
beans: BeanCollection,
cellCtrl: CellCtrl,
Expand All @@ -30,6 +44,131 @@ const getEditErrorsForPosition = (
return errors?.length ? errors.join(translate('tooltipValidationErrorSeparator', '. ')) : undefined;
};

const getCellTruncationCheck = (beans: BeanCollection, ctrl: CellCtrl): (() => boolean) | undefined => {
const isTooltipWhenTruncated = _isShowTooltipWhenTruncated(beans.gos);
if (!isTooltipWhenTruncated || ctrl.isCellRenderer()) {
return undefined;
}

return _isElementOverflowingCallback(() => {
const eCell = ctrl.eGui;
return eCell.children.length === 0 ? eCell : (eCell.querySelector('.ag-cell-value') as HTMLElement | undefined);
});
};

const buildCellTooltipDisplayFunctions = (
beans: BeanCollection,
ctrl: CellCtrl,
shouldDisplayTooltip?: () => boolean
): CellTooltipDisplayFunctions => {
const { editSvc } = beans;
const { column } = ctrl;
const isCellTruncated = getCellTruncationCheck(beans, ctrl);

const shouldDisplayCellTooltip = () => {
if (editSvc?.isEditing(ctrl)) {
return false;
}
if (!isCellTruncated) {
return true;
}
if (!column.isTooltipEnabled()) {
return false;
}
return isCellTruncated();
};

return {
shouldDisplayDefault: shouldDisplayCellTooltip,
shouldDisplayColumnTooltip: shouldDisplayCellTooltip,
shouldDisplayCustomTooltip: shouldDisplayTooltip ?? shouldDisplayCellTooltip,
};
};

const resolveCellTooltip = ({
beans,
ctrl,
value,
displayFunctions,
translate,
}: {
beans: BeanCollection;
ctrl: CellCtrl;
value?: string;
displayFunctions: CellTooltipDisplayFunctions;
translate: LocaleTextFunc;
}): ResolvedCellTooltip | null => {
const { editSvc, formula, gos } = beans;
const { column, rowNode } = ctrl;

// 1) formula error tooltip has highest priority.
if (formula?.active && column.isAllowFormula()) {
const error = formula.getFormulaError(column, rowNode);
if (error) {
return {
value: error.message,
location: 'cellFormula',
shouldDisplay: () => !!formula?.getFormulaError(column, rowNode),
};
}
}

// 2) edit-model validation errors take priority when not editing.
const isEditing = !!editSvc?.isEditing(ctrl);
if (!isEditing) {
const errorMessages = getEditErrorsForPosition(beans, ctrl, translate);
if (errorMessages) {
return {
value: errorMessages,
location: 'cellEditor',
shouldDisplay: () => !editSvc?.isEditing(ctrl) && !!getEditErrorsForPosition(beans, ctrl, translate),
};
}
}

const { shouldDisplayCustomTooltip, shouldDisplayColumnTooltip } = displayFunctions;

// 3) explicit value from cellRenderer params (setTooltip) wins over colDef tooltips.
if (value != null) {
return { value, location: 'cell', shouldDisplay: shouldDisplayCustomTooltip };
}

const colDef = column.getColDef();
const data = rowNode.data;

// 4) column tooltip field/valueGetter is the final fallback.
if (colDef.tooltipField && _exists(data)) {
return {
value: _getValueUsingField(data, colDef.tooltipField, column.isTooltipFieldContainsDots()),
location: 'cell',
shouldDisplay: shouldDisplayColumnTooltip,
};
}

const valueGetter = colDef.tooltipValueGetter;

if (valueGetter) {
return {
value: valueGetter(
_addGridCommonParams(gos, {
location: 'cell',
colDef: column.getColDef(),
column: column,
rowIndex: ctrl.cellPosition.rowIndex,
node: rowNode,
data: rowNode.data,
value: ctrl.value,
valueFormatted: ctrl.valueFormatted,
})
),
location: 'cell',
shouldDisplay: shouldDisplayColumnTooltip,
};
}

return null;
};

export class TooltipService extends BeanStub implements NamedBean {
beanName = 'tooltipSvc' as const;

Expand Down Expand Up @@ -137,86 +276,35 @@ export class TooltipService extends BeanStub implements NamedBean {
shouldDisplayTooltip?: () => boolean
): TooltipFeature | undefined {
const { beans } = this;
const { gos, editSvc } = beans;
const { column, rowNode } = ctrl;

let location: 'cell' | 'cellEditor' = 'cell';

const getTooltipValue = () => {
const isEditing = !!editSvc?.isEditing(ctrl);
const errorMessages = !isEditing && getEditErrorsForPosition(beans, ctrl, this.getLocaleTextFunc());

if (errorMessages) {
location = 'cellEditor';
return errorMessages;
}

location = 'cell';

const colDef = column.getColDef();
const data = rowNode.data;

if (colDef.tooltipField && _exists(data)) {
return _getValueUsingField(data, colDef.tooltipField, column.isTooltipFieldContainsDots());
}

const valueGetter = colDef.tooltipValueGetter;

if (valueGetter) {
return valueGetter(
_addGridCommonParams(gos, {
location: 'cell',
colDef: column.getColDef(),
column: column,
rowIndex: ctrl.cellPosition.rowIndex,
node: rowNode,
data: rowNode.data,
value: ctrl.value,
valueFormatted: ctrl.valueFormatted,
})
);
}

return null;
const displayFunctions = buildCellTooltipDisplayFunctions(beans, ctrl, shouldDisplayTooltip);
const translate = this.getLocaleTextFunc();
let resolvedTooltip: ResolvedCellTooltip | null = null;

const resolveAndStore = () => {
resolvedTooltip = resolveCellTooltip({
beans,
ctrl,
value,
displayFunctions,
translate,
});
return resolvedTooltip;
};

const isTooltipWhenTruncated = _isShowTooltipWhenTruncated(gos);

if (!shouldDisplayTooltip) {
if (isTooltipWhenTruncated && !ctrl.isCellRenderer()) {
shouldDisplayTooltip = () => {
const isEditing = !!editSvc?.isEditing(ctrl);
const errorMessages = !isEditing && getEditErrorsForPosition(beans, ctrl, this.getLocaleTextFunc());

if (errorMessages) {
return true;
}

const isTooltipEnabled = column.isTooltipEnabled();

if (!isTooltipEnabled) {
return false;
}

const isElementOverflowing = _isElementOverflowingCallback(() => {
const eCell = ctrl.eGui;
return eCell.children.length === 0
? eCell
: (eCell.querySelector('.ag-cell-value') as HTMLElement | undefined);
});

return !isEditing && isElementOverflowing();
};
} else {
shouldDisplayTooltip = () => !editSvc?.isEditing(ctrl);
}
}
const getTooltipValue = () => resolveAndStore()?.value;

const tooltipCtrl: ITooltipCtrl = {
getGui: () => ctrl.eGui,
getLocation: () => location,
getTooltipValue: value != null ? () => value : getTooltipValue,
shouldDisplayTooltip,
getLocation: () => resolvedTooltip?.location ?? 'cell',
getTooltipValue,
shouldDisplayTooltip: () => {
const tooltip = resolvedTooltip ?? resolveAndStore();
if (!tooltip) {
return false;
}
return tooltip.shouldDisplay ? tooltip.shouldDisplay() : true;
},
getAdditionalParams: () => ({
column,
colDef: column.getColDef(),
Expand Down Expand Up @@ -258,36 +346,6 @@ export class TooltipService extends BeanStub implements NamedBean {
return ctrl.createBean(tooltipFeature, context);
}

public setupFormulaTooltip(cellCtrl: CellCtrl): TooltipFeature | undefined {
const { beans } = this;
const { context, formula } = beans;

if (!this.beans.formula?.active || !cellCtrl.column.isAllowFormula()) {
return;
}

const tooltipParams: ITooltipCtrl = {
getGui: () => cellCtrl.eGui,
getTooltipValue: () => {
const error = formula?.getFormulaError(cellCtrl.column, cellCtrl.rowNode);
return error ? error.message : undefined;
},
getLocation: () => 'cellFormula',
shouldDisplayTooltip: () => {
const error = formula?.getFormulaError(cellCtrl.column, cellCtrl.rowNode);
return !!error;
},
};

const tooltipFeature = this.createTooltipFeature(tooltipParams, beans);

if (!tooltipFeature) {
return;
}

return cellCtrl.createBean(tooltipFeature, context);
}

public setupCellEditorTooltip(cellCtrl: CellCtrl, editor: ICellEditor) {
const { beans } = this;
const { context } = beans;
Expand Down
Loading
Loading