Feat/attachments (#9526)

Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: JzoNg <jzongcode@gmail.com>
This commit is contained in:
zxhlyh
2024-10-21 10:32:37 +08:00
committed by GitHub
parent 4fd2743efa
commit 7a1d6fe509
445 changed files with 11759 additions and 6922 deletions

View File

@@ -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>
))}