From f51c4044b95b0ea0a95f1b6ed2d35d88451b2985 Mon Sep 17 00:00:00 2001 From: Konrad Szwarc Date: Tue, 31 Jan 2023 21:55:15 +0100 Subject: [PATCH] Initialize tooltips dynamically --- src/web/scripts/initialize-tooltips.ts | 124 +++++++++++++------------ 1 file changed, 64 insertions(+), 60 deletions(-) diff --git a/src/web/scripts/initialize-tooltips.ts b/src/web/scripts/initialize-tooltips.ts index fdac059..4d02274 100644 --- a/src/web/scripts/initialize-tooltips.ts +++ b/src/web/scripts/initialize-tooltips.ts @@ -1,5 +1,4 @@ -import { autoUpdate, computePosition, flip, offset, Placement, shift } from '@floating-ui/dom'; -import { nanoid } from 'nanoid'; +import type { Placement } from '@floating-ui/dom'; interface UpdateTooltipOptions { element: HTMLElement; @@ -7,67 +6,72 @@ interface UpdateTooltipOptions { placement: Placement; } -const updateTooltip = - ({ element, tooltip, placement }: UpdateTooltipOptions) => - () => { - computePosition(element, tooltip, { placement, middleware: [offset(8), flip(), shift({ padding: 8 })] }).then( - ({ x, y }) => { - Object.assign(tooltip.style, { - left: `${x}px`, - top: `${y}px`, - }); - } - ); - }; +const elementsWithTooltipData = [...document.querySelectorAll('[data-tooltip]')] as HTMLElement[]; -const tooltipClass = - /* tw */ 'absolute top-0 left-0 hidden max-w-sm animate-show rounded-lg bg-gray-700 px-3 py-1 text-white dark:bg-gray-100 dark:text-gray-800 sm:max-w-xs'; +if (elementsWithTooltipData.length > 0) { + Promise.all([import('@floating-ui/dom'), import('nanoid')]).then(([floatingUi, { nanoid }]) => { + const { autoUpdate, computePosition, flip, offset, shift } = floatingUi; -const createTooltip = (content: string) => { - const tooltip = document.createElement('div'); + const updateTooltip = + ({ element, tooltip, placement }: UpdateTooltipOptions) => + () => { + computePosition(element, tooltip, { placement, middleware: [offset(8), flip(), shift({ padding: 8 })] }).then( + ({ x, y }) => { + Object.assign(tooltip.style, { + left: `${x}px`, + top: `${y}px`, + }); + } + ); + }; - tooltip.innerText = content; - tooltip.setAttribute('id', `tooltip-${nanoid(8)}`); - tooltip.setAttribute('class', tooltipClass); - tooltip.setAttribute('role', 'tooltip'); + const tooltipClass = + /* tw */ 'absolute top-0 left-0 hidden max-w-sm animate-show rounded-lg bg-gray-700 px-3 py-1 text-white dark:bg-gray-100 dark:text-gray-800 sm:max-w-xs'; - return tooltip; -}; + const createTooltip = (content: string) => { + const tooltip = document.createElement('div'); -const addListeners = (element: HTMLElement, tooltip: HTMLElement, updateFn: () => void) => { - element.addEventListener('mouseenter', () => { - tooltip.style.display = 'block'; - updateFn(); + tooltip.innerText = content; + tooltip.setAttribute('id', `tooltip-${nanoid(8)}`); + tooltip.setAttribute('class', tooltipClass); + tooltip.setAttribute('role', 'tooltip'); + + return tooltip; + }; + + const addListeners = (element: HTMLElement, tooltip: HTMLElement, updateFn: () => void) => { + element.addEventListener('mouseenter', () => { + tooltip.style.display = 'block'; + updateFn(); + }); + + element.addEventListener('mouseleave', () => { + tooltip.style.display = ''; + }); + }; + + const creteTooltipsForElements = (elements: HTMLElement[]) => { + const tooltipsContainer = document.createElement('div'); + + const tooltips = elements.map((element) => { + const tooltip = createTooltip(element.dataset.tooltip ?? ''); + tooltipsContainer.appendChild(tooltip); + return { tooltip, element }; + }); + + document.body.appendChild(tooltipsContainer); + + return tooltips; + }; + + creteTooltipsForElements(elementsWithTooltipData).forEach(({ element, tooltip }) => { + const placement = (element.dataset.tooltipPlacement ?? 'top') as Placement; + const updateFn = updateTooltip({ element, tooltip, placement }); + + element.setAttribute('aria-describedby', tooltip.id); + + autoUpdate(element, tooltip, updateFn); + addListeners(element, tooltip, updateFn); + }); }); - - element.addEventListener('mouseleave', () => { - tooltip.style.display = ''; - }); -}; - -const creteTooltipsForElements = (elements: HTMLElement[]) => { - const tooltipsContainer = document.createElement('div'); - - const tooltips = elements.map((element) => { - const tooltip = createTooltip(element.dataset.tooltip ?? ''); - tooltipsContainer.appendChild(tooltip); - return { tooltip, element }; - }); - - document.body.appendChild(tooltipsContainer); - - return tooltips; -}; - -const elements = [...document.querySelectorAll('[data-tooltip]')] as HTMLElement[]; -const elementsWithTooltips = creteTooltipsForElements(elements); - -elementsWithTooltips.forEach(({ element, tooltip }) => { - const placement = (element.dataset.tooltipPlacement ?? 'top') as Placement; - const updateFn = updateTooltip({ element, tooltip, placement }); - - element.setAttribute('aria-describedby', tooltip.id); - - autoUpdate(element, tooltip, updateFn); - addListeners(element, tooltip, updateFn); -}); +}