createListNavigation is the core primitive for all collection components (Listbox, Select, Combobox, Menu). It provides value-based keyboard navigation, selection/activation modes, typeahead, and aria-activedescendant virtual focus. Listbox: standalone selectable list with single/multiple selection. Select: floating dropdown with trigger, keyboard navigation, form integration. Also fixes exactOptionalPropertyTypes compatibility in createDisclosureState and createListNavigation interfaces.
38 lines
1.2 KiB
TypeScript
38 lines
1.2 KiB
TypeScript
import type { Accessor } from "solid-js";
|
|
import { createControllableSignal } from "./create-controllable-signal";
|
|
|
|
export interface CreateDisclosureStateOptions {
|
|
open?: boolean | undefined;
|
|
defaultOpen?: boolean | undefined;
|
|
onOpenChange?: ((open: boolean) => void) | undefined;
|
|
}
|
|
|
|
export interface DisclosureState {
|
|
isOpen: Accessor<boolean>;
|
|
open: () => void;
|
|
close: () => void;
|
|
toggle: () => void;
|
|
}
|
|
|
|
/**
|
|
* Shared open/close state for all disclosure components (Dialog, Popover,
|
|
* Tooltip, Collapsible, etc.). Wraps createControllableSignal with
|
|
* convenience open/close/toggle methods.
|
|
*/
|
|
export function createDisclosureState(options: CreateDisclosureStateOptions): DisclosureState {
|
|
const [isOpen, setIsOpen] = createControllableSignal<boolean>({
|
|
value: () => options.open,
|
|
defaultValue: () => options.defaultOpen ?? false,
|
|
...(options.onOpenChange !== undefined && { onChange: options.onOpenChange }),
|
|
});
|
|
|
|
return {
|
|
isOpen,
|
|
open: () => setIsOpen(true),
|
|
close: () => setIsOpen(false),
|
|
// Imperative-only: do not call toggle() inside a reactive computation (effect/memo),
|
|
// as isOpen() would create an unwanted reactive dependency there.
|
|
toggle: () => setIsOpen(!isOpen()),
|
|
};
|
|
}
|