mirror of
http://112.124.100.131/huang.ze/ebiz-dify-ai.git
synced 2025-12-09 19:06:51 +08:00
Feat/attachments (#9526)
Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: JzoNg <jzongcode@gmail.com>
This commit is contained in:
@@ -24,11 +24,12 @@ const defaultItems = [
|
||||
export type Item = {
|
||||
value: number | string
|
||||
name: string
|
||||
}
|
||||
} & Record<string, any>
|
||||
|
||||
export type ISelectProps = {
|
||||
className?: string
|
||||
wrapperClassName?: string
|
||||
renderTrigger?: (value: Item | null) => JSX.Element | null
|
||||
items?: Item[]
|
||||
defaultValue?: number | string
|
||||
disabled?: boolean
|
||||
@@ -37,7 +38,17 @@ export type ISelectProps = {
|
||||
bgClassName?: string
|
||||
placeholder?: string
|
||||
overlayClassName?: string
|
||||
optionWrapClassName?: string
|
||||
optionClassName?: string
|
||||
hideChecked?: boolean
|
||||
notClearable?: boolean
|
||||
renderOption?: ({
|
||||
item,
|
||||
selected,
|
||||
}: {
|
||||
item: Item
|
||||
selected: boolean
|
||||
}) => React.ReactNode
|
||||
}
|
||||
const Select: FC<ISelectProps> = ({
|
||||
className,
|
||||
@@ -49,6 +60,7 @@ const Select: FC<ISelectProps> = ({
|
||||
bgClassName = 'bg-gray-100',
|
||||
overlayClassName,
|
||||
optionClassName,
|
||||
renderOption,
|
||||
}) => {
|
||||
const [query, setQuery] = useState('')
|
||||
const [open, setOpen] = useState(false)
|
||||
@@ -61,6 +73,7 @@ const Select: FC<ISelectProps> = ({
|
||||
defaultSelect = existed
|
||||
|
||||
setSelectedItem(defaultSelect)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [defaultValue])
|
||||
|
||||
const filteredItems: Item[]
|
||||
@@ -99,7 +112,7 @@ const Select: FC<ISelectProps> = ({
|
||||
if (!disabled)
|
||||
setOpen(!open)
|
||||
}
|
||||
} className={classNames(optionClassName, `flex items-center h-9 w-full rounded-lg border-0 ${bgClassName} py-1.5 pl-3 pr-10 shadow-sm sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-gray-200 group-hover:bg-gray-200`)}>
|
||||
} className={classNames(`flex items-center h-9 w-full rounded-lg border-0 ${bgClassName} py-1.5 pl-3 pr-10 shadow-sm sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-gray-200 group-hover:bg-gray-200`, optionClassName)}>
|
||||
<div className='w-0 grow text-left truncate' title={selectedItem?.name}>{selectedItem?.name}</div>
|
||||
</Combobox.Button>}
|
||||
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none group-hover:bg-gray-200" onClick={
|
||||
@@ -120,24 +133,30 @@ const Select: FC<ISelectProps> = ({
|
||||
value={item}
|
||||
className={({ active }: { active: boolean }) =>
|
||||
classNames(
|
||||
optionClassName,
|
||||
'relative cursor-default select-none py-2 pl-3 pr-9 rounded-lg hover:bg-gray-100 text-gray-700',
|
||||
active ? 'bg-gray-100' : '',
|
||||
optionClassName,
|
||||
)
|
||||
}
|
||||
>
|
||||
{({ /* active, */ selected }) => (
|
||||
<>
|
||||
<span className={classNames('block', selected && 'font-normal')}>{item.name}</span>
|
||||
{selected && (
|
||||
<span
|
||||
className={classNames(
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4 text-gray-700',
|
||||
)}
|
||||
>
|
||||
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</span>
|
||||
)}
|
||||
{renderOption
|
||||
? renderOption({ item, selected })
|
||||
: (
|
||||
<>
|
||||
<span className={classNames('block', selected && 'font-normal')}>{item.name}</span>
|
||||
{selected && (
|
||||
<span
|
||||
className={classNames(
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4 text-gray-700',
|
||||
)}
|
||||
>
|
||||
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Combobox.Option>
|
||||
@@ -152,11 +171,17 @@ const Select: FC<ISelectProps> = ({
|
||||
const SimpleSelect: FC<ISelectProps> = ({
|
||||
className,
|
||||
wrapperClassName = '',
|
||||
renderTrigger,
|
||||
items = defaultItems,
|
||||
defaultValue = 1,
|
||||
disabled = false,
|
||||
onSelect,
|
||||
placeholder,
|
||||
optionWrapClassName,
|
||||
optionClassName,
|
||||
hideChecked,
|
||||
notClearable,
|
||||
renderOption,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const localPlaceholder = placeholder || t('common.placeholder.select')
|
||||
@@ -169,6 +194,7 @@ const SimpleSelect: FC<ISelectProps> = ({
|
||||
defaultSelect = existed
|
||||
|
||||
setSelectedItem(defaultSelect)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [defaultValue])
|
||||
|
||||
return (
|
||||
@@ -181,30 +207,34 @@ const SimpleSelect: FC<ISelectProps> = ({
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className={`relative h-9 ${wrapperClassName}`}>
|
||||
<Listbox.Button className={`w-full h-full rounded-lg border-0 bg-gray-100 py-1.5 pl-3 pr-10 sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-gray-200 group-hover:bg-gray-200 ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'} ${className}`}>
|
||||
<span className={classNames('block truncate text-left', !selectedItem?.name && 'text-gray-400')}>{selectedItem?.name ?? localPlaceholder}</span>
|
||||
<span className="absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
{selectedItem
|
||||
? (
|
||||
<XMarkIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setSelectedItem(null)
|
||||
onSelect({ name: '', value: '' })
|
||||
}}
|
||||
className="h-5 w-5 text-gray-400 cursor-pointer"
|
||||
aria-hidden="false"
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<ChevronDownIcon
|
||||
className="h-5 w-5 text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
</Listbox.Button>
|
||||
<div className={classNames('group/simple-select relative h-9', wrapperClassName)}>
|
||||
{renderTrigger && <Listbox.Button className='w-full'>{renderTrigger(selectedItem)}</Listbox.Button>}
|
||||
{!renderTrigger && (
|
||||
<Listbox.Button className={classNames(`flex items-center w-full h-full rounded-lg border-0 bg-gray-100 pl-3 pr-10 sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-gray-200 group-hover/simple-select:bg-state-base-hover-alt ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}`, className)}>
|
||||
<span className={classNames('block truncate text-left system-sm-regular text-components-input-text-filled', !selectedItem?.name && 'text-components-input-text-placeholder')}>{selectedItem?.name ?? localPlaceholder}</span>
|
||||
<span className="absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
{(selectedItem && !notClearable)
|
||||
? (
|
||||
<XMarkIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setSelectedItem(null)
|
||||
onSelect({ name: '', value: '' })
|
||||
}}
|
||||
className="h-4 w-4 text-text-quaternary cursor-pointer"
|
||||
aria-hidden="false"
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<ChevronDownIcon
|
||||
className="h-4 w-4 text-text-quaternary group-hover/simple-select:text-text-secondary"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
</Listbox.Button>
|
||||
)}
|
||||
|
||||
{!disabled && (
|
||||
<Transition
|
||||
as={Fragment}
|
||||
@@ -213,29 +243,35 @@ const SimpleSelect: FC<ISelectProps> = ({
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
||||
<Listbox.Options className="absolute z-10 mt-1 px-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg border-gray-200 border-[0.5px] focus:outline-none sm:text-sm">
|
||||
<Listbox.Options className={classNames('absolute z-10 mt-1 px-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg border-gray-200 border-[0.5px] focus:outline-none sm:text-sm', optionWrapClassName)}>
|
||||
{items.map((item: Item) => (
|
||||
<Listbox.Option
|
||||
key={item.value}
|
||||
className={({ active }) =>
|
||||
`relative cursor-pointer select-none py-2 pl-3 pr-9 rounded-lg hover:bg-gray-100 text-gray-700 ${active ? 'bg-gray-100' : ''
|
||||
}`
|
||||
classNames(
|
||||
`relative cursor-pointer select-none py-2 pl-3 pr-9 rounded-lg hover:bg-gray-100 text-gray-700 ${active ? 'bg-gray-100' : ''}`,
|
||||
optionClassName,
|
||||
)
|
||||
}
|
||||
value={item}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ /* active, */ selected }) => (
|
||||
<>
|
||||
<span className={classNames('block', selected && 'font-normal')}>{item.name}</span>
|
||||
{selected && (
|
||||
<span
|
||||
className={classNames(
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4 text-gray-700',
|
||||
{renderOption
|
||||
? renderOption({ item, selected })
|
||||
: (<>
|
||||
<span className={classNames('block', selected && 'font-normal')}>{item.name}</span>
|
||||
{selected && !hideChecked && (
|
||||
<span
|
||||
className={classNames(
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4 text-gray-700',
|
||||
)}
|
||||
>
|
||||
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</span>
|
||||
)}
|
||||
>
|
||||
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</span>
|
||||
)}
|
||||
</>)}
|
||||
</>
|
||||
)}
|
||||
</Listbox.Option>
|
||||
@@ -253,14 +289,26 @@ type PortalSelectProps = {
|
||||
onSelect: (value: Item) => void
|
||||
items: Item[]
|
||||
placeholder?: string
|
||||
renderTrigger?: (value?: Item) => JSX.Element | null
|
||||
triggerClassName?: string
|
||||
triggerClassNameFn?: (open: boolean) => string
|
||||
popupClassName?: string
|
||||
popupInnerClassName?: string
|
||||
readonly?: boolean
|
||||
hideChecked?: boolean
|
||||
}
|
||||
const PortalSelect: FC<PortalSelectProps> = ({
|
||||
value,
|
||||
onSelect,
|
||||
items,
|
||||
placeholder,
|
||||
renderTrigger,
|
||||
triggerClassName,
|
||||
triggerClassNameFn,
|
||||
popupClassName,
|
||||
popupInnerClassName,
|
||||
readonly,
|
||||
hideChecked,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
@@ -274,27 +322,32 @@ const PortalSelect: FC<PortalSelectProps> = ({
|
||||
placement='bottom-start'
|
||||
offset={4}
|
||||
>
|
||||
<PortalToFollowElemTrigger onClick={() => setOpen(v => !v)} className='w-full'>
|
||||
<div
|
||||
className={`
|
||||
flex items-center justify-between px-2.5 h-9 rounded-lg border-0 bg-gray-100 text-sm cursor-pointer
|
||||
`}
|
||||
title={selectedItem?.name}
|
||||
>
|
||||
<span
|
||||
className={`
|
||||
<PortalToFollowElemTrigger onClick={() => !readonly && setOpen(v => !v)} className='w-full'>
|
||||
{renderTrigger
|
||||
? renderTrigger(selectedItem)
|
||||
: (
|
||||
<div
|
||||
className={classNames(`
|
||||
flex items-center justify-between px-2.5 h-9 rounded-lg border-0 bg-gray-100 text-sm ${readonly ? 'cursor-not-allowed' : 'cursor-pointer'}
|
||||
`, triggerClassName, triggerClassNameFn?.(open))}
|
||||
title={selectedItem?.name}
|
||||
>
|
||||
<span
|
||||
className={`
|
||||
grow truncate
|
||||
${!selectedItem?.name && 'text-gray-400'}
|
||||
`}
|
||||
>
|
||||
{selectedItem?.name ?? localPlaceholder}
|
||||
</span>
|
||||
<ChevronDownIcon className='shrink-0 h-4 w-4 text-gray-400' />
|
||||
</div>
|
||||
>
|
||||
{selectedItem?.name ?? localPlaceholder}
|
||||
</span>
|
||||
<ChevronDownIcon className='shrink-0 h-4 w-4 text-gray-400' />
|
||||
</div>
|
||||
)}
|
||||
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className={`z-20 ${popupClassName}`}>
|
||||
<div
|
||||
className='px-1 py-1 max-h-60 overflow-auto rounded-md bg-white text-base shadow-lg border-gray-200 border-[0.5px] focus:outline-none sm:text-sm'
|
||||
className={classNames('px-1 py-1 max-h-60 overflow-auto rounded-md bg-white text-base shadow-lg border-gray-200 border-[0.5px] focus:outline-none sm:text-sm', popupInnerClassName)}
|
||||
>
|
||||
{items.map((item: Item) => (
|
||||
<div
|
||||
@@ -315,8 +368,8 @@ const PortalSelect: FC<PortalSelectProps> = ({
|
||||
>
|
||||
{item.name}
|
||||
</span>
|
||||
{item.value === value && (
|
||||
<CheckIcon className='shrink-0 h-4 w-4' />
|
||||
{!hideChecked && item.value === value && (
|
||||
<CheckIcon className='shrink-0 h-4 w-4 text-text-accent' />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user