mirror of
https://github.com/langgenius/dify.git
synced 2026-04-17 03:16:33 +08:00
191 lines
5.4 KiB
TypeScript
191 lines
5.4 KiB
TypeScript
'use client'
|
|
|
|
import type { ReactNode } from 'react'
|
|
import type { Placement } from '@/app/components/base/ui/placement'
|
|
import { Select as BaseSelect } from '@base-ui/react/select'
|
|
import { cn } from '@langgenius/dify-ui/cn'
|
|
import {
|
|
overlayLabelClassName,
|
|
overlaySeparatorClassName,
|
|
} from '@/app/components/base/ui/overlay-shared'
|
|
import { parsePlacement } from '@/app/components/base/ui/placement'
|
|
|
|
export const Select = BaseSelect.Root
|
|
export const SelectValue = BaseSelect.Value
|
|
/** @public */
|
|
export const SelectGroup = BaseSelect.Group
|
|
|
|
const selectSizeClassName: Record<string, string> = {
|
|
small: 'h-6 gap-px rounded-md px-2 py-1 system-xs-regular',
|
|
medium: 'h-8 gap-0.5 rounded-lg px-3 py-2 system-sm-regular',
|
|
large: 'h-9 gap-0.5 rounded-[10px] px-4 py-2 system-md-regular',
|
|
}
|
|
|
|
type SelectTriggerProps = BaseSelect.Trigger.Props & {
|
|
size?: 'small' | 'medium' | 'large'
|
|
}
|
|
|
|
export function SelectTrigger({
|
|
className,
|
|
children,
|
|
size = 'medium',
|
|
...props
|
|
}: SelectTriggerProps) {
|
|
return (
|
|
<BaseSelect.Trigger
|
|
className={cn(
|
|
'group flex w-full items-center border-0 bg-components-input-bg-normal text-left text-components-input-text-filled outline-hidden',
|
|
'hover:bg-state-base-hover-alt focus-visible:bg-state-base-hover-alt',
|
|
'data-placeholder:text-components-input-text-placeholder',
|
|
selectSizeClassName[size],
|
|
'data-readonly:cursor-default data-readonly:bg-transparent data-readonly:hover:bg-transparent',
|
|
'data-disabled:cursor-not-allowed data-disabled:bg-components-input-bg-disabled data-disabled:text-components-input-text-filled-disabled data-disabled:hover:bg-components-input-bg-disabled',
|
|
'data-disabled:data-placeholder:text-components-input-text-disabled',
|
|
className,
|
|
)}
|
|
{...props}
|
|
>
|
|
<span className="min-w-0 grow truncate">
|
|
{children}
|
|
</span>
|
|
<BaseSelect.Icon className="shrink-0 text-text-quaternary transition-colors group-hover:text-text-secondary group-data-readonly:hidden data-open:text-text-secondary">
|
|
<span className="i-ri-arrow-down-s-line h-4 w-4" aria-hidden="true" />
|
|
</BaseSelect.Icon>
|
|
</BaseSelect.Trigger>
|
|
)
|
|
}
|
|
|
|
export function SelectLabel({
|
|
className,
|
|
...props
|
|
}: BaseSelect.GroupLabel.Props) {
|
|
return (
|
|
<BaseSelect.GroupLabel
|
|
className={cn(overlayLabelClassName, className)}
|
|
{...props}
|
|
/>
|
|
)
|
|
}
|
|
|
|
/** @public */
|
|
export function SelectSeparator({
|
|
className,
|
|
...props
|
|
}: BaseSelect.Separator.Props) {
|
|
return (
|
|
<BaseSelect.Separator
|
|
className={cn(overlaySeparatorClassName, className)}
|
|
{...props}
|
|
/>
|
|
)
|
|
}
|
|
|
|
type SelectContentProps = {
|
|
children: ReactNode
|
|
placement?: Placement
|
|
sideOffset?: number
|
|
alignOffset?: number
|
|
className?: string
|
|
popupClassName?: string
|
|
listClassName?: string
|
|
positionerProps?: Omit<
|
|
BaseSelect.Positioner.Props,
|
|
'children' | 'className' | 'side' | 'align' | 'sideOffset' | 'alignOffset'
|
|
>
|
|
popupProps?: Omit<
|
|
BaseSelect.Popup.Props,
|
|
'children' | 'className'
|
|
>
|
|
listProps?: Omit<
|
|
BaseSelect.List.Props,
|
|
'children' | 'className'
|
|
>
|
|
}
|
|
|
|
export function SelectContent({
|
|
children,
|
|
placement = 'bottom-start',
|
|
sideOffset = 4,
|
|
alignOffset = 0,
|
|
className,
|
|
popupClassName,
|
|
listClassName,
|
|
positionerProps,
|
|
popupProps,
|
|
listProps,
|
|
}: SelectContentProps) {
|
|
const { side, align } = parsePlacement(placement)
|
|
|
|
return (
|
|
<BaseSelect.Portal>
|
|
<BaseSelect.Positioner
|
|
side={side}
|
|
align={align}
|
|
sideOffset={sideOffset}
|
|
alignOffset={alignOffset}
|
|
alignItemWithTrigger={false}
|
|
className={cn('z-1002 outline-hidden', className)}
|
|
{...positionerProps}
|
|
>
|
|
<BaseSelect.Popup
|
|
className={cn(
|
|
'min-w-(--anchor-width) rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg',
|
|
'origin-(--transform-origin) transition-[transform,scale,opacity] data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 motion-reduce:transition-none',
|
|
popupClassName,
|
|
)}
|
|
{...popupProps}
|
|
>
|
|
<BaseSelect.List
|
|
className={cn('max-h-80 overflow-auto p-1 outline-hidden', listClassName)}
|
|
{...listProps}
|
|
>
|
|
{children}
|
|
</BaseSelect.List>
|
|
</BaseSelect.Popup>
|
|
</BaseSelect.Positioner>
|
|
</BaseSelect.Portal>
|
|
)
|
|
}
|
|
|
|
export function SelectItem({
|
|
className,
|
|
...props
|
|
}: BaseSelect.Item.Props) {
|
|
return (
|
|
<BaseSelect.Item
|
|
className={cn(
|
|
'flex h-8 cursor-pointer items-center rounded-lg px-2 system-sm-medium text-text-secondary outline-hidden',
|
|
'data-disabled:cursor-not-allowed data-disabled:opacity-50 data-highlighted:bg-state-base-hover',
|
|
className,
|
|
)}
|
|
{...props}
|
|
/>
|
|
)
|
|
}
|
|
|
|
export function SelectItemText({
|
|
className,
|
|
...props
|
|
}: BaseSelect.ItemText.Props) {
|
|
return (
|
|
<BaseSelect.ItemText
|
|
className={cn('mr-1 min-w-0 grow truncate px-1', className)}
|
|
{...props}
|
|
/>
|
|
)
|
|
}
|
|
|
|
export function SelectItemIndicator({
|
|
className,
|
|
...props
|
|
}: Omit<BaseSelect.ItemIndicator.Props, 'children'>) {
|
|
return (
|
|
<BaseSelect.ItemIndicator
|
|
className={cn('ml-auto flex shrink-0 items-center text-text-accent', className)}
|
|
{...props}
|
|
>
|
|
<span className="i-ri-check-line h-4 w-4" aria-hidden />
|
|
</BaseSelect.ItemIndicator>
|
|
)
|
|
}
|