Files
v10/dist/dev/ui/tooltip/tooltip-popup.js

132 lines
4.8 KiB
JavaScript

"use client";
import { useComposedRefs } from "../../utils/use-composed-refs.js";
import { renderElement } from "../../utils/use-render.js";
import { useTooltipContext } from "./context.js";
import { TooltipLabel } from "./tooltip-label.js";
import { TooltipShortcut } from "./tooltip-shortcut.js";
import { getAnchorPositionStyle, getPopupPositionRect, getPositionedSide, getPositioningBoundaryRect, isEventWithinElement, resolveOffsets, resolvePositioningBoundary } from "@videojs/core/dom";
import { forwardRef, useCallback, useLayoutEffect, useMemo, useRef, useState } from "react";
import { supportsAnchorPositioning } from "@videojs/utils/dom";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import { TooltipCSSVars } from "@videojs/core";
//#region src/ui/tooltip/tooltip-popup.tsx
const POPUP_RESET = {
position: "fixed",
inset: "auto",
margin: 0
};
/** Container for the tooltip content. Positioned relative to the trigger using CSS anchor positioning with a JavaScript fallback. */
const TooltipPopup = forwardRef(function TooltipPopup({ render, className, style, children, ...elementProps }, forwardedRef) {
const { core, tooltip, state, preferredSide, setPositionedSide, stateAttrMap, anchorName, popupId, boundary, container } = useTooltipContext();
const internalRef = useRef(null);
const composedRef = useComposedRefs(forwardedRef, useCallback((el) => {
tooltip.setPopupElement(el);
if (el && supportsAnchorPositioning()) el.style.setProperty("position-anchor", `--${anchorName}`);
}, [tooltip, anchorName]), internalRef);
const posOpts = useMemo(() => ({
side: preferredSide,
align: state.align
}), [preferredSide, state.align]);
const anchorStyle = useMemo(() => {
if (!supportsAnchorPositioning()) return null;
const { positionAnchor: _, ...rest } = getAnchorPositionStyle(anchorName, posOpts, void 0, void 0, void 0, void 0, TooltipCSSVars);
return rest;
}, [anchorName, posOpts]);
const [position, setPosition] = useState(null);
useLayoutEffect(() => {
if (!state.open) {
setPosition(null);
return;
}
function measure() {
const triggerEl = tooltip.triggerElement;
const popupEl = internalRef.current;
if (!triggerEl || !popupEl) return;
const triggerRect = triggerEl.getBoundingClientRect();
const root = popupEl.getRootNode();
const boundaryElement = resolvePositioningBoundary(boundary, {
container,
root
});
const popupRect = getPopupPositionRect(popupEl, posOpts.side);
const boundaryRect = getPositioningBoundaryRect(boundaryElement);
const offsets = resolveOffsets(popupEl, TooltipCSSVars);
const side = getPositionedSide(triggerRect, popupRect, boundaryRect, posOpts, offsets);
const { positionAnchor: _, ...nextStyle } = getAnchorPositionStyle(anchorName, {
...posOpts,
side
}, triggerRect, supportsAnchorPositioning() ? void 0 : popupRect, boundaryRect, offsets, TooltipCSSVars);
setPosition(nextStyle);
setPositionedSide(side);
}
measure();
const triggerEl = tooltip.triggerElement;
const popupEl = internalRef.current;
const boundaryElement = popupEl ? resolvePositioningBoundary(boundary, {
container,
root: popupEl.getRootNode()
}) : null;
let rafId = 0;
function reposition(event) {
if (event && isEventWithinElement(event, internalRef.current)) return;
cancelAnimationFrame(rafId);
rafId = requestAnimationFrame(measure);
}
reposition();
const resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(() => {
reposition();
}) : null;
if (triggerEl && resizeObserver) resizeObserver.observe(triggerEl);
if (popupEl && resizeObserver) resizeObserver.observe(popupEl);
if (boundaryElement && resizeObserver) resizeObserver.observe(boundaryElement);
window.addEventListener("scroll", reposition, {
capture: true,
passive: true
});
window.addEventListener("resize", reposition);
return () => {
cancelAnimationFrame(rafId);
resizeObserver?.disconnect();
window.removeEventListener("scroll", reposition, true);
window.removeEventListener("resize", reposition);
};
}, [
state.open,
anchorName,
posOpts,
tooltip,
boundary,
container,
setPositionedSide
]);
const positioningStyle = position ?? anchorStyle ?? POPUP_RESET;
if (!state.open) return null;
const body = children !== void 0 ? children : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(TooltipLabel, {}), /* @__PURE__ */ jsx(TooltipShortcut, {})] });
const { onFocusOut, ...restPopupProps } = tooltip.popupProps;
return renderElement("div", {
render,
className,
style
}, {
state,
stateAttrMap,
ref: composedRef,
props: [
{
id: popupId,
style: positioningStyle,
...core.getPopupAttrs(state)
},
{ children: body },
{
...restPopupProps,
onBlur: onFocusOut
},
elementProps
]
});
});
//#endregion
export { TooltipPopup };
//# sourceMappingURL=tooltip-popup.js.map