From 070d0944448fae4fd8ec140a13c5c073c31ccdc4 Mon Sep 17 00:00:00 2001 From: rahim Date: Wed, 4 Mar 2026 13:18:36 -0800 Subject: [PATCH] fix(react): set anchor-name and position-anchor imperatively in popover (#715) --- packages/react/src/ui/popover/popover-popup.tsx | 12 ++++++++---- packages/react/src/ui/popover/popover-trigger.tsx | 12 ++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/react/src/ui/popover/popover-popup.tsx b/packages/react/src/ui/popover/popover-popup.tsx index 5b826ef3..cd614b26 100644 --- a/packages/react/src/ui/popover/popover-popup.tsx +++ b/packages/react/src/ui/popover/popover-popup.tsx @@ -26,8 +26,11 @@ export const PopoverPopup = forwardRef(functi const popupRef = useCallback( (el: HTMLDivElement | null) => { popover.setPopupElement(el); + if (el && supportsAnchorPositioning()) { + el.style.setProperty('position-anchor', `--${anchorName}`); + } }, - [popover] + [popover, anchorName] ); const composedRef = useComposedRefs(forwardedRef, popupRef, internalRef); @@ -37,11 +40,12 @@ export const PopoverPopup = forwardRef(functi const posOpts = useMemo(() => ({ side: state.side, align: state.align }), [state.side, state.align]); // CSS Anchor Positioning — computed from state, no measurement needed. - // getAnchorPositionStyle returns camelCase keys, directly compatible - // with React's style prop. + // `position-anchor` is set imperatively in the ref callback above + // because React's style prop silently drops unrecognised CSS properties. const anchorStyle = useMemo(() => { if (!supportsAnchorPositioning()) return null; - return getAnchorPositionStyle(anchorName, posOpts) as CSSProperties; + const { positionAnchor: _, ...rest } = getAnchorPositionStyle(anchorName, posOpts); + return rest as CSSProperties; }, [anchorName, posOpts]); // Manual fallback — measure rects after layout, before paint. diff --git a/packages/react/src/ui/popover/popover-trigger.tsx b/packages/react/src/ui/popover/popover-trigger.tsx index 95a4d39d..6a405db5 100644 --- a/packages/react/src/ui/popover/popover-trigger.tsx +++ b/packages/react/src/ui/popover/popover-trigger.tsx @@ -1,8 +1,8 @@ 'use client'; import type { PopoverState } from '@videojs/core'; -import { getAnchorNameStyle } from '@videojs/core/dom'; -import { forwardRef, useCallback, useMemo } from 'react'; +import { supportsAnchorPositioning } from '@videojs/utils/dom'; +import { forwardRef, useCallback } from 'react'; import type { UIComponentProps } from '../../utils/types'; import { renderElement } from '../../utils/use-render'; @@ -20,12 +20,13 @@ export const PopoverTrigger = forwardRef const triggerRef = useCallback( (el: HTMLButtonElement | null) => { popover.setTriggerElement(el); + if (el && supportsAnchorPositioning()) { + el.style.setProperty('anchor-name', `--${anchorName}`); + } }, - [popover] + [popover, anchorName] ); - const anchorStyle = useMemo(() => getAnchorNameStyle(anchorName), [anchorName]); - // Remap DOM focus events to React synthetic event names. // createPopover() uses onFocusIn/onFocusOut (matching DOM focusin/focusout), // but React maps those to onFocus/onBlur. @@ -41,7 +42,6 @@ export const PopoverTrigger = forwardRef props: [ { type: 'button' as const, - style: anchorStyle, ...core.getTriggerAttrs(state, popupId), }, { ...restTriggerProps, onFocus: onFocusIn, onBlur: onFocusOut },