Initialize tooltips dynamically

This commit is contained in:
Konrad Szwarc 2023-01-31 21:55:15 +01:00
parent 544ad70d87
commit f51c4044b9

View file

@ -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);
});
}