@kobalte/core
Version:
Unstyled components and primitives for building accessible web apps and design systems with SolidJS.
344 lines (339 loc) • 13.3 kB
JavaScript
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 };