@bento/listbox 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -4
- package/dist/index.cjs +713 -349
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +156 -151
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +269 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +715 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +28 -20
- package/src/header.tsx +19 -20
- package/src/listbox-item.tsx +9 -8
- package/src/listbox-section.tsx +9 -4
- package/src/listbox.tsx +15 -13
- package/src/utils.ts +1 -1
- package/dist/index.d.ts +0 -264
- package/dist/index.js +0 -367
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/header.tsx","../src/listbox-section.tsx","../src/utils.ts","../src/listbox.tsx","../src/listbox-item.tsx"],"sourcesContent":["import type React from 'react';\nimport { createContext, useContext } from 'react';\nimport { createLeafComponent } from '@react-aria/collections';\nimport { useProps } from '@bento/use-props';\nimport { withSlots, type Slots } from '@bento/slots';\n\n/**\n * Props for the Header component.\n * @interface HeaderProps\n */\nexport interface HeaderProps extends Slots, React.ComponentProps<'header'> {\n /**\n * The children of the header.\n */\n readonly children?: React.ReactNode;\n}\n\n/**\n * Context value structure for Header components.\n * Extends HTML attributes to support all standard header element properties.\n * @interface HeaderContextValue\n */\ninterface HeaderContextValue extends React.HTMLAttributes<HTMLElement> {\n /**\n * Reference to the header element for forwarding.\n */\n readonly ref?: React.RefObject<HTMLDivElement | null>;\n}\n\n/**\n * Combined props type for the internal BentoHeader implementation.\n * Merges Header-specific props with standard HTML attributes to provide\n * a comprehensive interface for the internal header component.\n *\n * @type BentoHeaderProps\n * @internal\n */\ntype BentoHeaderProps = HeaderProps & React.HTMLAttributes<HTMLElement>;\n\n/**\n * React context for providing header-related attributes and refs to Header components.\n * Used internally by ListBoxSection to pass heading props to Header elements.\n * @public\n */\nexport const HeaderContext = createContext<HeaderContextValue>({});\n\n/**\n * Internal implementation of the BentoHeader component with slots support.\n * This component handles prop processing and context integration.\n * It merges props from useProps and HeaderContext while preserving styling props.\n *\n * @internal\n */\nconst BentoHeaderImpl = withSlots('BentoHeader', function BentoHeader(...args: any[]) {\n const [props, ref] = args as [BentoHeaderProps, React.ForwardedRef<HTMLElement>];\n const { props: processedProps, apply } = useProps(props);\n const contextProps = useContext(HeaderContext);\n\n // Apply user props directly (preserves className, style, etc.)\n const appliedUserProps = apply(processedProps);\n\n const composed = {\n ...contextProps,\n ...appliedUserProps // User props take precedence over context\n };\n\n return (\n <header {...composed} ref={contextProps.ref || ref}>\n {processedProps.children}\n </header>\n );\n});\n\n/**\n * Wrapper component that connects the BentoHeaderImpl to React Aria's collection system.\n * This function serves as an adapter between the createLeafComponent system and\n * the internal BentoHeaderImpl component, ensuring proper prop forwarding and ref handling.\n *\n * @param {HeaderProps} props - Header component props\n * @param {React.ReactNode} [props.children] - React children to render inside the header\n * @param {React.ForwardedRef<HTMLElement>} ref - Forwarded ref to the header element\n * @returns {React.ReactElement} The BentoHeaderImpl component with forwarded props and ref\n * @internal\n */\nfunction HeaderWrapper(props: HeaderProps, ref: React.ForwardedRef<HTMLElement>) {\n return <BentoHeaderImpl {...props} ref={ref} />;\n}\n\n/**\n * A Header represents a heading for a section within a ListBox.\n * Uses React Aria's createLeafComponent for automatic collection handling.\n *\n * @component\n * @param {HeaderProps} props - The props for the Header component\n * @param {React.ForwardedRef<HTMLElement>} ref - Forwarded ref to the header element\n * @returns {JSX.Element} A header element with proper accessibility attributes\n *\n * @example\n * ```tsx\n * <Header>My Section Title</Header>\n * ```\n * @public\n */\n/**\n * Base Header component created through React Aria's collection system.\n * This handles the connection to the parent ListBox's collection state and\n * integrates with the collection rendering system.\n * @internal\n */\nconst HeaderBase = createLeafComponent('header', HeaderWrapper);\n\n/**\n * A Header component for section headings within a ListBox.\n * Provides semantic header structure with proper accessibility attributes\n * and integrates with React Aria's collection system for automatic handling.\n *\n * This is the main public interface for creating headers in ListBox sections.\n * It automatically receives heading props from the parent ListBoxSection via HeaderContext.\n *\n * @component\n * @example\n * ```tsx\n * <ListBoxSection>\n * <Header>Fruits</Header>\n * <ListBoxItem>Apple</ListBoxItem>\n * <ListBoxItem>Banana</ListBoxItem>\n * </ListBoxSection>\n * ```\n * @public\n */\nexport const Header = HeaderBase as React.ForwardRefExoticComponent<HeaderProps & React.RefAttributes<HTMLElement>>;\n","import type React from 'react';\nimport { useRef, useContext } from 'react';\nimport { useListBoxSection, mergeProps } from 'react-aria';\nimport { createBranchComponent } from '@react-aria/collections';\nimport { CollectionRendererContext } from 'react-aria-components';\nimport type { Node } from '@react-types/shared';\nimport { useDataAttributes } from '@bento/use-data-attributes';\nimport { useProps } from '@bento/use-props';\nimport { withSlots } from '@bento/slots';\nimport { HeaderContext } from './header.tsx';\nimport { ListStateContext } from './listbox.tsx';\n\n/**\n * Props for the ListBoxSection component.\n * @interface ListBoxSectionProps\n */\nexport interface ListBoxSectionProps extends Omit<React.ComponentProps<'section'>, 'title'> {\n /**\n * A slot name for the component. Used by Bento's slot system.\n */\n readonly slot?: string;\n /**\n * The title of the section.\n */\n readonly title?: React.ReactNode;\n /**\n * The children of the section.\n */\n readonly children?: React.ReactNode;\n}\n\n/**\n * Internal props interface for BentoListBoxSectionImpl component.\n * Extends ListBoxSectionProps with internal React Aria node data and allows\n * additional properties for flexibility in prop handling.\n *\n * @interface BentoListBoxSectionImplProps\n * @template T - The type of the section node data\n * @internal\n */\ninterface BentoListBoxSectionImplProps<T = unknown> extends ListBoxSectionProps {\n readonly __node?: Node<T>;\n readonly [key: string]: unknown;\n}\n\n/**\n * Props interface for the ListBoxSectionInner component.\n * Contains the React Aria section node that represents this section\n * in the collection hierarchy for dynamic rendering.\n *\n * @interface ListBoxSectionInnerProps\n * @internal\n */\ninterface ListBoxSectionInnerProps {\n readonly section: Node<unknown>;\n}\n\n/**\n * Internal implementation of the BentoListBoxSection component with slots support.\n * This component handles the core logic for rendering a section within a ListBox,\n * including title rendering, accessibility attributes, and child content management.\n * It integrates with React Aria's useListBoxSection hook for proper ARIA compliance.\n *\n * @internal\n */\nconst BentoListBoxSectionImpl = withSlots('BentoListBoxSection', function BentoListBoxSectionImpl<\n T\n>(...restArgs: any[]) {\n const [{ __node, children, title: titleProp, ...rest }, ref] = restArgs as [\n BentoListBoxSectionImplProps<T>,\n React.ForwardedRef<HTMLElement>\n ];\n const { props, apply } = useProps(rest);\n const data = useDataAttributes({ level: __node?.level });\n const headingRef = useRef<HTMLDivElement>(null);\n\n const title = titleProp ?? props.title ?? __node?.rendered;\n const { groupProps, headingProps } = useListBoxSection({\n heading: title,\n 'aria-label': props['aria-label']\n });\n\n const composed = mergeProps(apply({ ...data, ...props }, ['children', 'title', 'slot']), groupProps);\n\n const sectionContent = children || props.children;\n\n return (\n <section {...composed} ref={ref}>\n <HeaderContext.Provider value={{ ...headingProps, ref: headingRef }}>\n {title && <div {...headingProps}>{title}</div>}\n {sectionContent}\n </HeaderContext.Provider>\n </section>\n );\n});\n\n/**\n * Wrapper component that connects BentoListBoxSectionImpl to React Aria's collection system.\n * This function serves as an adapter between createBranchComponent and the internal\n * BentoListBoxSectionImpl, ensuring proper prop forwarding and node injection for sections.\n *\n * @template T - The type of the section node data\n * @param {ListBoxSectionProps} props - ListBoxSection component props\n * @param {string} [props.slot] - Slot name for Bento's slot system\n * @param {React.ReactNode} [props.title] - Title for the section\n * @param {React.ReactNode} [props.children] - Children to render in the section\n * @param {string} [props.aria-label] - ARIA label for accessibility\n * @param {React.ForwardedRef<HTMLElement>} ref - Ref forwarded from the collection system\n * @param {Node<T>} section - React Aria node containing section metadata and collection info\n * @returns {React.ReactElement} The BentoListBoxSectionImpl component with proper node and ref wiring\n * @internal\n */\n/* v8 ignore start */\nfunction ListBoxSectionWrapper<T extends object>(\n props: ListBoxSectionProps,\n ref: React.ForwardedRef<HTMLElement>,\n section: Node<T>\n) {\n return <BentoListBoxSectionImpl {...props} __node={section} ref={ref} />;\n}\n/* v8 ignore stop */\n\n/**\n * Base ListBoxSection component created through React Aria's collection system.\n * This handles the connection to the parent ListBox's collection state and\n * manages the branch structure for nested items.\n * @internal\n */\nconst ListBoxSectionBase = createBranchComponent('section', ListBoxSectionWrapper);\n\n/**\n * Internal component for rendering dynamic collection sections.\n * This component is used specifically for sections that are part of a dynamic collection,\n * connecting to the ListStateContext and CollectionRendererContext to properly render\n * nested items through React Aria's collection system.\n *\n * @component\n * @param {object} props - The component props containing the section node\n * @param {Node<unknown>} props.section - The React Aria node representing this section in the collection\n * @throws {BentoError} Throws an error if used outside of a ListBox context\n * @returns {React.ReactElement} JSX element representing a dynamically rendered listbox section\n * @internal\n */\nexport const ListBoxSectionInner: React.FC<ListBoxSectionInnerProps> = function ListBoxSectionInner({ section }) {\n const state = useContext(ListStateContext);\n const { CollectionBranch } = useContext(CollectionRendererContext);\n\n return (\n <BentoListBoxSectionImpl {...section.props} __node={section}>\n {CollectionBranch && state?.collection ? (\n <CollectionBranch collection={state.collection} parent={section} />\n ) : null}\n </BentoListBoxSectionImpl>\n );\n};\n\n/**\n * A section component for organizing related items within a ListBox.\n *\n * @component\n * @example\n * ```tsx\n * <ListBoxSection title=\"Fruits\">\n * <ListBoxItem>Apple</ListBoxItem>\n * <ListBoxItem>Banana</ListBoxItem>\n * </ListBoxSection>\n * ```\n * @public\n */\nexport const ListBoxSection = ListBoxSectionBase as <_T extends object>(\n props: ListBoxSectionProps & { children?: React.ReactNode }\n) => React.ReactElement;\n","/* v8 ignore next */\nimport React, { type ForwardedRef, useEffect, useRef } from 'react';\n\n/**\n * NOTE: This utility will be moved to the new use-collection package.\n * Safe wrapper for React Aria's useObjectRef that handles test environments where refs are not extensible.\n *\n * **Critical for Vitest Browser Mode Testing**: When running tests in Vitest's browser mode with Playwright,\n * the test environment can freeze or make objects non-extensible. React Aria's `useObjectRef` attempts to\n * dynamically add properties to ref objects, which fails with \"Cannot add property current, object is not extensible\"\n * in these constrained test environments.\n *\n * **Technical Details:**\n * - Vitest browser mode uses Playwright's Chrome DevTools Protocol for test execution\n * - The V8 engine's security model can freeze objects during test isolation\n * - React Aria's useObjectRef uses `Object.defineProperty()` to add reactive properties to refs\n * - This conflicts with frozen objects in browser testing scenarios\n *\n * **Why This Solution Works:**\n * - Creates an internal ref that's always mutable (created in our controlled environment)\n * - Safely forwards values to the external ref using try/catch for frozen object scenarios\n * - Maintains the same ref forwarding behavior as React Aria's useObjectRef in normal environments\n * - Gracefully degrades in test environments without breaking functionality\n *\n * **Production Impact**: Zero. Object freezing only occurs in specific test configurations.\n * In production and development, this behaves identically to React Aria's useObjectRef.\n *\n * @template T - The type of the ref element\n * @param {React.ForwardedRef<T>} ref - The forwarded ref to handle safely\n * @returns {React.RefObject<T>} A safe ref object that works in all environments including frozen test contexts\n * @public\n */\nexport function useSafeObjectRef<T>(ref: ForwardedRef<T>): React.RefObject<T | null> {\n const internalRef = useRef<T>(null);\n\n useEffect(function updateForwardedRef() {\n const current = internalRef.current;\n\n if (typeof ref === 'function') {\n ref(current);\n } else if (ref && 'current' in ref) {\n try {\n (ref as React.MutableRefObject<T | null>).current = current;\n /* v8 ignore start */\n } catch {\n //\n // Silently ignore errors in test environments where objects might be frozen.\n // This specifically handles Vitest browser mode with Playwright where the V8 engine\n // may freeze ref objects during test isolation, preventing dynamic property assignment.\n //\n }\n /* v8 ignore stop */\n }\n });\n\n return internalRef;\n}\n","import type React from 'react';\nimport { createContext, useContext, useMemo, type ForwardedRef, type RefObject } from 'react';\nimport {\n FocusScope,\n mergeProps,\n useCollator,\n useLocale,\n useListBox,\n useFocusRing,\n ListKeyboardDelegate\n} from 'react-aria';\nimport { type ListState, type SelectionBehavior, useListState, type Orientation, type Node } from 'react-stately';\nimport { CollectionBuilder, Collection as AriaCollection } from '@react-aria/collections';\nimport type { AriaListBoxProps } from '@react-types/listbox';\nimport { useDataAttributes } from '@bento/use-data-attributes';\nimport { useProps } from '@bento/use-props';\nimport { withSlots, type Slots } from '@bento/slots';\nimport { ListBoxItemImpl } from './listbox-item.tsx';\nimport { ListBoxSectionInner } from './listbox-section.tsx';\nimport { useSafeObjectRef } from './utils.ts';\n\n/**\n * Render props provided to ListBox render functions and empty state renderers.\n * @interface ListBoxRenderProps\n */\nexport interface ListBoxRenderProps {\n /**\n * Whether the listbox has no items and should display its empty state.\n * @selector [data-empty]\n */\n readonly isEmpty: boolean;\n /**\n * Whether the listbox is currently focused.\n * @selector [data-focused]\n */\n readonly isFocused: boolean;\n /**\n * Whether the listbox is currently keyboard focused.\n * @selector [data-focus-visible]\n */\n readonly isFocusVisible: boolean;\n /**\n * Whether the listbox is currently the active drop target.\n * @selector [data-drop-target]\n */\n readonly isDropTarget: boolean;\n /**\n * Whether the items are arranged in a stack or grid.\n * @selector [data-layout=\"stack | grid\"]\n */\n readonly layout?: 'stack' | 'grid';\n /**\n * State of the listbox.\n */\n readonly state: ListState<unknown>;\n /**\n * The items array when using dynamic collections.\n */\n readonly items?: Iterable<unknown>;\n}\n\n/**\n * Props for the ListBox component.\n * @interface ListBoxProps\n * @template T The type of items in the collection\n */\nexport interface ListBoxProps<T>\n extends Omit<AriaListBoxProps<T>, 'label' | 'children'>,\n Omit<React.ComponentProps<'div'>, keyof AriaListBoxProps<T> | 'children'>,\n Slots {\n /**\n * How multiple selection should behave in the collection.\n */\n readonly selectionBehavior?: SelectionBehavior;\n /**\n * Provides content to display when there are no items in the list.\n */\n readonly renderEmptyState?: (props: ListBoxRenderProps) => React.ReactNode;\n /**\n * Whether the items are arranged in a stack layout.\n * @default 'stack'\n */\n readonly layout?: 'stack';\n /**\n * The primary orientation of the items. Usually this is the direction that the collection scrolls.\n * @default 'vertical'\n */\n readonly orientation?: Orientation;\n /**\n * Static children or render function for the ListBox.\n * When items prop is provided, children receives individual items for React Aria compatibility.\n * When no items prop is provided, children receives Bento render props { isEmpty, isFocused, state, etc. }.\n */\n readonly children?:\n | React.ReactNode\n | ((item: T) => React.ReactNode)\n | ((props: ListBoxRenderProps) => React.ReactNode);\n}\n\n/**\n * React context for sharing ListBox state across components.\n * This context provides the ListBox state to child components like ListBoxItem and ListBoxSection,\n * enabling them to access selection state, collection data, and other shared functionality.\n *\n * @context\n * @internal\n */\nconst ListStateContext = createContext<ListState<unknown> | null>(null);\n\n/**\n * Custom hook to manage ListBox state creation and context handling.\n * This hook either uses an existing state from context or creates a new one.\n * It's designed to work both as a standalone component and within a parent component\n * that provides ListBox state through context.\n *\n * @param {Record<string, unknown>} props - Configuration object for the ListBox state\n * @returns {object} An object containing the state instance and context state flag\n * @returns {ListState<unknown>} returns.state - The ListBox state instance\n * @returns {ListState<unknown> | null} returns.contextState - Existing context state, if any\n * @internal\n */\nfunction useListBoxState(props: Record<string, unknown>) {\n const contextState = useContext(ListStateContext);\n\n const stateProps = {\n ...props,\n children: undefined,\n items: undefined\n };\n\n const state = contextState ?? useListState(stateProps);\n\n return { state, contextState };\n}\n\n/**\n * Renders content with optional context provider wrapper.\n * If no context state exists, wraps the content in a ListStateContext.Provider.\n * This allows the ListBox to work both standalone and as part of a larger component tree.\n *\n * @param {React.ReactNode} content - The React content to render\n * @param {ListState<unknown>} state - The ListBox state to provide via context\n * @param {ListState<unknown> | null} contextState - Existing context state, if any\n * @returns {React.ReactNode} The content, optionally wrapped in a context provider\n * @internal\n */\nfunction renderWithOptionalContext(\n content: React.ReactNode,\n state: ListState<unknown>,\n contextState: ListState<unknown> | null\n): React.ReactNode {\n /* v8 ignore next */\n return contextState ? content : <ListStateContext.Provider value={state}>{content}</ListStateContext.Provider>;\n}\n\n/**\n * Creates and memoizes a keyboard delegate for the ListBox.\n * The keyboard delegate handles keyboard navigation logic, including\n * arrow key navigation, home/end keys, and type-ahead functionality.\n *\n * @param {object} config - Configuration object for the keyboard delegate\n * @param {ListState<unknown>['collection']} config.collection - The collection of items in the ListBox\n * @param {Intl.Collator} config.collator - Intl collator for string comparison in type-ahead\n * @param {React.RefObject<HTMLDivElement>} config.listBoxRef - Reference to the ListBox DOM element\n * @param {ListState<unknown>['selectionManager']} config.selectionManager - Selection manager from the state\n * @param {'stack' | 'grid'} [config.layout] - Layout mode (stack or grid)\n * @param {Orientation} [config.orientation] - Primary orientation of the items\n * @param {'ltr' | 'rtl'} config.direction - Text direction (ltr or rtl)\n * @param {ListKeyboardDelegate<unknown>} [config.keyboardDelegate] - Custom keyboard delegate to use instead of default\n * @returns {ListKeyboardDelegate<unknown>} A keyboard delegate instance for handling keyboard interactions\n * @internal\n */\nfunction useKeyboardDelegate({\n collection,\n collator,\n listBoxRef,\n selectionManager,\n layout,\n orientation,\n direction,\n keyboardDelegate: providedDelegate\n}: {\n readonly collection: ListState<unknown>['collection'];\n readonly collator: Intl.Collator;\n readonly listBoxRef: React.RefObject<HTMLDivElement | null>;\n readonly selectionManager: ListState<unknown>['selectionManager'];\n readonly layout?: 'stack' | 'grid';\n readonly orientation?: Orientation;\n readonly direction: 'ltr' | 'rtl';\n readonly keyboardDelegate?: ListKeyboardDelegate<unknown>;\n}): ListKeyboardDelegate<unknown> {\n const { disabledBehavior, disabledKeys } = selectionManager;\n\n return useMemo(\n function createKeyboardDelegate() {\n return (\n providedDelegate ||\n new ListKeyboardDelegate({\n collection,\n collator,\n ref: listBoxRef,\n disabledKeys,\n disabledBehavior,\n layout,\n orientation,\n direction\n })\n );\n },\n [collection, collator, listBoxRef, selectionManager, orientation, direction, layout, providedDelegate]\n );\n}\n\n/**\n * Generates data attributes for the ListBox element based on its current state.\n * These attributes are used for styling selectors and accessibility indicators.\n *\n * @param {object} config - Configuration object containing ListBox state flags\n * @param {boolean} config.isEmpty - Whether the listbox has no items\n * @param {boolean} config.isFocused - Whether the listbox is currently focused\n * @param {boolean} config.isFocusVisible - Whether focus should be visually indicated\n * @param {'stack' | 'grid'} [config.layout] - Layout mode (stack or grid)\n * @param {Orientation} [config.orientation] - Primary orientation of the items\n * @param {ListState<unknown>['selectionManager']} config.selectionManager - Selection manager containing selection state\n * @param {boolean} [config.allowsTabNavigation] - Whether tab navigation is enabled\n * @param {boolean} [config.shouldFocusWrap] - Whether focus wraps at boundaries\n * @param {SelectionBehavior} [config.originalSelectionBehavior] - Original selection behavior setting\n * @returns {Record<string, unknown>} Object with data attributes for the ListBox element\n * @internal\n */\nfunction useListBoxDataAttributes({\n isEmpty,\n isFocused,\n isFocusVisible,\n layout,\n orientation,\n selectionManager,\n allowsTabNavigation,\n shouldFocusWrap,\n originalSelectionBehavior\n}: {\n readonly isEmpty: boolean;\n readonly isFocused: boolean;\n readonly isFocusVisible: boolean;\n readonly layout?: 'stack' | 'grid';\n readonly orientation?: Orientation;\n readonly selectionManager: ListState<unknown>['selectionManager'];\n readonly allowsTabNavigation?: boolean;\n readonly shouldFocusWrap?: boolean;\n readonly originalSelectionBehavior?: SelectionBehavior;\n}) {\n return useDataAttributes({\n empty: isEmpty,\n focused: isFocused,\n 'focus-visible': isFocusVisible,\n layout,\n orientation,\n 'selection-mode': selectionManager.selectionMode !== 'none' ? selectionManager.selectionMode : undefined,\n 'selection-behavior': originalSelectionBehavior !== undefined ? selectionManager.selectionBehavior : undefined,\n 'allows-tab-navigation': allowsTabNavigation,\n 'focus-wrap': shouldFocusWrap\n });\n}\n\n/**\n * Composes all props for the ListBox element including DOM props, ARIA props,\n * focus props, and data attributes. Handles prop application through useProps\n * and manages ref assignment to avoid proxy extensibility issues.\n *\n * @param {object} config - Configuration object containing all props to compose\n * @param {Record<string, unknown>} config.otherProps - Additional props from the component\n * @param {ListBoxRenderProps} config.renderValues - Values available to render functions\n * @param {Record<string, unknown>} config.listBoxProps - Props from useListBox hook\n * @param {Record<string, unknown>} config.focusProps - Props from useFocusRing hook\n * @param {Record<string, unknown>} config.dataAttributes - Data attributes for styling/selectors\n * @param {ListState<unknown>['selectionManager']} config.selectionManager - Selection manager for ARIA attributes\n * @param {React.RefObject<HTMLDivElement>} config.listBoxRef - Reference to attach to the final element\n * @returns {Record<string, unknown>} Composed props object ready for the ListBox element\n * @internal\n */\nfunction useComposedProps({\n otherProps,\n renderValues,\n listBoxProps,\n focusProps,\n dataAttributes,\n selectionManager,\n listBoxRef\n}: {\n readonly otherProps: Record<string, unknown>;\n readonly renderValues: ListBoxRenderProps;\n readonly listBoxProps: Record<string, unknown>;\n readonly focusProps: Record<string, unknown>;\n readonly dataAttributes: Record<string, unknown>;\n readonly selectionManager: ListState<unknown>['selectionManager'];\n readonly listBoxRef: React.RefObject<HTMLDivElement | null>;\n}) {\n const { apply } = useProps(otherProps, renderValues);\n\n const propsToExclude = [\n 'renderEmptyState',\n 'selectionMode',\n 'defaultSelectedKeys',\n 'disabledKeys',\n 'disallowEmptySelection',\n 'shouldFocusWrap',\n 'items',\n 'children',\n 'selectionBehavior',\n 'keyboardDelegate'\n ];\n\n // Apply user props directly (preserves className, style, etc.)\n const appliedUserProps = apply(otherProps, propsToExclude);\n\n // React Aria and Bento props\n const baseProps = {\n ...mergeProps(listBoxProps, focusProps),\n ...dataAttributes,\n ...(selectionManager.selectionMode !== 'none' && {\n 'aria-multiselectable': selectionManager.selectionMode === 'multiple'\n })\n };\n\n //\n // Merge all props together with user props taking precedence\n //\n const finalProps = {\n ...baseProps,\n ...appliedUserProps,\n ref: listBoxRef // Set ref directly to avoid extensibility issues\n };\n\n return finalProps;\n}\n\n/**\n * Renders the empty state content for the ListBox when no items are present.\n * Handles both function-based render props and direct JSX elements.\n * If a function is provided, calls it with render values; otherwise returns as-is.\n *\n * @param {(props: ListBoxRenderProps) => React.ReactNode} renderEmptyStateFn - Function or JSX element to render for empty state\n * @param {ListBoxRenderProps} renderValues - Current render values to pass to render function\n * @returns {React.ReactNode} Rendered empty state content\n * @internal\n */\nfunction renderEmptyState(\n renderEmptyStateFn: (props: ListBoxRenderProps) => React.ReactNode,\n renderValues: ListBoxRenderProps\n): React.ReactNode {\n //\n // Handle cases where renderEmptyState is not a function (e.g., JSX element passed directly)\n //\n if (typeof renderEmptyStateFn === 'function') {\n return renderEmptyStateFn(renderValues);\n }\n //\n // If it's not a function, just return it as-is (likely a JSX element)\n //\n return renderEmptyStateFn as React.ReactNode;\n}\n\n/**\n * Renders all items in the collection as React elements.\n * Handles both regular items and section items, using the appropriate\n * components (ListBoxItemImpl for items, ListBoxSectionInner for sections).\n *\n * @param {ListState<unknown>['collection']} collection - The collection of items to render\n * @returns {React.ReactElement[]} Array of rendered React elements for all collection items\n * @internal\n */\nfunction renderCollectionItems(collection: ListState<unknown>['collection']): React.ReactElement[] {\n return [...collection].map(function renderCollectionItem(item: Node<unknown>) {\n return item.type === 'section' ? (\n <ListBoxSectionInner key={item.key} section={item} />\n ) : (\n <ListBoxItemImpl key={item.key} __node={item as Node<object>}>\n {item.rendered}\n </ListBoxItemImpl>\n );\n });\n}\n\n/**\n * Determines what content to render inside the ListBox based on its configuration.\n * Handles three cases:\n * 1. Function children without items (Bento render prop pattern with full render props)\n * 2. Empty state when no items and renderEmptyState is provided\n * 3. Normal collection rendering (including items with children functions for React Aria compatibility)\n *\n * @param {object} config - Configuration object for rendering\n * @param {React.ReactNode | ((item: unknown) => React.ReactNode) | ((props: ListBoxRenderProps) => React.ReactNode)} [config.children] - Children prop (static, item render function, or ListBox render prop)\n * @param {Iterable<unknown>} [config.items] - Items array for dynamic collections\n * @param {boolean} config.isEmpty - Whether the collection is empty\n * @param {(props: ListBoxRenderProps) => React.ReactNode} [config.renderEmptyStateProp] - Function to render empty state\n * @param {ListBoxRenderProps} config.renderValues - Current render values for render functions\n * @param {ListState<unknown>['collection']} config.collection - The collection state to render\n * @returns {React.ReactNode} The appropriate content to render inside the ListBox\n * @internal\n */\nfunction renderListBoxContent({\n children,\n items,\n isEmpty,\n renderEmptyStateProp,\n renderValues,\n collection\n}: {\n readonly children?:\n | React.ReactNode\n | ((item: unknown) => React.ReactNode)\n | ((props: ListBoxRenderProps) => React.ReactNode);\n readonly items?: Iterable<unknown>;\n readonly isEmpty: boolean;\n readonly renderEmptyStateProp?: (props: ListBoxRenderProps) => React.ReactNode;\n readonly renderValues: ListBoxRenderProps;\n readonly collection: ListState<unknown>['collection'];\n}): React.ReactNode {\n // If children is a function AND no items provided, use Bento render prop pattern\n /* v8 ignore next */\n const hasRenderChildren = typeof children === 'function' && !items;\n\n /* v8 ignore next 3 */\n if (hasRenderChildren) {\n return (children as (props: ListBoxRenderProps) => React.ReactNode)(renderValues);\n }\n\n // Handle empty state\n if (isEmpty && renderEmptyStateProp) {\n return renderEmptyState(renderEmptyStateProp, renderValues);\n }\n\n // Render collection items (React Aria handles items + children function internally)\n return renderCollectionItems(collection);\n}\n\n/**\n * Internal ListBox component that handles the core rendering logic.\n * This component manages all the hooks, state, and prop composition needed\n * for a fully functional ListBox. It's wrapped by the main ListBox component\n * which handles collection building.\n *\n * @param {object} props - Component props\n * @param {ListState<unknown>} props.state - The ListBox state instance\n * @param {(props: ListBoxRenderProps) => React.ReactNode} [props.renderEmptyState] - Function to render when no items are present\n * @param {React.ReactNode | ((item: unknown) => React.ReactNode) | ((props: ListBoxRenderProps) => React.ReactNode)} [props.children] - Static children, item render function, or ListBox render function\n * @param {Iterable<unknown>} [props.items] - Items array for dynamic collections\n * @param {React.RefObject<HTMLDivElement>} props.listBoxRef - Reference to the ListBox DOM element\n * @param {'stack'} [props.layout] - Layout mode (stack or grid)\n * @param {Orientation} [props.orientation] - Primary orientation of the items\n * @param {boolean} [props.shouldSelectOnPressUp] - Whether selection occurs on pointer up\n * @param {ListKeyboardDelegate<unknown>} [props.keyboardDelegate] - Custom keyboard navigation delegate\n * @param {boolean} [props.allowsTabNavigation] - Whether tab key navigates between items\n * @param {boolean} [props.shouldFocusWrap] - Whether focus wraps at boundaries\n * @param {'none' | 'single' | 'multiple'} [props.selectionMode] - Selection mode (none, single, multiple)\n * @param {SelectionBehavior} [props.selectionBehavior] - Selection behavior (toggle, replace)\n * @returns {React.ReactElement} A fully functional ListBox element with focus scope\n * @internal\n */\nconst ListBoxInner: React.FC<{\n readonly state: ListState<unknown>;\n readonly renderEmptyState?: (props: ListBoxRenderProps) => React.ReactNode;\n readonly children?:\n | React.ReactNode\n | ((item: unknown) => React.ReactNode)\n | ((props: ListBoxRenderProps) => React.ReactNode);\n readonly items?: Iterable<unknown>;\n readonly listBoxRef: RefObject<HTMLDivElement | null>;\n readonly layout?: 'stack';\n readonly orientation?: Orientation;\n readonly shouldSelectOnPressUp?: boolean;\n readonly keyboardDelegate?: ListKeyboardDelegate<unknown>;\n readonly allowsTabNavigation?: boolean;\n readonly shouldFocusWrap?: boolean;\n readonly selectionMode?: 'none' | 'single' | 'multiple';\n readonly selectionBehavior?: SelectionBehavior;\n}> = function ListBoxInner({\n state,\n renderEmptyState: renderEmptyStateProp,\n children,\n items,\n listBoxRef,\n ...otherProps\n}) {\n const { layout = 'stack', orientation = 'vertical', shouldSelectOnPressUp, selectionBehavior } = otherProps;\n\n const { collection, selectionManager } = state;\n const { direction } = useLocale();\n const collator = useCollator({ usage: 'search', sensitivity: 'base' });\n\n const keyboardDelegate = useKeyboardDelegate({\n collection,\n collator,\n listBoxRef,\n selectionManager,\n layout,\n orientation,\n direction,\n keyboardDelegate: otherProps.keyboardDelegate\n });\n\n const { listBoxProps } = useListBox(\n {\n ...otherProps,\n shouldSelectOnPressUp,\n keyboardDelegate\n },\n state,\n listBoxRef\n );\n\n const { focusProps, isFocused, isFocusVisible } = useFocusRing();\n const isEmpty = state.collection.size === 0;\n\n const renderValues: ListBoxRenderProps = {\n isEmpty,\n isFocused,\n isFocusVisible,\n isDropTarget: false,\n layout,\n state,\n items\n };\n\n const dataAttributes = useListBoxDataAttributes({\n isEmpty,\n isFocused,\n isFocusVisible,\n layout,\n orientation,\n selectionManager,\n allowsTabNavigation: otherProps.allowsTabNavigation,\n shouldFocusWrap: otherProps.shouldFocusWrap,\n originalSelectionBehavior: selectionBehavior\n });\n\n const composedProps = useComposedProps({\n otherProps,\n renderValues,\n listBoxProps: listBoxProps as Record<string, unknown>,\n focusProps: focusProps as Record<string, unknown>,\n dataAttributes,\n selectionManager,\n listBoxRef\n });\n\n return (\n <FocusScope>\n <div {...composedProps}>\n {renderListBoxContent({\n children,\n items,\n isEmpty,\n renderEmptyStateProp,\n renderValues,\n collection\n })}\n </div>\n </FocusScope>\n );\n};\n\n/**\n * A complete ListBox component providing accessible selection lists with keyboard navigation.\n * Supports both static children and dynamic collections, with single/multiple selection modes.\n * Built on React Aria with full ARIA compliance and keyboard accessibility.\n *\n * @component\n * @template T The type of items in the collection\n * @param {ListBoxProps<T>} args - The properties passed to the ListBox component\n * @param {React.ForwardedRef<HTMLDivElement>} ref - The ref to the listbox container\n * @returns {React.ReactElement} A ListBox component\n *\n * @example\n * ```tsx\n * <ListBox aria-label=\"Fruits\" selectionMode=\"single\">\n * <ListBoxItem id=\"apple\" textValue=\"Apple\">Apple</ListBoxItem>\n * <ListBoxItem id=\"banana\" textValue=\"Banana\">Banana</ListBoxItem>\n * </ListBox>\n * ```\n * @public\n */\nfunction ListBoxComponent<T>(...args: any[]): React.ReactElement {\n const [_args, ref] = args as [ListBoxProps<T>, React.ForwardedRef<HTMLDivElement>];\n return (\n <CollectionBuilder content={<AriaCollection {...(_args as unknown as Parameters<typeof AriaCollection>[0])} />}>\n {function buildCollection(collection: unknown) {\n return <StandaloneListBox props={_args as ListBoxProps<unknown>} listBoxRef={ref} collection={collection} />;\n }}\n </CollectionBuilder>\n );\n}\n\n/**\n * Standalone ListBox component that manages its own state and collection.\n * This component is used internally by the main ListBox component after\n * collection building is complete. It handles prop processing, state creation,\n * and context management.\n *\n * @param {object} props - Component props\n * @param {ListBoxProps<unknown>} props.props - The original ListBox props\n * @param {React.ForwardedRef<HTMLDivElement>} props.listBoxRef - Reference to forward to the ListBox element\n * @param {unknown} props.collection - Built collection from CollectionBuilder\n * @returns {React.ReactElement} A complete ListBox with state management and optional context wrapping\n * @internal\n */\nconst StandaloneListBox: React.FC<{\n readonly props: ListBoxProps<unknown>;\n readonly listBoxRef: ForwardedRef<HTMLDivElement>;\n readonly collection: unknown;\n}> = function StandaloneListBox({ props, listBoxRef, collection }) {\n //\n // Extract renderEmptyState before useProps processes it to avoid render prop corruption\n //\n const originalRenderEmptyState = props.renderEmptyState;\n\n const { props: processedProps } = useProps(props);\n const processedRef = useSafeObjectRef(listBoxRef);\n const { state, contextState } = useListBoxState({ ...processedProps, collection });\n\n const { renderEmptyState: _, ...cleanProcessedProps } = processedProps as ListBoxProps<unknown> & {\n renderEmptyState?: unknown;\n };\n\n const content = (\n <ListBoxInner\n state={state}\n listBoxRef={processedRef}\n renderEmptyState={originalRenderEmptyState}\n {...cleanProcessedProps}\n />\n );\n\n return renderWithOptionalContext(content, state, contextState);\n};\n\n/**\n * A complete ListBox component providing accessible selection lists with keyboard navigation.\n * Supports both static children and dynamic collections, with single/multiple selection modes.\n * Built on React Aria with full ARIA compliance and keyboard accessibility.\n *\n * @component\n * @example\n * ```tsx\n * <ListBox aria-label=\"Fruits\" selectionMode=\"single\">\n * <ListBoxItem id=\"apple\" textValue=\"Apple\">Apple</ListBoxItem>\n * <ListBoxItem id=\"banana\" textValue=\"Banana\">Banana</ListBoxItem>\n * </ListBox>\n * ```\n * @public\n */\nexport const ListBox = withSlots('BentoListBox', ListBoxComponent);\n\n/**\n * Collection component for building dynamic collections in ListBox.\n * Re-exported from React Aria Collections.\n * @public\n */\nexport { AriaCollection as Collection };\n\n/**\n * Context for sharing ListBox state across components.\n * Used internally by ListBoxItem and ListBoxSection components.\n * @public\n */\nexport { ListStateContext };\n","import React, { type ForwardedRef, type ReactNode, createContext, useContext, useMemo } from 'react';\nimport { mergeProps, useOption, useHover } from 'react-aria';\nimport { createLeafComponent } from '@react-aria/collections';\nimport type { HoverEvents, Key, LinkDOMProps, Node } from '@react-types/shared';\nimport { useDataAttributes } from '@bento/use-data-attributes';\nimport { useProps } from '@bento/use-props';\nimport { ListStateContext } from './listbox.tsx';\nimport { useSafeObjectRef } from './utils.ts';\nimport { withSlots } from '@bento/slots';\n\n/**\n * Context value structure for text-related slot attributes.\n * Used to provide label and description attributes to child components.\n * @interface TextContextValue\n */\ninterface TextContextValue {\n readonly slots: {\n /** Attributes for label elements */\n readonly label?: React.HTMLAttributes<HTMLElement>;\n /** Attributes for description elements */\n readonly description?: React.HTMLAttributes<HTMLElement>;\n };\n}\n\n/**\n * Internal context for providing text-related slot attributes to child components.\n * This context allows ListBoxItem to pass label and description attributes\n * to nested components that need them for accessibility.\n * @internal\n */\nconst TextContext = createContext<TextContextValue>({ slots: {} });\n\n/**\n * Render props provided to ListBoxItem render functions.\n * @interface ListBoxItemRenderProps\n */\nexport interface ListBoxItemRenderProps {\n /**\n * Whether the item is currently hovered.\n * @selector [data-hovered]\n */\n readonly isHovered: boolean;\n /**\n * Whether the item is currently pressed.\n * @selector [data-pressed]\n */\n readonly isPressed: boolean;\n /**\n * Whether the item is currently selected.\n * @selector [data-selected]\n */\n readonly isSelected: boolean;\n /**\n * Whether the item is currently focused.\n * @selector [data-focused]\n */\n readonly isFocused: boolean;\n /**\n * Whether the item is currently keyboard focused.\n * @selector [data-focus-visible]\n */\n readonly isFocusVisible: boolean;\n /**\n * Whether the item is disabled.\n * @selector [data-disabled]\n */\n readonly isDisabled: boolean;\n /**\n * The type of selection that is allowed in the collection.\n * @selector [data-selection-mode=\"none | single | multiple\"]\n */\n readonly selectionMode: 'none' | 'single' | 'multiple';\n /**\n * The selection behavior for the collection.\n * @selector [data-selection-behavior=\"toggle | replace\"]\n */\n readonly selectionBehavior: 'toggle' | 'replace';\n}\n\n/**\n * Props for the ListBoxItem component.\n * @interface ListBoxItemProps\n * @template T The type of the item value\n */\nexport interface ListBoxItemProps<T = object>\n extends LinkDOMProps,\n HoverEvents,\n Omit<React.HTMLAttributes<HTMLElement>, keyof LinkDOMProps | keyof HoverEvents | 'id' | 'children'> {\n /** The unique id of the item. If not provided, React Aria will auto-generate one. */\n readonly id?: Key;\n /** The object value that this item represents. When using dynamic collections, this is set automatically. */\n readonly value?: T;\n /** A string representation of the item's contents, used for features like typeahead. If not provided, React Aria will derive it from children automatically. */\n readonly textValue?: string;\n /**\n * Handler that is called when a user performs an action on the item. The exact user event depends on the\n * collection's `selectionBehavior` prop and the interaction modality.\n */\n readonly onAction?: () => void;\n /** The contents of the item. Can be a render function that receives render props. */\n readonly children?: ReactNode | ((values: ListBoxItemRenderProps) => ReactNode);\n /**\n * A slot name for the component. Used by Bento's slot system.\n */\n readonly slot?: string;\n /** Whether the item is disabled. */\n readonly isDisabled?: boolean;\n}\n\n/**\n * Internal implementation component for ListBoxItem.\n * Handles the core logic for rendering a single listbox item with proper accessibility.\n * This component manages all the hooks, state, and interactions needed for a functional\n * listbox item including selection, hover, focus, and keyboard interactions.\n *\n * @template T - The type of the item value\n * @param {object} props - Combined ListBoxItem props and internal node data\n * @param {Node<T>} props.__node - Internal React Aria node containing item metadata\n * @param {React.ReactNode | ((values: ListBoxItemRenderProps) => React.ReactNode)} [props.children] - Content to render, can be static or a render function\n * @param {boolean} [props.isDisabled] - Whether the item is disabled\n * @param {string} [props.aria-label] - ARIA label for accessibility\n * @param {(e: HoverEvent) => void} [props.onHoverStart] - Handler for hover start events\n * @param {(isHovering: boolean) => void} [props.onHoverChange] - Handler for hover change events\n * @param {(e: HoverEvent) => void} [props.onHoverEnd] - Handler for hover end events\n * @param {React.ForwardedRef<HTMLDivElement>} ref - Forwarded ref to the item element\n * @returns {React.ReactElement} A fully interactive listbox item with accessibility and state management\n * @internal\n */\nconst ListBoxItemImplComponent = function ListBoxItemImplComponent<T extends object>(...restArgs: any[]) {\n const [{ __node, ...props }, ref] = restArgs as [\n ListBoxItemProps<T> & { readonly __node: Node<T> },\n ForwardedRef<HTMLDivElement>\n ];\n const state = useContext(ListStateContext)!;\n const safeRef = useSafeObjectRef(ref);\n\n const { optionProps, labelProps, descriptionProps, ...states } = useOption(\n {\n key: __node.key,\n 'aria-label': props['aria-label'],\n isDisabled: props.isDisabled\n },\n state,\n safeRef\n );\n\n const { hoverProps, isHovered } = useHover({\n isDisabled: states.isDisabled,\n onHoverStart: props.onHoverStart,\n onHoverChange: props.onHoverChange,\n onHoverEnd: props.onHoverEnd\n });\n\n const renderValues: ListBoxItemRenderProps = {\n ...states,\n isHovered,\n selectionMode: state.selectionManager.selectionMode,\n selectionBehavior: state.selectionManager.selectionBehavior\n };\n\n const content = typeof props.children === 'function' ? props.children(renderValues) : props.children;\n\n const { apply } = useProps(props, renderValues);\n\n const dataAttributes = useDataAttributes({\n selected: states.isSelected,\n disabled: states.isDisabled,\n hovered: isHovered,\n focused: states.isFocused,\n 'focus-visible': states.isFocusVisible,\n pressed: states.isPressed,\n level: __node.level,\n 'selection-mode': state.selectionManager.selectionMode,\n 'selection-behavior': state.selectionManager.selectionBehavior\n });\n\n const textContext = useMemo(\n function createTextContext() {\n return {\n slots: {\n label: labelProps,\n description: descriptionProps\n }\n };\n },\n [labelProps, descriptionProps]\n );\n\n const ElementType = __node.props.href ? 'a' : 'div';\n\n // Use original node props (which contain className) not filtered finalProps\n const appliedUserProps = apply(__node.props, ['ref']);\n\n const finalAttributes = {\n ...mergeProps(optionProps, hoverProps), // React Aria props\n ...dataAttributes, // Bento data attributes\n ...appliedUserProps,\n ref: safeRef,\n 'data-text-value': __node.textValue\n };\n\n return (\n <TextContext.Provider value={textContext}>\n {React.createElement(ElementType, finalAttributes, content)}\n </TextContext.Provider>\n );\n};\n\n/**\n * Enhanced ListBoxItem implementation with slots support.\n * This wraps the core ListBoxItemImplComponent with Bento's slot system\n * for advanced composition and styling capabilities.\n * @internal\n */\nexport const ListBoxItemImpl = withSlots('BentoListBoxItem', ListBoxItemImplComponent);\n\n/**\n * Adapter component that connects ListBoxItemImpl to React Aria's collection system.\n * This function serves as a bridge between React Aria's createLeafComponent and\n * the internal ListBoxItemImpl, ensuring proper prop forwarding and node injection.\n *\n * @template T - The type of the item value\n * @param {ListBoxItemProps<T>} props - ListBoxItem component props\n * @param {React.ForwardedRef<HTMLDivElement>} forwardedRef - Ref forwarded from the collection system\n * @param {Node<T>} item - React Aria node containing item metadata and collection info\n * @returns {React.ReactElement} The ListBoxItemImpl component with proper node and ref wiring\n * @internal\n */\nfunction ListBoxItemComponent<T extends object>(\n props: ListBoxItemProps<T>,\n forwardedRef: ForwardedRef<HTMLDivElement>,\n item: Node<T>\n) {\n return <ListBoxItemImpl {...props} ref={forwardedRef as any} __node={item} />;\n}\n\n/**\n * Base ListBoxItem component created through React Aria's collection system.\n * This handles the connection to the parent ListBox's collection state.\n * @internal\n */\nconst ListBoxItemBase = createLeafComponent('item', ListBoxItemComponent);\n\n/**\n * A single item within a ListBox component.\n * Handles user interactions, accessibility, and state management for individual options.\n *\n * @component\n * @template T The type of the item value\n * @example\n * ```tsx\n * <ListBoxItem>Simple option</ListBoxItem>\n * ```\n * @public\n */\nexport const ListBoxItem = ListBoxItemBase;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bento/listbox",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Listbox component",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
|
-
"module": "./dist/index.
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
8
|
"scripts": {
|
|
9
|
-
"build": "
|
|
10
|
-
"dev": "
|
|
11
|
-
"lint": "biome lint &&
|
|
12
|
-
"
|
|
13
|
-
"prepublishOnly": "node ../../scripts/compile-readme.ts",
|
|
14
|
-
"pretest": "npm run build",
|
|
9
|
+
"build": "tsdown",
|
|
10
|
+
"dev": "tsdown --watch",
|
|
11
|
+
"lint": "biome lint && tsgo --noEmit",
|
|
12
|
+
"prepublishOnly": "node ../../../scripts/compile-readme.ts",
|
|
15
13
|
"test": "vitest --run",
|
|
16
|
-
"test:watch": "vitest"
|
|
14
|
+
"test:watch": "vitest",
|
|
15
|
+
"typecheck": "tsgo --noEmit -p tsconfig.json"
|
|
17
16
|
},
|
|
18
17
|
"repository": {
|
|
19
18
|
"type": "git",
|
|
20
|
-
"url": "git+https://github.com/godaddy/
|
|
19
|
+
"url": "git+https://github.com/godaddy/antares.git"
|
|
21
20
|
},
|
|
22
21
|
"keywords": [
|
|
23
22
|
"a11y",
|
|
@@ -34,9 +33,9 @@
|
|
|
34
33
|
"author": "GoDaddy Operating Company, LLC",
|
|
35
34
|
"license": "MIT",
|
|
36
35
|
"bugs": {
|
|
37
|
-
"url": "https://github.com/godaddy/
|
|
36
|
+
"url": "https://github.com/godaddy/antares/issues"
|
|
38
37
|
},
|
|
39
|
-
"homepage": "https://github.com/godaddy/
|
|
38
|
+
"homepage": "https://github.com/godaddy/antares#readme",
|
|
40
39
|
"files": [
|
|
41
40
|
"dist",
|
|
42
41
|
"src",
|
|
@@ -45,12 +44,21 @@
|
|
|
45
44
|
"dependencies": {
|
|
46
45
|
"@bento/slots": "^0.3.0",
|
|
47
46
|
"@bento/use-data-attributes": "^0.1.1",
|
|
48
|
-
"@bento/use-props": "^0.2.
|
|
49
|
-
"@react-aria/collections": "^3.
|
|
50
|
-
"@react-types/listbox": "^3.
|
|
51
|
-
"react-
|
|
52
|
-
"react-aria
|
|
53
|
-
"react-
|
|
47
|
+
"@bento/use-props": "^0.2.3",
|
|
48
|
+
"@react-aria/collections": "^3.1.0",
|
|
49
|
+
"@react-types/listbox": "^3.8.0",
|
|
50
|
+
"@react-types/shared": "^3.34.0",
|
|
51
|
+
"react-aria": "^3.48.0",
|
|
52
|
+
"react-aria-components": "^1.18.0",
|
|
53
|
+
"react-stately": "^3.46.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/react": "^19.2.15",
|
|
57
|
+
"@types/react-dom": "^19.2.3",
|
|
58
|
+
"tsdown": "^0.22.1",
|
|
59
|
+
"typescript": "^6.0.3",
|
|
60
|
+
"vitest": "^4.1.7",
|
|
61
|
+
"vitest-browser-react": "^2.2.0"
|
|
54
62
|
},
|
|
55
63
|
"peerDependencies": {
|
|
56
64
|
"react": "18.x || 19.x",
|
|
@@ -59,8 +67,8 @@
|
|
|
59
67
|
"exports": {
|
|
60
68
|
".": {
|
|
61
69
|
"import": {
|
|
62
|
-
"types": "./dist/index.d.
|
|
63
|
-
"default": "./dist/index.
|
|
70
|
+
"types": "./dist/index.d.mts",
|
|
71
|
+
"default": "./dist/index.mjs"
|
|
64
72
|
},
|
|
65
73
|
"require": {
|
|
66
74
|
"types": "./dist/index.d.cts",
|
package/src/header.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { createContext, useContext } from 'react';
|
|
2
3
|
import { createLeafComponent } from '@react-aria/collections';
|
|
3
4
|
import { useProps } from '@bento/use-props';
|
|
4
5
|
import { withSlots, type Slots } from '@bento/slots';
|
|
@@ -23,7 +24,7 @@ interface HeaderContextValue extends React.HTMLAttributes<HTMLElement> {
|
|
|
23
24
|
/**
|
|
24
25
|
* Reference to the header element for forwarding.
|
|
25
26
|
*/
|
|
26
|
-
readonly ref?: React.RefObject<HTMLDivElement>;
|
|
27
|
+
readonly ref?: React.RefObject<HTMLDivElement | null>;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
/**
|
|
@@ -50,27 +51,25 @@ export const HeaderContext = createContext<HeaderContextValue>({});
|
|
|
50
51
|
*
|
|
51
52
|
* @internal
|
|
52
53
|
*/
|
|
53
|
-
const BentoHeaderImpl = withSlots(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const contextProps = useContext(HeaderContext);
|
|
54
|
+
const BentoHeaderImpl = withSlots('BentoHeader', function BentoHeader(...args: any[]) {
|
|
55
|
+
const [props, ref] = args as [BentoHeaderProps, React.ForwardedRef<HTMLElement>];
|
|
56
|
+
const { props: processedProps, apply } = useProps(props);
|
|
57
|
+
const contextProps = useContext(HeaderContext);
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
// Apply user props directly (preserves className, style, etc.)
|
|
60
|
+
const appliedUserProps = apply(processedProps);
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
const composed = {
|
|
63
|
+
...contextProps,
|
|
64
|
+
...appliedUserProps // User props take precedence over context
|
|
65
|
+
};
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
);
|
|
67
|
+
return (
|
|
68
|
+
<header {...composed} ref={contextProps.ref || ref}>
|
|
69
|
+
{processedProps.children}
|
|
70
|
+
</header>
|
|
71
|
+
);
|
|
72
|
+
});
|
|
74
73
|
|
|
75
74
|
/**
|
|
76
75
|
* Wrapper component that connects the BentoHeaderImpl to React Aria's collection system.
|
package/src/listbox-item.tsx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import React, { ForwardedRef, ReactNode, createContext, useContext, useMemo } from 'react';
|
|
1
|
+
import React, { type ForwardedRef, type ReactNode, createContext, useContext, useMemo } from 'react';
|
|
2
2
|
import { mergeProps, useOption, useHover } from 'react-aria';
|
|
3
3
|
import { createLeafComponent } from '@react-aria/collections';
|
|
4
|
-
import { HoverEvents, Key, LinkDOMProps, Node } from '@react-types/shared';
|
|
4
|
+
import type { HoverEvents, Key, LinkDOMProps, Node } from '@react-types/shared';
|
|
5
5
|
import { useDataAttributes } from '@bento/use-data-attributes';
|
|
6
6
|
import { useProps } from '@bento/use-props';
|
|
7
|
-
import { ListStateContext } from './listbox';
|
|
8
|
-
import { useSafeObjectRef } from './utils';
|
|
7
|
+
import { ListStateContext } from './listbox.tsx';
|
|
8
|
+
import { useSafeObjectRef } from './utils.ts';
|
|
9
9
|
import { withSlots } from '@bento/slots';
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -126,10 +126,11 @@ export interface ListBoxItemProps<T = object>
|
|
|
126
126
|
* @returns {React.ReactElement} A fully interactive listbox item with accessibility and state management
|
|
127
127
|
* @internal
|
|
128
128
|
*/
|
|
129
|
-
const ListBoxItemImplComponent = function ListBoxItemImplComponent<T extends object>(
|
|
130
|
-
{ __node, ...props }
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
const ListBoxItemImplComponent = function ListBoxItemImplComponent<T extends object>(...restArgs: any[]) {
|
|
130
|
+
const [{ __node, ...props }, ref] = restArgs as [
|
|
131
|
+
ListBoxItemProps<T> & { readonly __node: Node<T> },
|
|
132
|
+
ForwardedRef<HTMLDivElement>
|
|
133
|
+
];
|
|
133
134
|
const state = useContext(ListStateContext)!;
|
|
134
135
|
const safeRef = useSafeObjectRef(ref);
|
|
135
136
|
|
package/src/listbox-section.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { useRef, useContext } from 'react';
|
|
2
3
|
import { useListBoxSection, mergeProps } from 'react-aria';
|
|
3
4
|
import { createBranchComponent } from '@react-aria/collections';
|
|
4
5
|
import { CollectionRendererContext } from 'react-aria-components';
|
|
@@ -6,8 +7,8 @@ import type { Node } from '@react-types/shared';
|
|
|
6
7
|
import { useDataAttributes } from '@bento/use-data-attributes';
|
|
7
8
|
import { useProps } from '@bento/use-props';
|
|
8
9
|
import { withSlots } from '@bento/slots';
|
|
9
|
-
import { HeaderContext } from './header';
|
|
10
|
-
import { ListStateContext } from './listbox';
|
|
10
|
+
import { HeaderContext } from './header.tsx';
|
|
11
|
+
import { ListStateContext } from './listbox.tsx';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Props for the ListBoxSection component.
|
|
@@ -64,7 +65,11 @@ interface ListBoxSectionInnerProps {
|
|
|
64
65
|
*/
|
|
65
66
|
const BentoListBoxSectionImpl = withSlots('BentoListBoxSection', function BentoListBoxSectionImpl<
|
|
66
67
|
T
|
|
67
|
-
>(
|
|
68
|
+
>(...restArgs: any[]) {
|
|
69
|
+
const [{ __node, children, title: titleProp, ...rest }, ref] = restArgs as [
|
|
70
|
+
BentoListBoxSectionImplProps<T>,
|
|
71
|
+
React.ForwardedRef<HTMLElement>
|
|
72
|
+
];
|
|
68
73
|
const { props, apply } = useProps(rest);
|
|
69
74
|
const data = useDataAttributes({ level: __node?.level });
|
|
70
75
|
const headingRef = useRef<HTMLDivElement>(null);
|
package/src/listbox.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { createContext, useContext, useMemo, type ForwardedRef, type RefObject } from 'react';
|
|
2
3
|
import {
|
|
3
4
|
FocusScope,
|
|
4
5
|
mergeProps,
|
|
@@ -8,15 +9,15 @@ import {
|
|
|
8
9
|
useFocusRing,
|
|
9
10
|
ListKeyboardDelegate
|
|
10
11
|
} from 'react-aria';
|
|
11
|
-
import { ListState, SelectionBehavior, useListState, Orientation, Node } from 'react-stately';
|
|
12
|
+
import { type ListState, type SelectionBehavior, useListState, type Orientation, type Node } from 'react-stately';
|
|
12
13
|
import { CollectionBuilder, Collection as AriaCollection } from '@react-aria/collections';
|
|
13
|
-
import { AriaListBoxProps } from '@react-types/listbox';
|
|
14
|
+
import type { AriaListBoxProps } from '@react-types/listbox';
|
|
14
15
|
import { useDataAttributes } from '@bento/use-data-attributes';
|
|
15
16
|
import { useProps } from '@bento/use-props';
|
|
16
|
-
import { withSlots, Slots } from '@bento/slots';
|
|
17
|
-
import { ListBoxItemImpl } from './listbox-item';
|
|
18
|
-
import { ListBoxSectionInner } from './listbox-section';
|
|
19
|
-
import { useSafeObjectRef } from './utils';
|
|
17
|
+
import { withSlots, type Slots } from '@bento/slots';
|
|
18
|
+
import { ListBoxItemImpl } from './listbox-item.tsx';
|
|
19
|
+
import { ListBoxSectionInner } from './listbox-section.tsx';
|
|
20
|
+
import { useSafeObjectRef } from './utils.ts';
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Render props provided to ListBox render functions and empty state renderers.
|
|
@@ -181,7 +182,7 @@ function useKeyboardDelegate({
|
|
|
181
182
|
}: {
|
|
182
183
|
readonly collection: ListState<unknown>['collection'];
|
|
183
184
|
readonly collator: Intl.Collator;
|
|
184
|
-
readonly listBoxRef: React.RefObject<HTMLDivElement>;
|
|
185
|
+
readonly listBoxRef: React.RefObject<HTMLDivElement | null>;
|
|
185
186
|
readonly selectionManager: ListState<unknown>['selectionManager'];
|
|
186
187
|
readonly layout?: 'stack' | 'grid';
|
|
187
188
|
readonly orientation?: Orientation;
|
|
@@ -292,7 +293,7 @@ function useComposedProps({
|
|
|
292
293
|
readonly focusProps: Record<string, unknown>;
|
|
293
294
|
readonly dataAttributes: Record<string, unknown>;
|
|
294
295
|
readonly selectionManager: ListState<unknown>['selectionManager'];
|
|
295
|
-
readonly listBoxRef: React.RefObject<HTMLDivElement>;
|
|
296
|
+
readonly listBoxRef: React.RefObject<HTMLDivElement | null>;
|
|
296
297
|
}) {
|
|
297
298
|
const { apply } = useProps(otherProps, renderValues);
|
|
298
299
|
|
|
@@ -464,7 +465,7 @@ const ListBoxInner: React.FC<{
|
|
|
464
465
|
| ((item: unknown) => React.ReactNode)
|
|
465
466
|
| ((props: ListBoxRenderProps) => React.ReactNode);
|
|
466
467
|
readonly items?: Iterable<unknown>;
|
|
467
|
-
readonly listBoxRef: RefObject<HTMLDivElement>;
|
|
468
|
+
readonly listBoxRef: RefObject<HTMLDivElement | null>;
|
|
468
469
|
readonly layout?: 'stack';
|
|
469
470
|
readonly orientation?: Orientation;
|
|
470
471
|
readonly shouldSelectOnPressUp?: boolean;
|
|
@@ -579,11 +580,12 @@ const ListBoxInner: React.FC<{
|
|
|
579
580
|
* ```
|
|
580
581
|
* @public
|
|
581
582
|
*/
|
|
582
|
-
function ListBoxComponent<T>(args:
|
|
583
|
+
function ListBoxComponent<T>(...args: any[]): React.ReactElement {
|
|
584
|
+
const [_args, ref] = args as [ListBoxProps<T>, React.ForwardedRef<HTMLDivElement>];
|
|
583
585
|
return (
|
|
584
|
-
<CollectionBuilder content={<AriaCollection {...(
|
|
586
|
+
<CollectionBuilder content={<AriaCollection {...(_args as unknown as Parameters<typeof AriaCollection>[0])} />}>
|
|
585
587
|
{function buildCollection(collection: unknown) {
|
|
586
|
-
return <StandaloneListBox props={
|
|
588
|
+
return <StandaloneListBox props={_args as ListBoxProps<unknown>} listBoxRef={ref} collection={collection} />;
|
|
587
589
|
}}
|
|
588
590
|
</CollectionBuilder>
|
|
589
591
|
);
|
package/src/utils.ts
CHANGED
|
@@ -30,7 +30,7 @@ import React, { type ForwardedRef, useEffect, useRef } from 'react';
|
|
|
30
30
|
* @returns {React.RefObject<T>} A safe ref object that works in all environments including frozen test contexts
|
|
31
31
|
* @public
|
|
32
32
|
*/
|
|
33
|
-
export function useSafeObjectRef<T>(ref: ForwardedRef<T>): React.RefObject<T> {
|
|
33
|
+
export function useSafeObjectRef<T>(ref: ForwardedRef<T>): React.RefObject<T | null> {
|
|
34
34
|
const internalRef = useRef<T>(null);
|
|
35
35
|
|
|
36
36
|
useEffect(function updateForwardedRef() {
|