165 lines
5.0 KiB
JavaScript
165 lines
5.0 KiB
JavaScript
(function initTooltipOverlayModule() {
|
|
const getViewportWidth = () => {
|
|
if (window.visualViewport && typeof window.visualViewport.width === 'number') {
|
|
return window.visualViewport.width;
|
|
}
|
|
return window.innerWidth;
|
|
};
|
|
|
|
const getPixelValue = (propertyName) => {
|
|
const rootStyles = getComputedStyle(document.documentElement);
|
|
const rawValue = rootStyles.getPropertyValue(propertyName).trim();
|
|
const rootFontSize = parseFloat(rootStyles.fontSize) || 16;
|
|
const value = parseFloat(rawValue);
|
|
|
|
if (Number.isNaN(value)) {
|
|
return 0;
|
|
}
|
|
if (rawValue.endsWith('rem')) {
|
|
return value * rootFontSize;
|
|
}
|
|
return value;
|
|
};
|
|
|
|
const setTooltipState = (wrap, isOpen) => {
|
|
const trigger = wrap.querySelector('[data-component-part="tooltip-trigger"]');
|
|
const panel = wrap.querySelector('[data-component-part="tooltip-panel"]');
|
|
if (!trigger || !panel) {
|
|
return;
|
|
}
|
|
|
|
wrap.dataset.tooltipOpen = String(isOpen);
|
|
panel.hidden = !isOpen;
|
|
trigger.setAttribute('aria-expanded', String(isOpen));
|
|
};
|
|
|
|
const positionTooltip = (wrap) => {
|
|
const trigger = wrap.querySelector('[data-component-part="tooltip-trigger"]');
|
|
const panel = wrap.querySelector('[data-component-part="tooltip-panel"]');
|
|
if (!trigger || !panel || panel.hidden) {
|
|
return;
|
|
}
|
|
|
|
const viewportWidth = getViewportWidth();
|
|
const safeInset = getPixelValue('--spacing-large');
|
|
const triggerRect = trigger.getBoundingClientRect();
|
|
const panelRect = panel.getBoundingClientRect();
|
|
const gap = getPixelValue('--spacing-small');
|
|
|
|
let placement = 'bottom';
|
|
if (triggerRect.right + (panelRect.width / 2) > viewportWidth - safeInset) {
|
|
placement = 'left';
|
|
} else if (triggerRect.left - (panelRect.width / 2) < safeInset) {
|
|
placement = 'right';
|
|
}
|
|
|
|
wrap.dataset.tooltipPlacement = placement;
|
|
|
|
if (placement === 'bottom') {
|
|
const nextPanelRect = panel.getBoundingClientRect();
|
|
if (nextPanelRect.bottom > window.innerHeight - safeInset
|
|
&& triggerRect.top - panelRect.height - gap >= safeInset) {
|
|
wrap.dataset.tooltipPlacement = 'top';
|
|
}
|
|
}
|
|
};
|
|
|
|
const closeTooltip = (wrap) => {
|
|
wrap.dataset.tooltipPinned = 'false';
|
|
wrap.dataset.tooltipHover = 'false';
|
|
wrap.dataset.tooltipFocus = 'false';
|
|
wrap.dataset.tooltipDismissed = 'false';
|
|
setTooltipState(wrap, false);
|
|
};
|
|
|
|
const openTooltip = (wrap) => {
|
|
setTooltipState(wrap, true);
|
|
positionTooltip(wrap);
|
|
};
|
|
|
|
const initTooltip = (wrap) => {
|
|
if (wrap.dataset.tooltipInit === 'true') {
|
|
return;
|
|
}
|
|
wrap.dataset.tooltipInit = 'true';
|
|
wrap.dataset.tooltipPlacement = 'bottom';
|
|
wrap.dataset.tooltipPinned = 'false';
|
|
wrap.dataset.tooltipHover = 'false';
|
|
wrap.dataset.tooltipFocus = 'false';
|
|
wrap.dataset.tooltipDismissed = 'false';
|
|
setTooltipState(wrap, false);
|
|
|
|
const trigger = wrap.querySelector('[data-component-part="tooltip-trigger"]');
|
|
if (!trigger) {
|
|
return;
|
|
}
|
|
|
|
wrap.addEventListener('pointerenter', () => {
|
|
if (wrap.dataset.tooltipDismissed === 'true') {
|
|
return;
|
|
}
|
|
wrap.dataset.tooltipHover = 'true';
|
|
openTooltip(wrap);
|
|
});
|
|
|
|
wrap.addEventListener('pointerleave', () => {
|
|
wrap.dataset.tooltipHover = 'false';
|
|
wrap.dataset.tooltipDismissed = 'false';
|
|
if (wrap.dataset.tooltipPinned !== 'true' && wrap.dataset.tooltipFocus !== 'true') {
|
|
setTooltipState(wrap, false);
|
|
}
|
|
});
|
|
|
|
wrap.addEventListener('focusin', () => {
|
|
if (wrap.dataset.tooltipDismissed === 'true') {
|
|
return;
|
|
}
|
|
wrap.dataset.tooltipFocus = 'true';
|
|
openTooltip(wrap);
|
|
});
|
|
|
|
wrap.addEventListener('focusout', (event) => {
|
|
if (wrap.contains(event.relatedTarget)) {
|
|
return;
|
|
}
|
|
wrap.dataset.tooltipFocus = 'false';
|
|
wrap.dataset.tooltipDismissed = 'false';
|
|
if (wrap.dataset.tooltipPinned !== 'true' && wrap.dataset.tooltipHover !== 'true') {
|
|
setTooltipState(wrap, false);
|
|
}
|
|
});
|
|
|
|
trigger.addEventListener('click', (event) => {
|
|
event.stopPropagation();
|
|
const isPinned = wrap.dataset.tooltipPinned === 'true';
|
|
wrap.dataset.tooltipPinned = String(!isPinned);
|
|
|
|
if (isPinned) {
|
|
wrap.dataset.tooltipDismissed = 'true';
|
|
setTooltipState(wrap, false);
|
|
return;
|
|
}
|
|
|
|
wrap.dataset.tooltipDismissed = 'false';
|
|
openTooltip(wrap);
|
|
});
|
|
};
|
|
|
|
document.querySelectorAll('.sg-tooltip-wrap').forEach(initTooltip);
|
|
|
|
document.addEventListener('click', (event) => {
|
|
document.querySelectorAll('.sg-tooltip-wrap').forEach((wrap) => {
|
|
if (!wrap.contains(event.target)) {
|
|
closeTooltip(wrap);
|
|
}
|
|
});
|
|
});
|
|
|
|
const repositionOpenTooltips = () => {
|
|
document.querySelectorAll('.sg-tooltip-wrap[data-tooltip-open="true"]').forEach(positionTooltip);
|
|
};
|
|
|
|
window.addEventListener('resize', repositionOpenTooltips);
|
|
window.addEventListener('scroll', repositionOpenTooltips, true);
|
|
})();
|