Initialize tooltips dynamically
This commit is contained in:
parent
544ad70d87
commit
f51c4044b9
1 changed files with 64 additions and 60 deletions
|
|
@ -1,5 +1,4 @@
|
||||||
import { autoUpdate, computePosition, flip, offset, Placement, shift } from '@floating-ui/dom';
|
import type { Placement } from '@floating-ui/dom';
|
||||||
import { nanoid } from 'nanoid';
|
|
||||||
|
|
||||||
interface UpdateTooltipOptions {
|
interface UpdateTooltipOptions {
|
||||||
element: HTMLElement;
|
element: HTMLElement;
|
||||||
|
|
@ -7,67 +6,72 @@ interface UpdateTooltipOptions {
|
||||||
placement: Placement;
|
placement: Placement;
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateTooltip =
|
const elementsWithTooltipData = [...document.querySelectorAll('[data-tooltip]')] as HTMLElement[];
|
||||||
({ 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 tooltipClass =
|
if (elementsWithTooltipData.length > 0) {
|
||||||
/* 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';
|
Promise.all([import('@floating-ui/dom'), import('nanoid')]).then(([floatingUi, { nanoid }]) => {
|
||||||
|
const { autoUpdate, computePosition, flip, offset, shift } = floatingUi;
|
||||||
|
|
||||||
const createTooltip = (content: string) => {
|
const updateTooltip =
|
||||||
const tooltip = document.createElement('div');
|
({ 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;
|
const tooltipClass =
|
||||||
tooltip.setAttribute('id', `tooltip-${nanoid(8)}`);
|
/* 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';
|
||||||
tooltip.setAttribute('class', tooltipClass);
|
|
||||||
tooltip.setAttribute('role', 'tooltip');
|
|
||||||
|
|
||||||
return tooltip;
|
const createTooltip = (content: string) => {
|
||||||
};
|
const tooltip = document.createElement('div');
|
||||||
|
|
||||||
const addListeners = (element: HTMLElement, tooltip: HTMLElement, updateFn: () => void) => {
|
tooltip.innerText = content;
|
||||||
element.addEventListener('mouseenter', () => {
|
tooltip.setAttribute('id', `tooltip-${nanoid(8)}`);
|
||||||
tooltip.style.display = 'block';
|
tooltip.setAttribute('class', tooltipClass);
|
||||||
updateFn();
|
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);
|
|
||||||
});
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue