Skip to content

Instantly share code, notes, and snippets.

@ginpei
Created May 19, 2021 16:57
Show Gist options
  • Save ginpei/ae79165648cff9c3e18ba94bef3300bd to your computer and use it in GitHub Desktop.
Save ginpei/ae79165648cff9c3e18ba94bef3300bd to your computer and use it in GitHub Desktop.
useListBox
import { useFocusRing } from "@react-aria/focus";
import { useListBox, useOption } from "@react-aria/listbox";
import { mergeProps } from "@react-aria/utils";
import { ListProps, ListState, useListState } from "@react-stately/list";
import { Node } from "@react-types/shared";
import { useRef } from "react";
interface ListBoxProps<T> extends ListProps<T> {
label?: string;
}
interface OptionProps<T> {
item: Node<T>;
state: ListState<T>;
}
export function NiceListBox<T extends Record<string, unknown>>(
props: ListBoxProps<T>
): JSX.Element {
// Create state based on the incoming props
const state = useListState(props);
// Get props for the listbox element
const ref = useRef() as React.RefObject<HTMLUListElement>;
const { listBoxProps, labelProps } = useListBox(props, state, ref);
return (
<>
<div {...labelProps}>{props.label}</div>
<ul
{...listBoxProps}
ref={ref}
style={{
padding: 0,
margin: "5px 0",
listStyle: "none",
border: "1px solid gray",
maxWidth: 250,
}}
>
{Array.from(state.collection).map((item) => (
<Option key={item.key} item={item} state={state} />
))}
</ul>
</>
);
}
function Option<T>({ item, state }: OptionProps<T>) {
// Get props for the option element
const ref = useRef() as React.RefObject<HTMLLIElement>;
const isDisabled = state.disabledKeys.has(item.key);
const isSelected = state.selectionManager.isSelected(item.key);
const { optionProps } = useOption(
{
key: item.key,
isDisabled,
isSelected,
},
state,
ref
);
// Determine whether we should show a keyboard
// focus ring for accessibility
const { isFocusVisible, focusProps } = useFocusRing();
return (
<li
{...mergeProps(optionProps, focusProps)}
ref={ref}
style={{
background: isSelected ? "blueviolet" : "transparent",
color: isSelected ? "white" : undefined,
padding: "2px 5px",
outline: isFocusVisible ? "2px solid orange" : "none",
}}
>
{item.rendered}
</li>
);
}
import { Item } from "@react-stately/collections";
import { NiceListBox } from "./NiceListBox";
const Playground: React.FC = () => {
return (
<div>
<NiceListBox label="Choose an option" selectionMode="single">
<Item>Item 1</Item>
<Item>Item 2</Item>
<Item>Item 3</Item>
</NiceListBox>
</div>
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment