fix(sw): focus anchored SWPopup / property panel when it opens

With anchorRef the frame renders visibility:hidden until useSWHeaderOverlay
places it, and the focus effect ran in that same commit, so focus() on the
hidden element was ignored. Focus now runs once the frame is placed; the
previous element is still captured first, so Escape / close return focus.
Align the SWPopup autoFocus default doc (true with anchorRef) and catalog.

(cherry picked from commit 65bc0dbd5df059ead06f10a3f95f2bd2734fbf2f)
This commit is contained in:
shkim4 committed 2026-10-19 17:42:37 +09:00
1 parent b7227ee963
commit b434f272e2
3 files changed
+7 -3

No files matched your search

@@ -327,13 +327,17 @@ export function SWPopupFrame({
if (!focus) return
const panel = panelRef.current
const previous = document.activeElement as HTMLElement | null
panel?.focus()
return () => {
// Closing with focus still inside (e.g. Escape on a field) returns it to where it came from.
const active = document.activeElement
if (panel && (active === document.body || active === null || panel.contains(active)) && previous?.isConnected) previous.focus()
}
}, [focus, panelRef])
// An anchored frame stays hidden until it is placed, and a hidden element cannot take focus.
const placed = style.visibility !== "hidden"
useLayoutEffect(() => {
if (focus && placed) panelRef.current?.focus()
}, [focus, placed, panelRef])
return (
<div ref={panelRef} id={id} tabIndex={-1} className={`hm-sw-property${className ? ` ${className}` : ""}`} data-selection-kind={kind} role="dialog" aria-modal="false" aria-labelledby={titleId}
style={{ ...style, ...customStyle }} {...rest}
@@ -13,7 +13,7 @@ export interface SWPopupProps extends Omit<HTMLAttributes<HTMLDivElement>, "titl
* the project's own elements. Nothing is restricted; the frame keeps the SW design whatever is inside.
*/
children?: ReactNode
/** Focus the popup when it opens (and return focus when it closes from inside). Default false. */
/** Focus the popup when it opens (and return focus when it closes from inside). Default: true with `anchorRef`, false without. */
autoFocus?: boolean
/** Anchor element: the popup is fixed below it and kept inside the viewport (as SWHeader does). */
anchorRef?: RefObject<HTMLElement | null>