UNPKG

@kobalte/core

Version:

Unstyled components and primitives for building accessible web apps and design systems with SolidJS.

344 lines (339 loc) 13.3 kB
import { getItemCount } from './2X5XWQIS.js'; import { createSelectableList } from './GLKC2QFF.js'; import { createSelectableItem, createListState } from './H6DSIDEC.js'; import { createRegisterId } from './E4R2EMM4.js'; import { Polymorphic } from './6Y7B2NEO.js'; import { __export } from './5ZKAE4VZ.js'; import { createComponent, mergeProps } from 'solid-js/web'; import { mergeDefaultProps, isMac, isWebKit, createGenerateId, mergeRefs, composeEventHandlers, access, Key, callHandler, focusWithoutScrolling } from '@kobalte/utils'; import { createContext, useContext, createUniqueId, splitProps, createSignal, createMemo, createEffect, onCleanup, Show, Switch, Match } from 'solid-js'; // src/listbox/index.tsx var listbox_exports = {}; __export(listbox_exports, { Item: () => ListboxItem, ItemDescription: () => ListboxItemDescription, ItemIndicator: () => ListboxItemIndicator, ItemLabel: () => ListboxItemLabel, Listbox: () => Listbox, Root: () => ListboxRoot, Section: () => ListboxSection, useListboxContext: () => useListboxContext }); var ListboxContext = createContext(); function useListboxContext() { const context = useContext(ListboxContext); if (context === void 0) { throw new Error("[kobalte]: `useListboxContext` must be used within a `Listbox` component"); } return context; } var ListboxItemContext = createContext(); function useListboxItemContext() { const context = useContext(ListboxItemContext); if (context === void 0) { throw new Error("[kobalte]: `useListboxItemContext` must be used within a `Listbox.Item` component"); } return context; } // src/listbox/listbox-item.tsx function ListboxItem(props) { let ref; const listBoxContext = useListboxContext(); const defaultId = `${listBoxContext.generateId("item")}-${createUniqueId()}`; const mergedProps = mergeDefaultProps({ id: defaultId }, props); const [local, others] = splitProps(mergedProps, ["ref", "item", "aria-label", "aria-labelledby", "aria-describedby", "onPointerMove", "onPointerDown", "onPointerUp", "onClick", "onKeyDown", "onMouseDown", "onFocus"]); const [labelId, setLabelId] = createSignal(); const [descriptionId, setDescriptionId] = createSignal(); const selectionManager = () => listBoxContext.listState().selectionManager(); const isHighlighted = () => selectionManager().focusedKey() === local.item.key; const selectableItem = createSelectableItem({ key: () => local.item.key, selectionManager, shouldSelectOnPressUp: listBoxContext.shouldSelectOnPressUp, allowsDifferentPressOrigin: () => { return listBoxContext.shouldSelectOnPressUp() && listBoxContext.shouldFocusOnHover(); }, shouldUseVirtualFocus: listBoxContext.shouldUseVirtualFocus, disabled: () => local.item.disabled }, () => ref); const ariaSelected = () => { if (selectionManager().selectionMode() === "none") { return void 0; } return selectableItem.isSelected(); }; const isNotSafariMacOS = createMemo(() => !(isMac() && isWebKit())); const ariaLabel = () => isNotSafariMacOS() ? local["aria-label"] : void 0; const ariaLabelledBy = () => isNotSafariMacOS() ? labelId() : void 0; const ariaDescribedBy = () => isNotSafariMacOS() ? descriptionId() : void 0; const ariaPosInSet = () => { if (!listBoxContext.isVirtualized()) { return void 0; } const index = listBoxContext.listState().collection().getItem(local.item.key)?.index; return index != null ? index + 1 : void 0; }; const ariaSetSize = () => { if (!listBoxContext.isVirtualized()) { return void 0; } return getItemCount(listBoxContext.listState().collection()); }; const onPointerMove = (e) => { callHandler(e, local.onPointerMove); if (e.pointerType !== "mouse") { return; } if (!selectableItem.isDisabled() && listBoxContext.shouldFocusOnHover()) { focusWithoutScrolling(e.currentTarget); selectionManager().setFocused(true); selectionManager().setFocusedKey(local.item.key); } }; const dataset = createMemo(() => ({ "data-disabled": selectableItem.isDisabled() ? "" : void 0, "data-selected": selectableItem.isSelected() ? "" : void 0, "data-highlighted": isHighlighted() ? "" : void 0 })); const context = { isSelected: selectableItem.isSelected, dataset, generateId: createGenerateId(() => others.id), registerLabelId: createRegisterId(setLabelId), registerDescriptionId: createRegisterId(setDescriptionId) }; return createComponent(ListboxItemContext.Provider, { value: context, get children() { return createComponent(Polymorphic, mergeProps({ as: "li", ref(r$) { const _ref$ = mergeRefs((el) => ref = el, local.ref); typeof _ref$ === "function" && _ref$(r$); }, role: "option", get tabIndex() { return selectableItem.tabIndex(); }, get ["aria-disabled"]() { return selectableItem.isDisabled(); }, get ["aria-selected"]() { return ariaSelected(); }, get ["aria-label"]() { return ariaLabel(); }, get ["aria-labelledby"]() { return ariaLabelledBy(); }, get ["aria-describedby"]() { return ariaDescribedBy(); }, get ["aria-posinset"]() { return ariaPosInSet(); }, get ["aria-setsize"]() { return ariaSetSize(); }, get ["data-key"]() { return selectableItem.dataKey(); }, get onPointerDown() { return composeEventHandlers([local.onPointerDown, selectableItem.onPointerDown]); }, get onPointerUp() { return composeEventHandlers([local.onPointerUp, selectableItem.onPointerUp]); }, get onClick() { return composeEventHandlers([local.onClick, selectableItem.onClick]); }, get onKeyDown() { return composeEventHandlers([local.onKeyDown, selectableItem.onKeyDown]); }, get onMouseDown() { return composeEventHandlers([local.onMouseDown, selectableItem.onMouseDown]); }, get onFocus() { return composeEventHandlers([local.onFocus, selectableItem.onFocus]); }, onPointerMove }, dataset, others)); } }); } function ListboxItemDescription(props) { const context = useListboxItemContext(); const mergedProps = mergeDefaultProps({ id: context.generateId("description") }, props); createEffect(() => onCleanup(context.registerDescriptionId(mergedProps.id))); return createComponent(Polymorphic, mergeProps({ as: "div" }, () => context.dataset(), mergedProps)); } function ListboxItemIndicator(props) { const context = useListboxItemContext(); const mergedProps = mergeDefaultProps({ id: context.generateId("indicator") }, props); const [local, others] = splitProps(mergedProps, ["forceMount"]); return createComponent(Show, { get when() { return local.forceMount || context.isSelected(); }, get children() { return createComponent(Polymorphic, mergeProps({ as: "div", "aria-hidden": "true" }, () => context.dataset(), others)); } }); } function ListboxItemLabel(props) { const context = useListboxItemContext(); const mergedProps = mergeDefaultProps({ id: context.generateId("label") }, props); createEffect(() => onCleanup(context.registerLabelId(mergedProps.id))); return createComponent(Polymorphic, mergeProps({ as: "div" }, () => context.dataset(), mergedProps)); } function ListboxRoot(props) { let ref; const defaultId = `listbox-${createUniqueId()}`; const mergedProps = mergeDefaultProps({ id: defaultId, selectionMode: "single", virtualized: false }, props); const [local, others] = splitProps(mergedProps, ["ref", "children", "renderItem", "renderSection", "value", "defaultValue", "onChange", "options", "optionValue", "optionTextValue", "optionDisabled", "optionGroupChildren", "state", "keyboardDelegate", "autoFocus", "selectionMode", "shouldFocusWrap", "shouldUseVirtualFocus", "shouldSelectOnPressUp", "shouldFocusOnHover", "allowDuplicateSelectionEvents", "disallowEmptySelection", "selectionBehavior", "selectOnFocus", "disallowTypeAhead", "allowsTabNavigation", "virtualized", "scrollToItem", "scrollRef", "onKeyDown", "onMouseDown", "onFocusIn", "onFocusOut"]); const listState = createMemo(() => { if (local.state) { return local.state; } return createListState({ selectedKeys: () => local.value, defaultSelectedKeys: () => local.defaultValue, onSelectionChange: local.onChange, allowDuplicateSelectionEvents: () => access(local.allowDuplicateSelectionEvents), disallowEmptySelection: () => access(local.disallowEmptySelection), selectionBehavior: () => access(local.selectionBehavior), selectionMode: () => access(local.selectionMode), dataSource: () => local.options ?? [], getKey: () => local.optionValue, getTextValue: () => local.optionTextValue, getDisabled: () => local.optionDisabled, getSectionChildren: () => local.optionGroupChildren }); }); const selectableList = createSelectableList({ selectionManager: () => listState().selectionManager(), collection: () => listState().collection(), autoFocus: () => access(local.autoFocus), shouldFocusWrap: () => access(local.shouldFocusWrap), keyboardDelegate: () => local.keyboardDelegate, disallowEmptySelection: () => access(local.disallowEmptySelection), selectOnFocus: () => access(local.selectOnFocus), disallowTypeAhead: () => access(local.disallowTypeAhead), shouldUseVirtualFocus: () => access(local.shouldUseVirtualFocus), allowsTabNavigation: () => access(local.allowsTabNavigation), isVirtualized: () => local.virtualized, scrollToKey: () => local.scrollToItem }, () => ref, () => local.scrollRef?.()); const context = { listState, generateId: createGenerateId(() => others.id), shouldUseVirtualFocus: () => mergedProps.shouldUseVirtualFocus, shouldSelectOnPressUp: () => mergedProps.shouldSelectOnPressUp, shouldFocusOnHover: () => mergedProps.shouldFocusOnHover, isVirtualized: () => local.virtualized }; return createComponent(ListboxContext.Provider, { value: context, get children() { return createComponent(Polymorphic, mergeProps({ as: "ul", ref(r$) { const _ref$ = mergeRefs((el) => ref = el, local.ref); typeof _ref$ === "function" && _ref$(r$); }, role: "listbox", get tabIndex() { return selectableList.tabIndex(); }, get ["aria-multiselectable"]() { return listState().selectionManager().selectionMode() === "multiple" ? true : void 0; }, get onKeyDown() { return composeEventHandlers([local.onKeyDown, selectableList.onKeyDown]); }, get onMouseDown() { return composeEventHandlers([local.onMouseDown, selectableList.onMouseDown]); }, get onFocusIn() { return composeEventHandlers([local.onFocusIn, selectableList.onFocusIn]); }, get onFocusOut() { return composeEventHandlers([local.onFocusOut, selectableList.onFocusOut]); } }, others, { get children() { return createComponent(Show, { get when() { return !local.virtualized; }, get fallback() { return local.children?.(listState().collection); }, get children() { return createComponent(Key, { get each() { return [...listState().collection()]; }, by: "key", children: (item) => createComponent(Switch, { get children() { return [createComponent(Match, { get when() { return item().type === "section"; }, get children() { return local.renderSection?.(item()); } }), createComponent(Match, { get when() { return item().type === "item"; }, get children() { return local.renderItem?.(item()); } })]; } }) }); } }); } })); } }); } function ListboxSection(props) { return createComponent(Polymorphic, mergeProps({ as: "li", role: "presentation" }, props)); } // src/listbox/index.tsx var Listbox = Object.assign(ListboxRoot, { Item: ListboxItem, ItemDescription: ListboxItemDescription, ItemIndicator: ListboxItemIndicator, ItemLabel: ListboxItemLabel, Section: ListboxSection }); export { Listbox, ListboxItem, ListboxItemDescription, ListboxItemIndicator, ListboxItemLabel, ListboxRoot, ListboxSection, listbox_exports, useListboxContext };