mirror of
https://github.com/zoriya/v10.git
synced 2026-08-08 23:27:58 +00:00
132 lines
4.8 KiB
JavaScript
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
|