use-roving-index
use-roving-index
roving tabindex 키보드 내비게이션 패턴을 구현하는 훅이에요.
출처: 문서
본문
사용법 (Usage)
use-roving-index는 roving tabindex 키보드 내비게이션 패턴을 구현해요. 포커스 가능한 엘리먼트 그룹에서 하나의 엘리먼트만 tabIndex={0}(Tab 키로 도달 가능)을 가지며, 나머지는 모두 tabIndex={-1}이에요. 화살표 키로 그룹 안의 항목 사이를 이동할 수 있어요.
import { Button, Group } from '@mantine/core';
import { useRovingIndex } from '@mantine/hooks';
const items = ['Bold', 'Italic', 'Underline', 'Strikethrough', 'Code'];
function Demo() {
const { getItemProps } = useRovingIndex({
total: items.length,
orientation: 'horizontal',
loop: true,
});
return (
{items.map((item, index) => (
{item}
))}
);
}
방향 (Orientation)
내비게이션에 사용할 화살표 키를 제어하려면 orientation을 설정해요.
'horizontal'(default) – ArrowLeft/ArrowRight'vertical'– ArrowUp/ArrowDown'both'– 네 방향 화살표 키 모두
import { Stack, UnstyledButton } from '@mantine/core';
import { useRovingIndex } from '@mantine/hooks';
const items = ['General', 'Account', 'Security', 'Notifications', 'Privacy'];
function Demo() {
const { getItemProps, focusedIndex } = useRovingIndex({
total: items.length,
orientation: 'vertical',
loop: true,
});
return (
{items.map((item, index) => (
{item}
))}
);
}
그리드 내비게이션 (Grid navigation)
2D 그리드 내비게이션을 활성화하려면 columns를 설정해요. ArrowLeft/ArrowRight는 한 행 안에서 이동하고, ArrowUp/ArrowDown은 열 위치를 유지한 채 행 사이를 이동해요. 내비게이션은 그리드 경계에서 멈춰요. Ctrl+Home/Ctrl+End로 그리드의 첫/마지막 항목으로 이동하고, Home/End로 현재 행의 첫/마지막 항목으로 이동할 수 있어요.
import { SimpleGrid, UnstyledButton } from '@mantine/core';
import { useRovingIndex } from '@mantine/hooks';
function Demo() {
const total = 9;
const columns = 3;
const { getItemProps, focusedIndex } = useRovingIndex({
total,
columns,
});
return (
{Array.from({ length: total }, (_, index) => (
Cell {index + 1}
))}
);
}
비활성 항목 (Disabled items)
항목을 비활성으로 표시하려면 isItemDisabled 콜백을 사용해요. 비활성 항목은 키보드 내비게이션에서 건너뛰어져요. 처음에 포커스된 항목이 비활성이면, 대신 첫 번째 비활성이 아닌 항목이 포커스를 받아요.
import { Button, Group } from '@mantine/core';
import { useRovingIndex } from '@mantine/hooks';
const items = ['Cut', 'Copy', 'Paste', 'Delete', 'Select All'];
const disabledIndices = new Set([1, 3]);
function Demo() {
const { getItemProps } = useRovingIndex({
total: items.length,
orientation: 'horizontal',
loop: true,
isItemDisabled: (index) => disabledIndices.has(index),
});
return (
{items.map((item, index) => (
{item}
))}
);
}
순환 (Loop)
기본적으로 내비게이션은 경계에서 순환해요(loop가 true). 첫 항목과 마지막 항목에서 멈추려면 loop={false}로 설정해요.
import { useState } from 'react';
import { Button, Checkbox, Group, Stack } from '@mantine/core';
import { useRovingIndex } from '@mantine/hooks';
const items = ['First', 'Second', 'Third', 'Fourth', 'Fifth'];
function Demo() {
const [loop, setLoop] = useState(true);
const { getItemProps } = useRovingIndex({
total: items.length,
orientation: 'horizontal',
loop,
});
return (
setLoop(event.currentTarget.checked)}
/>
{items.map((item, index) => (
{item}
))}
);
}
제어 모드 (Controlled mode)
focusedIndex와 onFocusChange로 포커스된 인덱스를 외부에서 제어할 수 있어요.
import { useState } from 'react';
import { useRovingIndex } from '@mantine/hooks';
function Demo() {
const [focusedIndex, setFocusedIndex] = useState(0);
const { getItemProps } = useRovingIndex({
total: 5,
focusedIndex,
onFocusChange: setFocusedIndex,
});
// ...
}
activateOnFocus
키보드 내비게이션으로 항목이 포커스를 받을 때 자동으로 클릭하게 하려면 activateOnFocus를 true로 설정해요. 포커스와 선택이 동기화되어야 하는 탭 같은 인터페이스에 유용해요.
import { useRovingIndex } from '@mantine/hooks';
function Demo() {
const { getItemProps } = useRovingIndex({
total: 5,
activateOnFocus: true,
});
// ...
}
RTL 지원 (RTL support)
오른쪽에서 왼쪽으로 읽는 레이아웃에서 ArrowLeft/ArrowRight 동작을 바꾸려면 dir="rtl"을 설정해요.
import { useRovingIndex } from '@mantine/hooks';
function Demo() {
const { getItemProps } = useRovingIndex({
total: 5,
dir: 'rtl',
});
// ...
}
정의 (Definition)
export interface UseRovingIndexInput {
/** Total number of items in the group */
total: number;
/** Which arrow keys navigate, `'horizontal'` by default */
orientation?: 'horizontal' | 'vertical' | 'both';
/** Whether navigation wraps at boundaries, `true` by default */
loop?: boolean;
/** Text direction, `'ltr'` by default */
dir?: 'rtl' | 'ltr';
/** Whether to click element when it receives focus via keyboard, `false` by default */
activateOnFocus?: boolean;
/** Number of columns for grid (2D) navigation. When set, enables grid mode */
columns?: number;
/** Controlled focused index */
focusedIndex?: number;
/** Initial focused index for uncontrolled mode, first non-disabled item by default */
initialIndex?: number;
/** Called when focused index changes */
onFocusChange?: (index: number) => void;
/** Function to check if item at given index is disabled, `() => false` by default */
isItemDisabled?: (index: number) => boolean;
}
export interface UseRovingIndexGetItemPropsInput {
/** Index of the item in the group */
index: number;
/** Called when item is clicked */
onClick?: React.MouseEventHandler;
/** Called when keydown event fires on item */
onKeyDown?: React.KeyboardEventHandler;
}
export interface UseRovingIndexReturnValue {
/** Get props to spread on each navigable item */
getItemProps: (options: UseRovingIndexGetItemPropsInput) => {
tabIndex: 0 | -1;
onKeyDown: React.KeyboardEventHandler;
onClick: React.MouseEventHandler;
ref: React.RefCallback;
};
/** Currently focused index */
focusedIndex: number;
/** Programmatically set focused index */
setFocusedIndex: (index: number) => void;
}
function useRovingIndex(input: UseRovingIndexInput): UseRovingIndexReturnValue;
내보내는 타입 (Exported types)
UseRovingIndexInput, UseRovingIndexGetItemPropsInput, UseRovingIndexReturnValue 타입은 @mantine/hooks 패키지에서 내보내져요. 애플리케이션에서 임포트할 수 있어요.
import type {
UseRovingIndexInput,
UseRovingIndexGetItemPropsInput,
UseRovingIndexReturnValue,
} from '@mantine/hooks';