sdxc

Type to search, or start from one of these:

ListBox

A run of selectable option rows sharing one native grouping name.

Move issue to
Active
Archived

Arrow keys move the active row, Enter selects it, and typing jumps to a match.

View code
let value = "docs-site";
let activeValue: listbox.ListboxValue = "docs-site";

<listbox.Context
	value={value}
	activeValue={activeValue}
	onSelect={(next) => {
		value = next ?? value;
		void handle.update();
	}}
	onHighlight={(next) => {
		activeValue = next;
		void handle.update();
	}}
>
	<div mix={[vstack({ gap: 2, align: "stretch" })]}>
		<Header mix={[m(0)]}>Move issue to</Header>

		<ListBox
			role="listbox"
			tabIndex={0}
			aria-label="Move issue to"
			name="projectId"
			mix={[listboxKeys()]}
		>
			{GROUPS.map((group) => (
				<Section key={group.id} aria-labelledby={group.id + "-heading"}>
					<Header id={group.id + "-heading"}>{group.label}</Header>
					{group.options.map((option) => (
						<ListBox.Item
							key={option.value}
							value={option.value}
							checked={value === option.value}
							disabled={option.disabled}
							mix={[listbox.option({ value: option.value, label: option.label, disabled: option.disabled })]}
						>
							{option.label}
						</ListBox.Item>
					))}
				</Section>
			))}
		</ListBox>

		<p mix={[m(0), text("xs")]}>
			Arrow keys move the active row, Enter selects it, and typing jumps to a match.
		</p>
	</div>
</listbox.Context>

Installation

An ordinary dependency: install it, import the theme once, and import the component where it is used.

npm add @sdxc/ui
import "@sdxc/ui/theme.css";

Usage

import { ListBox } from "@sdxc/ui";

Renders the group host: a <div> laying its ListBox.Item options out as a scrollable column capped at a fixed block size, with role defaulting to "radiogroup" for single selection or "group" for multiple.

Composition

The parts the component publishes, each a static property of the host.

  • ListBox
  • ListBox.ItemRenders a single option: a native <label> pairing a visually hidden radio or checkbox input with the option's content.

Examples

<ListBox aria-label={t("settings.notifications")} multiple>
	<Section aria-labelledby="notifications-heading">
		<Header id="notifications-heading">{t("settings.channels")}</Header>
		<ListBox.Item value="email" defaultChecked>{t("settings.email")}</ListBox.Item>
		<ListBox.Item value="sms">{t("settings.sms")}</ListBox.Item>
	</Section>
</ListBox>

Props

Read from the component's own types, so every prop, every default and every allowed value is listed.

PropTypeDescription
name?stringNative grouping name shared by every ListBox.Item nested inside, provided through component context. Defaults to the group's own stable instance id, so options always group correctly.
multiple?booleanRenders every option as a checkbox, allowing more than one selected at once, instead of the default mutually exclusive radio behavior. Defaults to DEFAULT_MULTIPLE.

Also accepts everything in TagProps<"div">.

ListBox.Item

PropTypeDescription
valuestringValue submitted with the enclosing form when this option is selected.
name?stringNative grouping name for this option's underlying input. Defaults to the name provided by the nearest ancestor ListBox — set this only to opt a single option out of its group's shared name.
defaultChecked?booleanWhether this option starts selected, for a form that never tracks selection itself.
checked?booleanWhether this option is selected, for a form that tracks selection itself.
disabled?booleanWhether this option is inert and excluded from the group's tab order.
required?booleanSets the underlying input's native required attribute. On a radio input this requires at least one option in the group be selected; on a checkbox input it requires this specific option be checked.
children?RemixNodeThe option's visible content, associated with the input by native nesting.
parts?{ /** Additional mixin(s) applied to the hidden native `<input>`. */ input?: TagProps<"input">["mix"]; }Per-part styling for the option's hidden input element, layered after its own built-in styling. Use the mix prop instead to style the option's outer <label> host.

Also accepts everything in Omit<TagProps<"label">, "children">.