reakit
Version:
Toolkit for building accessible rich web apps with React
288 lines (287 loc) • 11 kB
TypeScript
import * as React from "react";
import { SealedInitialState } from "reakit-utils/useSealedState";
import { unstable_IdState, unstable_IdActions, unstable_IdInitialState, unstable_IdStateReturn } from "../Id/IdState";
import { Item, Group, Orientation } from "./__utils/types";
export declare function useCompositeState(initialState?: SealedInitialState<CompositeInitialState>): CompositeStateReturn;
export declare type CompositeState = unstable_IdState & {
/**
* If enabled, the composite element will act as an
* [aria-activedescendant](https://www.w3.org/TR/wai-aria-practices-1.1/#kbd_focus_activedescendant)
* container instead of
* [roving tabindex](https://www.w3.org/TR/wai-aria-practices/#kbd_roving_tabindex).
* DOM focus will remain on the composite while its items receive virtual focus.
* @default false
*/
unstable_virtual: boolean;
/**
* Determines how `next` and `previous` functions will behave. If `rtl` is
* set to `true`, they will be inverted. This only affects the composite
* widget behavior. You still need to set `dir="rtl"` on HTML/CSS.
* @default false
*/
rtl: boolean;
/**
* Defines the orientation of the composite widget. If the composite has a
* single row or column (one-dimensional), the `orientation` value determines
* which arrow keys can be used to move focus:
* - `undefined`: all arrow keys work.
* - `horizontal`: only left and right arrow keys work.
* - `vertical`: only up and down arrow keys work.
*
* It doesn't have any effect on two-dimensional composites.
* @default undefined
*/
orientation?: Orientation;
/**
* Lists all the composite items with their `id`, DOM `ref`, `disabled` state
* and `groupId` if any. This state is automatically updated when
* `registerItem` and `unregisterItem` are called.
* @example
* const composite = useCompositeState();
* composite.items.forEach((item) => {
* const { id, ref, disabled, groupId } = item;
* ...
* });
*/
items: Item[];
/**
* Lists all the composite groups with their `id` and DOM `ref`. This state
* is automatically updated when `registerGroup` and `unregisterGroup` are
* called.
* @example
* const composite = useCompositeState();
* composite.groups.forEach((group) => {
* const { id, ref } = group;
* ...
* });
*/
groups: Group[];
/**
* The current focused item `id`.
* - `undefined` will automatically focus the first enabled composite item.
* - `null` will focus the base composite element and users will be able to
* navigate out of it using arrow keys.
* - If `currentId` is initially set to `null`, the base composite element
* itself will have focus and users will be able to navigate to it using
* arrow keys.
* @default undefined
* @example
* // First enabled item has initial focus
* useCompositeState();
* // Base composite element has initial focus
* useCompositeState({ currentId: null });
* // Specific composite item element has initial focus
* useCompositeState({ currentId: "item-id" });
*/
currentId?: string | null;
/**
* On one-dimensional composites:
* - `true` loops from the last item to the first item and vice-versa.
* - `horizontal` loops only if `orientation` is `horizontal` or not set.
* - `vertical` loops only if `orientation` is `vertical` or not set.
* - If `currentId` is initially set to `null`, the composite element will
* be focused in between the last and first items.
*
* On two-dimensional composites:
* - `true` loops from the last row/column item to the first item in the
* same row/column and vice-versa. If it's the last item in the last row, it
* moves to the first item in the first row and vice-versa.
* - `horizontal` loops only from the last row item to the first item in
* the same row.
* - `vertical` loops only from the last column item to the first item in
* the column row.
* - If `currentId` is initially set to `null`, vertical loop will have no
* effect as moving down from the last row or up from the first row will
* focus the composite element.
* - If `wrap` matches the value of `loop`, it'll wrap between the last
* item in the last row or column and the first item in the first row or
* column and vice-versa.
* @default false
*/
loop: boolean | Orientation;
/**
* **Has effect only on two-dimensional composites**. If enabled, moving to
* the next item from the last one in a row or column will focus the first
* item in the next row or column and vice-versa.
* - `true` wraps between rows and columns.
* - `horizontal` wraps only between rows.
* - `vertical` wraps only between columns.
* - If `loop` matches the value of `wrap`, it'll wrap between the last
* item in the last row or column and the first item in the first row or
* column and vice-versa.
* @default false
*/
wrap: boolean | Orientation;
/**
* **Has effect only on two-dimensional composites**. If enabled, moving up
* or down when there's no next item or the next item is disabled will shift
* to the item right before it.
* @default false
*/
shift: boolean;
/**
* Stores the number of moves that have been performed by calling `move`,
* `next`, `previous`, `up`, `down`, `first` or `last`.
* @default 0
*/
unstable_moves: number;
/**
* @default false
* @private
*/
unstable_hasActiveWidget: boolean;
/**
* @default false
* @private
*/
unstable_includesBaseElement: boolean;
};
export declare type CompositeActions = unstable_IdActions & {
/**
* Registers a composite item.
* @example
* const ref = React.useRef();
* const composite = useCompositeState();
* React.useEffect(() => {
* composite.registerItem({ ref, id: "id" });
* return () => composite.unregisterItem("id");
* }, []);
*/
registerItem: (item: Item) => void;
/**
* Unregisters a composite item.
* @example
* const ref = React.useRef();
* const composite = useCompositeState();
* React.useEffect(() => {
* composite.registerItem({ ref, id: "id" });
* return () => composite.unregisterItem("id");
* }, []);
*/
unregisterItem: (id: string) => void;
/**
* Registers a composite group.
* @example
* const ref = React.useRef();
* const composite = useCompositeState();
* React.useEffect(() => {
* composite.registerGroup({ ref, id: "id" });
* return () => composite.unregisterGroup("id");
* }, []);
*/
registerGroup: (group: Group) => void;
/**
* Unregisters a composite group.
* @example
* const ref = React.useRef();
* const composite = useCompositeState();
* React.useEffect(() => {
* composite.registerGroup({ ref, id: "id" });
* return () => composite.unregisterGroup("id");
* }, []);
*/
unregisterGroup: (id: string) => void;
/**
* Moves focus to a given item ID.
* @example
* const composite = useCompositeState();
* composite.move("item-2"); // focus item 2
*/
move: (id: string | null) => void;
/**
* Moves focus to the next item.
*/
next: (unstable_allTheWay?: boolean) => void;
/**
* Moves focus to the previous item.
*/
previous: (unstable_allTheWay?: boolean) => void;
/**
* Moves focus to the item above.
*/
up: (unstable_allTheWay?: boolean) => void;
/**
* Moves focus to the item below.
*/
down: (unstable_allTheWay?: boolean) => void;
/**
* Moves focus to the first item.
*/
first: () => void;
/**
* Moves focus to the last item.
*/
last: () => void;
/**
* Sorts the `composite.items` based on the items position in the DOM. This
* is especially useful after modifying the composite items order in the DOM.
* Most of the time, though, you don't need to manually call this function as
* the re-ordering happens automatically.
*/
sort: () => void;
/**
* Sets `virtual`.
*/
unstable_setVirtual: React.Dispatch<React.SetStateAction<CompositeState["unstable_virtual"]>>;
/**
* Sets `rtl`.
* @example
* const composite = useCompositeState({ rtl: true });
* composite.setRTL(false);
*/
setRTL: React.Dispatch<React.SetStateAction<CompositeState["rtl"]>>;
/**
* Sets `orientation`.
*/
setOrientation: React.Dispatch<React.SetStateAction<CompositeState["orientation"]>>;
/**
* Sets `currentId`. This is different from `composite.move` as this only
* updates the `currentId` state without moving focus. When the composite
* widget gets focused by the user, the item referred by the `currentId`
* state will get focus.
* @example
* const composite = useCompositeState({ currentId: "item-1" });
* // Updates `composite.currentId` to `item-2`
* composite.setCurrentId("item-2");
*/
setCurrentId: React.Dispatch<React.SetStateAction<CompositeState["currentId"]>>;
/**
* Sets `loop`.
*/
setLoop: React.Dispatch<React.SetStateAction<CompositeState["loop"]>>;
/**
* Sets `wrap`.
*/
setWrap: React.Dispatch<React.SetStateAction<CompositeState["wrap"]>>;
/**
* Sets `shift`.
*/
setShift: React.Dispatch<React.SetStateAction<CompositeState["shift"]>>;
/**
* Resets to initial state.
* @example
* // On initial render, currentId will be item-1 and loop will be true
* const composite = useCompositeState({
* currentId: "item-1",
* loop: true,
* });
* // On next render, currentId will be item-2 and loop will be false
* composite.setCurrentId("item-2");
* composite.setLoop(false);
* // On next render, currentId will be item-1 and loop will be true
* composite.reset();
*/
reset: () => void;
/**
* Sets `includesBaseElement`.
* @private
*/
unstable_setIncludesBaseElement: React.Dispatch<React.SetStateAction<CompositeState["unstable_includesBaseElement"]>>;
/**
* Sets `hasActiveWidget`.
* @private
*/
unstable_setHasActiveWidget: React.Dispatch<React.SetStateAction<CompositeState["unstable_hasActiveWidget"]>>;
};
export declare type CompositeInitialState = unstable_IdInitialState & Partial<Pick<CompositeState, "unstable_virtual" | "rtl" | "orientation" | "currentId" | "loop" | "wrap" | "shift" | "unstable_includesBaseElement">>;
export declare type CompositeStateReturn = unstable_IdStateReturn & CompositeState & CompositeActions;