@slickgrid-universal/custom-tooltip-plugin 4.6.0 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,13 +29,20 @@ export declare class SlickCustomTooltip {
29
29
  name: 'CustomTooltip';
30
30
  protected _addonOptions?: CustomTooltipOption;
31
31
  protected _cellAddonOptions?: CustomTooltipOption;
32
- protected _cellNodeElm?: HTMLDivElement;
32
+ protected _cellNodeElm?: HTMLElement;
33
33
  protected _cellType: CellType;
34
34
  protected _cancellablePromise?: CancellablePromiseWrapper;
35
35
  protected _observable$?: Subscription;
36
36
  protected _rxjs?: RxJsFacade | null;
37
37
  protected _sharedService?: SharedService | null;
38
+ protected _tooltipBodyElm?: HTMLDivElement;
38
39
  protected _tooltipElm?: HTMLDivElement;
40
+ protected _mousePosition: {
41
+ x: number;
42
+ y: number;
43
+ };
44
+ protected _mouseTarget?: HTMLElement | null;
45
+ protected _hasMultipleTooltips: boolean;
39
46
  protected _defaultOptions: CustomTooltipOption<any>;
40
47
  protected _grid: SlickGrid;
41
48
  protected _eventHandler: SlickEventHandler;
@@ -43,6 +50,7 @@ export declare class SlickCustomTooltip {
43
50
  get addonOptions(): CustomTooltipOption | undefined;
44
51
  get cancellablePromise(): CancellablePromiseWrapper<any> | undefined;
45
52
  get cellAddonOptions(): CustomTooltipOption | undefined;
53
+ get bodyClassName(): string;
46
54
  get className(): string;
47
55
  get dataView(): CustomDataView;
48
56
  /** Getter for the Grid Options pulled through the Grid Object */
@@ -70,8 +78,8 @@ export declare class SlickCustomTooltip {
70
78
  cell: number;
71
79
  }, value: any, columnDef: Column, dataContext: any): void;
72
80
  /** depending on the selector type, execute the necessary handler code */
73
- protected handleOnHeaderMouseEnterByType(event: SlickEventData, args: any, selector: CellType): void;
74
- protected handleOnMouseEnter(event: SlickEventData): Promise<void>;
81
+ protected handleOnHeaderMouseOverByType(event: SlickEventData, args: any, selector: CellType): void;
82
+ protected handleOnMouseOver(event: SlickEventData): Promise<void>;
75
83
  /**
76
84
  * Parse the Custom Formatter (when provided) or return directly the text when it is already a string.
77
85
  * We will also sanitize the text in both cases before returning it so that it can be used safely.
@@ -1 +1 @@
1
- {"version":3,"file":"slickCustomTooltip.d.ts","sourceRoot":"","sources":["../../src/slickCustomTooltip.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"slickCustomTooltip.d.ts","sourceRoot":"","sources":["../../src/slickCustomTooltip.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slickgrid-universal/custom-tooltip-plugin",
3
- "version": "4.6.0",
3
+ "version": "4.7.0",
4
4
  "description": "A plugin to add Custom Tooltip when hovering a cell, it subscribes to the cell",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -8,9 +8,9 @@
8
8
  "exports": {
9
9
  ".": {
10
10
  "types": "./dist/types/index.d.ts",
11
+ "node": "./dist/cjs/index.js",
11
12
  "import": "./dist/esm/index.js",
12
- "require": "./dist/cjs/index.js",
13
- "default": "./dist/esm/index.js"
13
+ "require": "./dist/cjs/index.js"
14
14
  },
15
15
  "./package.json": "./package.json"
16
16
  },
@@ -38,9 +38,9 @@
38
38
  "not dead"
39
39
  ],
40
40
  "dependencies": {
41
- "@slickgrid-universal/common": "~4.6.0",
42
- "@slickgrid-universal/utils": "~4.6.0",
43
- "isomorphic-dompurify": "^2.6.0"
41
+ "@slickgrid-universal/common": "~4.7.0",
42
+ "@slickgrid-universal/utils": "~4.7.0",
43
+ "isomorphic-dompurify": "^2.7.0"
44
44
  },
45
- "gitHead": "e00a993f4f2e48eb28eee6e0985368a7b4374cbc"
45
+ "gitHead": "185b6f9e44400bec2f1d79568905ca79e4b338a5"
46
46
  }
@@ -29,6 +29,9 @@ import { isPrimitiveOrHTML } from '@slickgrid-universal/utils';
29
29
 
30
30
  type CellType = 'slick-cell' | 'slick-header-column' | 'slick-headerrow-column';
31
31
 
32
+ const CLOSEST_TOOLTIP_FILLED_ATTR = ['title', 'data-slick-tooltip'];
33
+ const SELECTOR_CLOSEST_TOOLTIP_ATTR = '[title], [data-slick-tooltip]';
34
+
32
35
  /**
33
36
  * A plugin to add Custom Tooltip when hovering a cell, it subscribes to the cell "onMouseEnter" and "onMouseLeave" events.
34
37
  * The "customTooltip" is defined in the Column Definition OR Grid Options (the first found will have priority over the second)
@@ -58,15 +61,21 @@ export class SlickCustomTooltip {
58
61
 
59
62
  protected _addonOptions?: CustomTooltipOption;
60
63
  protected _cellAddonOptions?: CustomTooltipOption;
61
- protected _cellNodeElm?: HTMLDivElement;
64
+ protected _cellNodeElm?: HTMLElement;
62
65
  protected _cellType: CellType = 'slick-cell';
63
66
  protected _cancellablePromise?: CancellablePromiseWrapper;
64
67
  protected _observable$?: Subscription;
65
68
  protected _rxjs?: RxJsFacade | null = null;
66
69
  protected _sharedService?: SharedService | null = null;
70
+ protected _tooltipBodyElm?: HTMLDivElement;
67
71
  protected _tooltipElm?: HTMLDivElement;
72
+ protected _mousePosition: { x: number; y: number; } = { x: 0, y: 0 };
73
+ protected _mouseTarget?: HTMLElement | null;
74
+ protected _hasMultipleTooltips = false;
68
75
  protected _defaultOptions = {
76
+ bodyClassName: 'tooltip-body',
69
77
  className: 'slick-custom-tooltip',
78
+ offsetArrow: 3, // same as `$slick-tooltip-arrow-side-margin` CSS/SASS variable
70
79
  offsetLeft: 0,
71
80
  offsetRight: 0,
72
81
  offsetTopBottom: 4,
@@ -93,6 +102,9 @@ export class SlickCustomTooltip {
93
102
  return this._cellAddonOptions;
94
103
  }
95
104
 
105
+ get bodyClassName(): string {
106
+ return this._cellAddonOptions?.bodyClassName ?? 'tooltip-body';
107
+ }
96
108
  get className(): string {
97
109
  return this._cellAddonOptions?.className ?? 'slick-custom-tooltip';
98
110
  }
@@ -127,12 +139,12 @@ export class SlickCustomTooltip {
127
139
  this._sharedService = containerService.get<SharedService>('SharedService');
128
140
  this._addonOptions = { ...this._defaultOptions, ...(this._sharedService?.gridOptions?.customTooltip) } as CustomTooltipOption;
129
141
  this._eventHandler
130
- .subscribe(grid.onMouseEnter, this.handleOnMouseEnter.bind(this))
131
- .subscribe(grid.onHeaderMouseEnter, (e, args) => this.handleOnHeaderMouseEnterByType(e, args, 'slick-header-column'))
132
- .subscribe(grid.onHeaderRowMouseEnter, (e, args) => this.handleOnHeaderMouseEnterByType(e, args, 'slick-headerrow-column'))
142
+ .subscribe(grid.onMouseEnter, this.handleOnMouseOver.bind(this))
143
+ .subscribe(grid.onHeaderMouseOver, (e, args) => this.handleOnHeaderMouseOverByType(e, args, 'slick-header-column'))
144
+ .subscribe(grid.onHeaderRowMouseOver, (e, args) => this.handleOnHeaderMouseOverByType(e, args, 'slick-headerrow-column'))
133
145
  .subscribe(grid.onMouseLeave, this.hideTooltip.bind(this))
134
- .subscribe(grid.onHeaderMouseLeave, this.hideTooltip.bind(this))
135
- .subscribe(grid.onHeaderRowMouseLeave, this.hideTooltip.bind(this));
146
+ .subscribe(grid.onHeaderMouseOut, this.hideTooltip.bind(this))
147
+ .subscribe(grid.onHeaderRowMouseOut, this.hideTooltip.bind(this));
136
148
  }
137
149
 
138
150
  dispose() {
@@ -180,8 +192,10 @@ export class SlickCustomTooltip {
180
192
  }
181
193
 
182
194
  /** depending on the selector type, execute the necessary handler code */
183
- protected handleOnHeaderMouseEnterByType(event: SlickEventData, args: any, selector: CellType) {
195
+ protected handleOnHeaderMouseOverByType(event: SlickEventData, args: any, selector: CellType) {
184
196
  this._cellType = selector;
197
+ this._mousePosition = { x: event.clientX || 0, y: event.clientY || 0 };
198
+ this._mouseTarget = document.elementFromPoint(event.clientX || 0, event.clientY || 0)?.closest(SELECTOR_CLOSEST_TOOLTIP_ATTR);
185
199
 
186
200
  // before doing anything, let's remove any previous tooltip before
187
201
  // and cancel any opened Promise/Observable when using async
@@ -221,8 +235,10 @@ export class SlickCustomTooltip {
221
235
  }
222
236
  }
223
237
 
224
- protected async handleOnMouseEnter(event: SlickEventData) {
238
+ protected async handleOnMouseOver(event: SlickEventData) {
225
239
  this._cellType = 'slick-cell';
240
+ this._mousePosition = { x: event.clientX || 0, y: event.clientY || 0 };
241
+ this._mouseTarget = document.elementFromPoint(event.clientX || 0, event.clientY || 0)?.closest(SELECTOR_CLOSEST_TOOLTIP_ATTR);
226
242
 
227
243
  // before doing anything, let's remove any previous tooltip before
228
244
  // and cancel any opened Promise/Observable when using async
@@ -318,34 +334,44 @@ export class SlickCustomTooltip {
318
334
  protected renderRegularTooltip(formatterOrText: Formatter | string | undefined, cell: { row: number; cell: number; }, value: any, columnDef: Column, item: any) {
319
335
  const tmpDiv = document.createElement('div');
320
336
  this._grid.applyHtmlCode(tmpDiv, this.parseFormatterAndSanitize(formatterOrText, cell, value, columnDef, item));
337
+ this._hasMultipleTooltips = (this._cellNodeElm?.querySelectorAll(SELECTOR_CLOSEST_TOOLTIP_ATTR).length || 0) > 1;
321
338
 
322
- let tooltipText = columnDef?.toolTip ?? '';
323
- let tmpTitleElm: HTMLDivElement | null | undefined;
339
+ let tmpTitleElm: HTMLElement | null | undefined;
340
+ const cellElm = (this._cellAddonOptions?.useRegularTooltipFromCellTextOnly || !this._mouseTarget)
341
+ ? this._cellNodeElm as HTMLElement
342
+ : this._mouseTarget;
324
343
 
344
+ let tooltipText = columnDef?.toolTip ?? '';
325
345
  if (!tooltipText) {
326
- if (this._cellType === 'slick-cell' && this._cellNodeElm && (this._cellNodeElm.clientWidth < this._cellNodeElm.scrollWidth) && !this._cellAddonOptions?.useRegularTooltipFromFormatterOnly) {
327
- tooltipText = this._cellNodeElm.textContent?.trim() ?? '';
346
+ if (this._cellType === 'slick-cell' && cellElm && (cellElm.clientWidth < cellElm.scrollWidth) && !this._cellAddonOptions?.useRegularTooltipFromFormatterOnly) {
347
+ tooltipText = cellElm.textContent?.trim() ?? '';
328
348
  if (this._cellAddonOptions?.tooltipTextMaxLength && tooltipText.length > this._cellAddonOptions?.tooltipTextMaxLength) {
329
349
  tooltipText = tooltipText.substring(0, this._cellAddonOptions.tooltipTextMaxLength - 3) + '...';
330
350
  }
331
- tmpTitleElm = this._cellNodeElm;
351
+ tmpTitleElm = cellElm;
332
352
  } else {
333
353
  if (this._cellAddonOptions?.useRegularTooltipFromFormatterOnly) {
334
- tmpTitleElm = tmpDiv.querySelector<HTMLDivElement>('[title], [data-slick-tooltip]');
354
+ tmpTitleElm = tmpDiv.querySelector<HTMLDivElement>(SELECTOR_CLOSEST_TOOLTIP_ATTR);
335
355
  } else {
336
- tmpTitleElm = findFirstAttribute(this._cellNodeElm, ['title', 'data-slick-tooltip']) ? this._cellNodeElm : tmpDiv.querySelector<HTMLDivElement>('[title], [data-slick-tooltip]');
337
- if ((!tmpTitleElm || !findFirstAttribute(tmpTitleElm, ['title', 'data-slick-tooltip'])) && this._cellNodeElm) {
338
- tmpTitleElm = this._cellNodeElm.querySelector<HTMLDivElement>('[title], [data-slick-tooltip]');
356
+ tmpTitleElm = findFirstAttribute(cellElm, CLOSEST_TOOLTIP_FILLED_ATTR)
357
+ ? cellElm
358
+ : tmpDiv.querySelector<HTMLDivElement>(SELECTOR_CLOSEST_TOOLTIP_ATTR);
359
+
360
+ if ((!tmpTitleElm || !findFirstAttribute(tmpTitleElm, CLOSEST_TOOLTIP_FILLED_ATTR)) && cellElm) {
361
+ tmpTitleElm = cellElm.querySelector<HTMLDivElement>(SELECTOR_CLOSEST_TOOLTIP_ATTR);
339
362
  }
340
363
  }
341
- if (!tooltipText || (typeof formatterOrText === 'function' && this._cellAddonOptions?.useRegularTooltipFromFormatterOnly)) {
342
- tooltipText = findFirstAttribute(tmpTitleElm, ['title', 'data-slick-tooltip']) || '';
364
+
365
+ if (tmpTitleElm?.style.display === 'none' || (this._hasMultipleTooltips && (!cellElm || cellElm === this._cellNodeElm))) {
366
+ tooltipText = '';
367
+ } else if (!tooltipText || (typeof formatterOrText === 'function' && this._cellAddonOptions?.useRegularTooltipFromFormatterOnly)) {
368
+ tooltipText = findFirstAttribute(tmpTitleElm, CLOSEST_TOOLTIP_FILLED_ATTR) || '';
343
369
  }
344
370
  }
345
371
  }
346
372
 
347
373
  if (tooltipText !== '') {
348
- this.renderTooltipFormatter(formatterOrText, cell, value, columnDef, item, tooltipText);
374
+ this.renderTooltipFormatter(formatterOrText, cell, value, columnDef, item, tooltipText, tmpTitleElm);
349
375
  }
350
376
 
351
377
  // also clear any "title" attribute to avoid showing a 2nd browser tooltip
@@ -355,16 +381,18 @@ export class SlickCustomTooltip {
355
381
  protected renderTooltipFormatter(formatter: Formatter | string | undefined, cell: { row: number; cell: number; }, value: any, columnDef: Column, item: unknown, tooltipText?: string, inputTitleElm?: Element | null) {
356
382
  // create the tooltip DOM element with the text returned by the Formatter
357
383
  this._tooltipElm = createDomElement('div', { className: this.className });
384
+ this._tooltipBodyElm = createDomElement('div', { className: this.bodyClassName });
358
385
  this._tooltipElm.classList.add(this.gridUid);
359
386
  this._tooltipElm.classList.add('l' + cell.cell);
360
387
  this._tooltipElm.classList.add('r' + cell.cell);
388
+ this.tooltipElm?.appendChild(this._tooltipBodyElm);
361
389
 
362
390
  // when cell is currently lock for editing, we'll force a tooltip title search
363
391
  // that can happen when user has a formatter but is currently editing and in that case we want the new value
364
- // ie: when user is currently editing and uses the Slider, when dragging its value is changing, so we wish to use the editing value instead of the previous cell value.
392
+ // e.g.: when user is currently editing and uses the Slider, when dragging its value is changing, so we wish to use the editing value instead of the previous cell value.
365
393
  if (value === null || value === undefined) {
366
- const tmpTitleElm = this._cellNodeElm?.querySelector<HTMLDivElement>('[title], [data-slick-tooltip]');
367
- value = findFirstAttribute(tmpTitleElm, ['title', 'data-slick-tooltip']) || value;
394
+ const tmpTitleElm = this._cellNodeElm?.querySelector<HTMLDivElement>(SELECTOR_CLOSEST_TOOLTIP_ATTR);
395
+ value = findFirstAttribute(tmpTitleElm, CLOSEST_TOOLTIP_FILLED_ATTR) || value;
368
396
  }
369
397
 
370
398
  let outputText = tooltipText || this.parseFormatterAndSanitize(formatter, cell, value, columnDef, item) || '';
@@ -373,12 +401,12 @@ export class SlickCustomTooltip {
373
401
  let finalOutputText = '';
374
402
  if (!tooltipText || this._cellAddonOptions?.renderRegularTooltipAsHtml) {
375
403
  finalOutputText = sanitizeTextByAvailableSanitizer(this.gridOptions, outputText);
376
- this._grid.applyHtmlCode(this._tooltipElm, finalOutputText);
377
- this._tooltipElm.style.whiteSpace = this._cellAddonOptions?.whiteSpace ?? this._defaultOptions.whiteSpace as string;
404
+ this._grid.applyHtmlCode(this._tooltipBodyElm, finalOutputText);
405
+ this._tooltipBodyElm.style.whiteSpace = this._cellAddonOptions?.whiteSpace ?? this._defaultOptions.whiteSpace as string;
378
406
  } else {
379
407
  finalOutputText = outputText || '';
380
- this._tooltipElm.textContent = finalOutputText;
381
- this._tooltipElm.style.whiteSpace = this._cellAddonOptions?.regularTooltipWhiteSpace ?? this._defaultOptions.regularTooltipWhiteSpace as string; // use `pre` so that sequences of white space are collapsed. Lines are broken at newline characters
408
+ this._tooltipBodyElm.textContent = finalOutputText;
409
+ this._tooltipBodyElm.style.whiteSpace = this._cellAddonOptions?.regularTooltipWhiteSpace ?? this._defaultOptions.regularTooltipWhiteSpace as string; // use `pre` so that sequences of white space are collapsed. Lines are broken at newline characters
382
410
  }
383
411
 
384
412
  // optional max height/width of the tooltip container
@@ -430,20 +458,20 @@ export class SlickCustomTooltip {
430
458
  // or when using "auto" and we detect not enough available space then we'll position to the "left" of the cell
431
459
  // NOTE the class name is for the arrow and is inverse compare to the tooltip itself, so if user ask for "left-align", then the arrow will in fact be "arrow-right-align"
432
460
  const position = this._cellAddonOptions?.position ?? 'auto';
461
+ let finalTooltipPosition = '';
433
462
  if (position === 'center') {
434
463
  newPositionLeft += (cellContainerWidth / 2) - (calculatedTooltipWidth / 2) + (this._cellAddonOptions?.offsetRight ?? 0);
435
- this._tooltipElm.classList.remove('arrow-left-align');
436
- this._tooltipElm.classList.remove('arrow-right-align');
464
+ finalTooltipPosition = 'top-center';
465
+ this._tooltipElm.classList.remove('arrow-left-align', 'arrow-right-align');
437
466
  this._tooltipElm.classList.add('arrow-center-align');
438
-
439
467
  } else if (position === 'right-align' || ((position === 'auto' || position !== 'left-align') && (newPositionLeft + calculatedTooltipWidth) > calculatedBodyWidth)) {
468
+ finalTooltipPosition = 'right';
440
469
  newPositionLeft -= (calculatedTooltipWidth - cellContainerWidth - (this._cellAddonOptions?.offsetLeft ?? 0));
441
- this._tooltipElm.classList.remove('arrow-center-align');
442
- this._tooltipElm.classList.remove('arrow-left-align');
470
+ this._tooltipElm.classList.remove('arrow-center-align', 'arrow-left-align');
443
471
  this._tooltipElm.classList.add('arrow-right-align');
444
472
  } else {
445
- this._tooltipElm.classList.remove('arrow-center-align');
446
- this._tooltipElm.classList.remove('arrow-right-align');
473
+ finalTooltipPosition = 'left';
474
+ this._tooltipElm.classList.remove('arrow-center-align', 'arrow-right-align');
447
475
  this._tooltipElm.classList.add('arrow-left-align');
448
476
  }
449
477
 
@@ -451,11 +479,23 @@ export class SlickCustomTooltip {
451
479
  // NOTE the class name is for the arrow and is inverse compare to the tooltip itself, so if user ask for "bottom", then the arrow will in fact be "arrow-top"
452
480
  if (position === 'bottom' || ((position === 'auto' || position !== 'top') && calculatedTooltipHeight > calculateAvailableSpace(this._cellNodeElm).top)) {
453
481
  newPositionTop = (cellPosition.top || 0) + (this.gridOptions.rowHeight ?? 0) + (this._cellAddonOptions?.offsetTopBottom ?? 0);
482
+ finalTooltipPosition = `bottom-${finalTooltipPosition}`;
454
483
  this._tooltipElm.classList.remove('arrow-down');
455
484
  this._tooltipElm.classList.add('arrow-up');
456
485
  } else {
457
- this._tooltipElm.classList.add('arrow-down');
486
+ finalTooltipPosition = `top-${finalTooltipPosition}`;
458
487
  this._tooltipElm.classList.remove('arrow-up');
488
+ this._tooltipElm.classList.add('arrow-down');
489
+ }
490
+
491
+ // when having multiple tooltips, we'll try to reposition tooltip to mouse position
492
+ if (this._tooltipElm && (this._hasMultipleTooltips || this.cellAddonOptions?.repositionByMouseOverTarget)) {
493
+ const mouseElmOffset = getOffset(this._mouseTarget)!;
494
+ if (finalTooltipPosition.includes('left') || finalTooltipPosition === 'top-center') {
495
+ newPositionLeft = mouseElmOffset.left - (this._addonOptions?.offsetArrow ?? 3);
496
+ } else if (finalTooltipPosition.includes('right')) {
497
+ newPositionLeft = mouseElmOffset.left - calculatedTooltipWidth + (this._mouseTarget?.offsetWidth ?? 0) + (this._addonOptions?.offsetArrow ?? 3);
498
+ }
459
499
  }
460
500
 
461
501
  // reposition the tooltip over the cell (90% of the time this will end up using a position on the "right" of the cell)
@@ -471,6 +511,10 @@ export class SlickCustomTooltip {
471
511
  protected swapAndClearTitleAttribute(inputTitleElm?: Element | null, tooltipText?: string) {
472
512
  // the title attribute might be directly on the slick-cell container element (when formatter returns a result object)
473
513
  // OR in a child element (most commonly as a custom formatter)
514
+ let cellWithTitleElm: Element | null | undefined;
515
+ if (inputTitleElm) {
516
+ cellWithTitleElm = (this._cellNodeElm && ((this._cellNodeElm.hasAttribute('title') && this._cellNodeElm.getAttribute('title')) ? this._cellNodeElm : this._cellNodeElm?.querySelector('[title]')));
517
+ }
474
518
  const titleElm = inputTitleElm || (this._cellNodeElm && ((this._cellNodeElm.hasAttribute('title') && this._cellNodeElm.getAttribute('title')) ? this._cellNodeElm : this._cellNodeElm?.querySelector('[title]')));
475
519
 
476
520
  // flip tooltip text from `title` to `data-slick-tooltip`
@@ -479,6 +523,11 @@ export class SlickCustomTooltip {
479
523
  if (titleElm.hasAttribute('title')) {
480
524
  titleElm.setAttribute('title', '');
481
525
  }
526
+ // targeted element might actually not be the cell element,
527
+ // so let's also clear the cell element title attribute to avoid showing native + custom tooltips
528
+ if (cellWithTitleElm?.hasAttribute('title')) {
529
+ cellWithTitleElm.setAttribute('title', '');
530
+ }
482
531
  }
483
532
  }
484
533
  }