️(frontend) add focus restore store

Create Zustand store to track last focused element
and restore focus after overlay close. DropdownMenu
auto-stores trigger ref when an option is clicked.
This commit is contained in:
Cyril
2026-03-06 10:44:27 +01:00
parent fb4c502c75
commit 9536227c52
3 changed files with 31 additions and 2 deletions
@@ -9,6 +9,7 @@ import { Button, Popover } from 'react-aria-components';
import styled, { css } from 'styled-components';
import { useCunninghamTheme } from '@/cunningham';
import { useFocusStore } from '@/stores';
import { BoxProps } from './Box';
@@ -70,8 +71,9 @@ export const DropButton = ({
const { themeTokens } = useCunninghamTheme();
const font = themeTokens['font']?.['families']['base'];
const [isLocalOpen, setIsLocalOpen] = useState(isOpen);
const addLastFocus = useFocusStore((state) => state.addLastFocus);
const triggerRef = useRef(null);
const triggerRef = useRef<HTMLButtonElement | null>(null);
useEffect(() => {
setIsLocalOpen(isOpen);
@@ -86,7 +88,10 @@ export const DropButton = ({
<>
<StyledButton
ref={triggerRef}
onPress={() => onOpenChangeHandler(true)}
onPress={() => {
addLastFocus(triggerRef.current);
onOpenChangeHandler(true);
}}
aria-label={label}
data-testid={testId}
$css={css`
@@ -1,2 +1,3 @@
export * from './useBroadcastStore';
export * from './useFocusStore';
export * from './useResponsiveStore';
@@ -0,0 +1,23 @@
import { create } from 'zustand';
interface UseFocusStore {
lastFocusedElement: HTMLElement | null;
addLastFocus: (target: HTMLElement | null) => void;
restoreFocus: () => void;
}
export const useFocusStore = create<UseFocusStore>((set, get) => ({
lastFocusedElement: null,
addLastFocus: (target) => set({ lastFocusedElement: target }),
restoreFocus: () => {
const { lastFocusedElement } = get();
if (!lastFocusedElement) {
return;
}
requestAnimationFrame(() => {
lastFocusedElement.focus();
});
set({ lastFocusedElement: null });
},
}));