summaryrefslogtreecommitdiffstatshomepage
path: root/core/modules/navigation/js/tooltip.js
blob: a92e045b1f813362774768e4a7068e727812cbc5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/* cspell:ignore uidom */
/**
 * @file
 *
 * Simple tooltip component.
 *
 * To use it just add:
 *
 * data-drupal-tooltip="title" - Text displayed in tooltip.
 *
 * data-drupal-tooltip-class="extra-class" - Optional class for css.
 *
 * data-drupal-tooltip-position="top" - Tooltip position (default right).
 *
 * @see https://floating-ui.com/ for available placement options.
 */

((Drupal, once, { computePosition, offset, shift, flip }) => {
  /**
   * Theme function for a tooltip.
   *
   * @param {object} dataset
   *   The dataset object.
   * @param {string} dataset.drupalTooltipClass
   *   Extra class for theming.
   * @param {string} dataset.drupalTooltip
   *   The text for tooltip.
   *
   * @return {HTMLElement}
   *   A DOM Node.
   */
  Drupal.theme.tooltipWrapper = (dataset) =>
    `<div class="toolbar-tooltip ${dataset.drupalTooltipClass || ''}">
      ${dataset.drupalTooltip}
    </div>`;

  /**
   * Attaches the tooltip behavior to all required triggers.
   *
   * @type {Drupal~behavior}
   *
   * @prop {Drupal~behaviorAttach} attach
   *   Attaches the tooltip behavior.
   */
  Drupal.behaviors.tooltipInit = {
    attach: (context) => {
      once('tooltip-trigger', '[data-drupal-tooltip]', context).forEach(
        (trigger) => {
          trigger.insertAdjacentHTML(
            'afterend',
            Drupal.theme.tooltipWrapper(trigger.dataset),
          );
          const tooltip = trigger.nextElementSibling;

          const updatePosition = () => {
            computePosition(trigger, tooltip, {
              strategy: 'fixed',
              placement: trigger.dataset.drupalTooltipPosition || 'right',
              middleware: [
                flip({ padding: 16 }),
                offset(6),
                shift({ padding: 16 }),
              ],
            }).then(({ x, y }) => {
              Object.assign(tooltip.style, {
                left: `${x}px`,
                top: `${y}px`,
              });
            });
          };

          // Small trick to avoid tooltip stays on same place when button size changed.
          const ro = new ResizeObserver(updatePosition);

          ro.observe(trigger);

          trigger.addEventListener('mouseover', updatePosition);
          trigger.addEventListener('focus', updatePosition);
        },
      );
    },
  };
})(Drupal, once, FloatingUIDOM);