diff --git a/src/accordion/AccordionItem.ts b/src/accordion/AccordionItem.ts index 7d8b105ec..02775ab91 100644 --- a/src/accordion/AccordionItem.ts +++ b/src/accordion/AccordionItem.ts @@ -22,9 +22,7 @@ export const useAccordionItem = createHook< compose: unstable_useId, keys: ACCORDION_KEYS, - useProps(options, { ref: htmlRef, ...htmlProps }) { - const { id, registerItem } = options; - + useProps({ id, registerItem }, { ref: htmlRef, ...htmlProps }) { const ref = React.useRef(null); React.useLayoutEffect(() => { diff --git a/src/accordion/AccordionState.ts b/src/accordion/AccordionState.ts index bd4012b04..0e3328c28 100644 --- a/src/accordion/AccordionState.ts +++ b/src/accordion/AccordionState.ts @@ -78,6 +78,40 @@ export function useAccordionState( const idState = unstable_useIdState(sealed); + const { buttons } = state; + const total = buttons.length; + const buttonIds = buttons.map(({ id }) => id); + + const next = React.useCallback( + (id: string) => { + const currentIndex = buttonIds.indexOf(id); + const nextIndex = (currentIndex + 1) % total; + + if (!loop && nextIndex === 0) return; + moveFocus(nextIndex, buttons); + }, + [buttonIds, buttons, loop, total], + ); + + const prev = React.useCallback( + (id: string) => { + const currentIndex = buttonIds.indexOf(id); + const prevIndex = (currentIndex - 1 + total) % total; + + if (!loop && prevIndex === total - 1) return; + moveFocus(prevIndex, buttons); + }, + [buttonIds, buttons, loop, total], + ); + + const first = React.useCallback(() => { + moveFocus(0, buttons); + }, [buttons]); + + const last = React.useCallback(() => { + moveFocus(total - 1, buttons); + }, [buttons, total]); + React.useEffect(() => { if (defaultActiveId) { dispatch({ type: "addActiveItem", id: defaultActiveId }); @@ -102,18 +136,10 @@ export function useAccordionState( registerItem: React.useCallback(item => { dispatch({ type: "registerItem", item }); }, []), - next: React.useCallback(id => { - dispatch({ type: "next", id }); - }, []), - prev: React.useCallback(id => { - dispatch({ type: "prev", id }); - }, []), - first: React.useCallback(() => { - dispatch({ type: "first" }); - }, []), - last: React.useCallback(() => { - dispatch({ type: "last" }); - }, []), + next, + prev, + first, + last, }; } @@ -122,20 +148,13 @@ export type AccordionReducerAction = | { type: "registerButton"; button: Button } | { type: "registerPanel"; panel: Panel } | { type: "addActiveItem"; id: string } - | { type: "removeActiveItem"; id: string } - | { type: "next"; id: string } - | { type: "prev"; id: string } - | { type: "first" } - | { type: "last" }; + | { type: "removeActiveItem"; id: string }; function reducer( state: AccordionState, action: AccordionReducerAction, ): AccordionState { - const { items, activeItems, buttons, allowMultiple, loop } = state; - - const total = buttons.length; - const buttonIds = buttons.map(({ id }) => id); + const { items, activeItems, buttons, allowMultiple } = state; switch (action.type) { case "addActiveItem": { @@ -190,54 +209,6 @@ function reducer( return { ...state, items: [...items, item] }; } - case "next": { - const { id } = action; - - const currentIndex = buttonIds.indexOf(id); - const nextIndex = (currentIndex + 1) % total; - - if (!loop && nextIndex === 0) { - return { ...state }; - } - - const nextItem = buttons[nextIndex]; - nextItem.ref?.current?.focus(); - - return { ...state }; - } - - case "prev": { - const { id } = action; - - const currentIndex = buttonIds.indexOf(id); - const prevIndex = (currentIndex - 1 + total) % total; - - if (!loop && prevIndex === total - 1) { - return { ...state }; - } - - const prevItem = buttons[prevIndex]; - prevItem.ref?.current?.focus(); - - return { ...state }; - } - - case "first": { - const firstItem = buttons[0]; - firstItem.ref?.current?.focus(); - - return { ...state }; - } - - case "last": { - const total = buttons.length; - - const lastItem = buttons[total - 1]; - lastItem.ref?.current?.focus(); - - return { ...state }; - } - default: throw new Error(); } @@ -254,3 +225,8 @@ function getNextItem(type: string, currentItem: Button | Panel, items: Item[]) { return { nextItem, nextItems }; } + +function moveFocus(index: number, buttons: Button[]) { + const item = buttons[index]; + item.ref?.current?.focus(); +}