298 lines
7.0 KiB
TypeScript
298 lines
7.0 KiB
TypeScript
"use client"
|
|
|
|
import * as React from "react"
|
|
import { cn } from "cn"
|
|
import {
|
|
Button as ButtonPrimitive,
|
|
composeRenderProps,
|
|
Header as HeaderPrimitive,
|
|
ListBoxItem as ListBoxItemPrimitive,
|
|
ListBox as ListBoxPrimitive,
|
|
ListBoxSection as ListBoxSectionPrimitive,
|
|
Popover as PopoverPrimitive,
|
|
SearchField,
|
|
Select as SelectPrimitive,
|
|
SelectValue as SelectValuePrimitive,
|
|
Separator as SeparatorPrimitive,
|
|
type ListBoxProps,
|
|
type SearchFieldProps,
|
|
type ListBoxSectionProps as SelectGroupProps,
|
|
type SelectProps,
|
|
type SelectValueProps,
|
|
} from "react-aria-components"
|
|
|
|
import {
|
|
InputGroup,
|
|
InputGroupAddon,
|
|
InputGroupInput,
|
|
} from "@/registry/bases/aria/ui/input-group"
|
|
import { IconPlaceholder } from "@/app/(create)/components/icon-placeholder"
|
|
|
|
function Select<T extends object, M extends "single" | "multiple" = "single">({
|
|
className,
|
|
...props
|
|
}: SelectProps<T, M>) {
|
|
return (
|
|
<SelectPrimitive
|
|
data-slot="select"
|
|
className={cn("w-fit", className)}
|
|
{...props}
|
|
/>
|
|
)
|
|
}
|
|
|
|
function SelectGroup<T extends object>({
|
|
className,
|
|
...props
|
|
}: SelectGroupProps<T>) {
|
|
return (
|
|
<ListBoxSectionPrimitive
|
|
data-slot="select-group"
|
|
className={cn("cn-select-group", className)}
|
|
{...props}
|
|
/>
|
|
)
|
|
}
|
|
|
|
function SelectValue<T extends object>({
|
|
className,
|
|
children,
|
|
...props
|
|
}: SelectValueProps<T>) {
|
|
return (
|
|
<SelectValuePrimitive
|
|
data-slot="select-value"
|
|
className={cn("cn-select-value cn-select-value-aria", className)}
|
|
{...props}
|
|
>
|
|
{typeof children === "function"
|
|
? children
|
|
: ({ selectedItems, selectedText, defaultChildren }) =>
|
|
selectedItems.length > 1 ? selectedText : defaultChildren}
|
|
</SelectValuePrimitive>
|
|
)
|
|
}
|
|
|
|
function SelectTrigger({
|
|
className,
|
|
size = "default",
|
|
children,
|
|
...props
|
|
}: Omit<React.ComponentProps<typeof ButtonPrimitive>, "children"> & {
|
|
children?: React.ReactNode
|
|
size?: "sm" | "default"
|
|
}) {
|
|
return (
|
|
<ButtonPrimitive
|
|
data-slot="select-trigger"
|
|
data-size={size}
|
|
className={cn(
|
|
"cn-select-trigger flex w-full items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
className
|
|
)}
|
|
{...props}
|
|
>
|
|
{children}
|
|
<IconPlaceholder
|
|
lucide="ChevronDownIcon"
|
|
tabler="IconSelector"
|
|
hugeicons="UnfoldMoreIcon"
|
|
phosphor="CaretDownIcon"
|
|
remixicon="RiArrowDownSLine"
|
|
className="cn-select-trigger-icon pointer-events-none"
|
|
/>
|
|
</ButtonPrimitive>
|
|
)
|
|
}
|
|
|
|
function SelectContent({
|
|
className,
|
|
children,
|
|
placement = "bottom",
|
|
offset = 4,
|
|
crossOffset = 0,
|
|
...props
|
|
}: Omit<
|
|
React.ComponentProps<typeof PopoverPrimitive>,
|
|
"className" | "children"
|
|
> & {
|
|
className?: string
|
|
children?: React.ReactNode
|
|
}) {
|
|
return (
|
|
<SelectPopover
|
|
className={className}
|
|
placement={placement}
|
|
offset={offset}
|
|
crossOffset={crossOffset}
|
|
{...props}
|
|
>
|
|
<SelectList>{children}</SelectList>
|
|
</SelectPopover>
|
|
)
|
|
}
|
|
|
|
function SelectPopover({
|
|
className,
|
|
children,
|
|
placement = "bottom start",
|
|
offset = 4,
|
|
crossOffset = 0,
|
|
...props
|
|
}: Omit<
|
|
React.ComponentProps<typeof PopoverPrimitive>,
|
|
"className" | "children"
|
|
> & {
|
|
className?: string
|
|
children?: React.ReactNode
|
|
}) {
|
|
return (
|
|
<PopoverPrimitive
|
|
data-slot="select-content"
|
|
placement={placement}
|
|
offset={offset}
|
|
crossOffset={crossOffset}
|
|
className={cn(
|
|
"cn-select-content-aria cn-menu-target cn-menu-translucent cn-menu-translucent-aria relative isolate z-50 w-(--trigger-width) origin-(--trigger-anchor-point) overflow-hidden",
|
|
className
|
|
)}
|
|
{...props}
|
|
>
|
|
{children}
|
|
</PopoverPrimitive>
|
|
)
|
|
}
|
|
|
|
function SelectList<T extends object>({
|
|
className,
|
|
...props
|
|
}: ListBoxProps<T>) {
|
|
return (
|
|
<ListBoxPrimitive
|
|
data-slot="select-list"
|
|
className={cn(
|
|
"group/select-list max-h-[inherit] overflow-x-hidden overflow-y-auto p-0 outline-hidden",
|
|
className
|
|
)}
|
|
{...props}
|
|
/>
|
|
)
|
|
}
|
|
|
|
function SelectInput({ className, ...props }: SearchFieldProps) {
|
|
return (
|
|
<SearchField
|
|
{...props}
|
|
autoFocus
|
|
data-slot="select-input-wrapper"
|
|
className={cn("p-1 pb-0", className)}
|
|
>
|
|
<InputGroup>
|
|
<InputGroupInput
|
|
data-slot="select-input"
|
|
className="[&::-webkit-search-cancel-button]:hidden"
|
|
/>
|
|
<InputGroupAddon>
|
|
<IconPlaceholder
|
|
lucide="SearchIcon"
|
|
tabler="IconSearch"
|
|
hugeicons="SearchIcon"
|
|
phosphor="MagnifyingGlassIcon"
|
|
remixicon="RiSearchLine"
|
|
className="cn-command-input-icon"
|
|
/>
|
|
</InputGroupAddon>
|
|
</InputGroup>
|
|
</SearchField>
|
|
)
|
|
}
|
|
|
|
function SelectLabel({
|
|
className,
|
|
...props
|
|
}: React.ComponentProps<typeof HeaderPrimitive>) {
|
|
return (
|
|
<HeaderPrimitive
|
|
data-slot="select-label"
|
|
className={cn("cn-select-label", className)}
|
|
{...props}
|
|
/>
|
|
)
|
|
}
|
|
|
|
function SelectItem({
|
|
className,
|
|
children,
|
|
...props
|
|
}: React.ComponentProps<typeof ListBoxItemPrimitive>) {
|
|
return (
|
|
<ListBoxItemPrimitive
|
|
data-slot="select-item"
|
|
textValue={typeof children === "string" ? children : undefined}
|
|
className={cn(
|
|
"cn-select-item cn-select-item-aria relative flex w-full cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
className
|
|
)}
|
|
{...props}
|
|
>
|
|
{composeRenderProps(children, (children, { isSelected }) => (
|
|
<>
|
|
<span className="cn-select-item-text shrink-0 whitespace-nowrap">
|
|
{children}
|
|
</span>
|
|
<span className="cn-select-item-indicator">
|
|
{isSelected ? (
|
|
<IconPlaceholder
|
|
lucide="CheckIcon"
|
|
tabler="IconCheck"
|
|
hugeicons="Tick02Icon"
|
|
phosphor="CheckIcon"
|
|
remixicon="RiCheckLine"
|
|
className="cn-select-item-indicator-icon pointer-events-none"
|
|
/>
|
|
) : null}
|
|
</span>
|
|
</>
|
|
))}
|
|
</ListBoxItemPrimitive>
|
|
)
|
|
}
|
|
|
|
function SelectSeparator({
|
|
className,
|
|
...props
|
|
}: React.ComponentProps<typeof SeparatorPrimitive>) {
|
|
return (
|
|
<SeparatorPrimitive
|
|
data-slot="select-separator"
|
|
className={cn("cn-select-separator pointer-events-none", className)}
|
|
{...props}
|
|
/>
|
|
)
|
|
}
|
|
|
|
function SelectEmpty({ className, ...props }: React.ComponentProps<"div">) {
|
|
return (
|
|
<div
|
|
data-slot="select-empty"
|
|
className={cn("cn-select-empty-aria", className)}
|
|
{...props}
|
|
/>
|
|
)
|
|
}
|
|
|
|
export {
|
|
Select,
|
|
SelectContent,
|
|
SelectGroup,
|
|
SelectInput,
|
|
SelectItem,
|
|
SelectLabel,
|
|
SelectList,
|
|
SelectPopover,
|
|
SelectSeparator,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
SelectEmpty,
|
|
}
|