import React from 'react'
import { type Selection, type SelectionMode } from '@react-types/shared'
import {
MenuTriggerConsumer,
MenuTriggerProvider,
SelectionConsumer,
SelectionProvider,
type MenuTriggerProviderContextType,
type MenuTriggerProviderProps,
type SelectionProviderContextType,
} from './context'
import { ListBox } from './subcomponents/ListBox'
import { ListBoxSection } from './subcomponents/ListBoxSection'
import { LoadMoreButton } from './subcomponents/LoadMoreButton'
import { MenuFooter, MenuLoadingSkeleton } from './subcomponents/MenuLayout'
import { MenuPopup, ResponsiveMenuPopup, type MenuPopupProps } from './subcomponents/MenuPopup'
import { MultiSelectOption } from './subcomponents/MultiSelectOption'
import { NoResults } from './subcomponents/NoResults'
import { SearchInput } from './subcomponents/SearchInput'
import { SectionDivider } from './subcomponents/SectionDivider'
import { ClearButton, SelectAllButton } from './subcomponents/SelectionControlButton'
import { FilterTriggerButton, RemovableFilterTrigger } from './subcomponents/Trigger'
import { type ItemType } from './types'
type SelectionProps = {
label: string // provide A11y context for listbox
items: ItemType[]
selectedKeys?: Selection
defaultSelectedKeys?: Selection
onSelectionChange?: (keys: Selection) => void
selectionMode?: SelectionMode
onSearchInputChange?: (searchInput: string) => void
}
export type FilterMultiSelectProps = {
trigger: (value?: MenuTriggerProviderContextType) => React.ReactNode
children: (value?: SelectionProviderContextType) => React.ReactNode // the content of the menu
/** Replaces the MenuPopup. Should only be used for changing how the floating element is positioned, ie: with the `` primitive. */
customMenuPopup?: React.ComponentType
triggerRef?: React.RefObject
className?: string
} & Omit &
Omit &
SelectionProps
export const FilterMultiSelect = ({
trigger,
children,
customMenuPopup,
isOpen,
defaultOpen,
onOpenChange,
isLoading,
loadingSkeleton,
label,
items,
selectedKeys,
defaultSelectedKeys,
onSelectionChange,
selectionMode = 'multiple',
onSearchInputChange,
triggerRef,
className,
...restProps
}: FilterMultiSelectProps): JSX.Element => {
const MenuComponent = customMenuPopup ?? MenuPopup
const menuTriggerProps = { isOpen, defaultOpen, onOpenChange, triggerRef }
const menuPopupProps = { isLoading, loadingSkeleton, ...restProps }
const disabledKeys: Selection = new Set(
items?.filter((item) => item.isDisabled === true).map((disabledItem) => disabledItem.value),
)
const selectionProps = {
label,
items,
selectedKeys,
defaultSelectedKeys,
onSelectionChange,
selectionMode,
disabledKeys,
onSearchInputChange,
}
return (
{trigger}
{children}
)
}
FilterMultiSelect.displayName = 'FilterMultiSelect'
FilterMultiSelect.TriggerButton = FilterTriggerButton
FilterMultiSelect.RemovableTrigger = RemovableFilterTrigger
FilterMultiSelect.SearchInput = SearchInput
FilterMultiSelect.ListBox = ListBox
FilterMultiSelect.ListBoxSection = ListBoxSection
FilterMultiSelect.SectionDivider = SectionDivider
FilterMultiSelect.Option = MultiSelectOption
FilterMultiSelect.SelectAllButton = SelectAllButton
FilterMultiSelect.ClearButton = ClearButton
FilterMultiSelect.MenuFooter = MenuFooter // For layout
FilterMultiSelect.MenuLoadingSkeleton = MenuLoadingSkeleton // Menu Loading Skeleton example
FilterMultiSelect.LoadMoreButton = LoadMoreButton
FilterMultiSelect.NoResults = NoResults
FilterMultiSelect.ResponsiveMenuPopup = ResponsiveMenuPopup