mirror of
https://github.com/langgenius/dify.git
synced 2024-11-16 11:42:29 +08:00
5ee7e03c1b
Co-authored-by: billsyli <billsyli@tencent.com>
122 lines
3.6 KiB
TypeScript
122 lines
3.6 KiB
TypeScript
import { Popover, Transition } from '@headlessui/react'
|
|
import { Fragment, cloneElement, useRef } from 'react'
|
|
import s from './style.module.css'
|
|
import cn from '@/utils/classnames'
|
|
|
|
export type HtmlContentProps = {
|
|
onClose?: () => void
|
|
onClick?: () => void
|
|
}
|
|
|
|
type IPopover = {
|
|
className?: string
|
|
htmlContent: React.ReactElement<HtmlContentProps>
|
|
popupClassName?: string
|
|
trigger?: 'click' | 'hover'
|
|
position?: 'bottom' | 'br' | 'bl'
|
|
btnElement?: string | React.ReactNode
|
|
btnClassName?: string | ((open: boolean) => string)
|
|
manualClose?: boolean
|
|
disabled?: boolean
|
|
}
|
|
|
|
const timeoutDuration = 100
|
|
|
|
export default function CustomPopover({
|
|
trigger = 'hover',
|
|
position = 'bottom',
|
|
htmlContent,
|
|
popupClassName,
|
|
btnElement,
|
|
className,
|
|
btnClassName,
|
|
manualClose,
|
|
disabled = false,
|
|
}: IPopover) {
|
|
const buttonRef = useRef<HTMLButtonElement>(null)
|
|
const timeOutRef = useRef<NodeJS.Timeout | null>(null)
|
|
|
|
const onMouseEnter = (isOpen: boolean) => {
|
|
timeOutRef.current && clearTimeout(timeOutRef.current)
|
|
!isOpen && buttonRef.current?.click()
|
|
}
|
|
|
|
const onMouseLeave = (isOpen: boolean) => {
|
|
timeOutRef.current = setTimeout(() => {
|
|
isOpen && buttonRef.current?.click()
|
|
}, timeoutDuration)
|
|
}
|
|
|
|
return (
|
|
<Popover className="relative">
|
|
{({ open }: { open: boolean }) => {
|
|
return (
|
|
<>
|
|
<div
|
|
{...(trigger !== 'hover'
|
|
? {}
|
|
: {
|
|
onMouseLeave: () => onMouseLeave(open),
|
|
onMouseEnter: () => onMouseEnter(open),
|
|
})}
|
|
>
|
|
<Popover.Button
|
|
ref={buttonRef}
|
|
disabled={disabled}
|
|
className={`group ${s.popupBtn} ${open ? '' : 'bg-gray-100'} ${!btnClassName
|
|
? ''
|
|
: typeof btnClassName === 'string'
|
|
? btnClassName
|
|
: btnClassName?.(open)
|
|
}`}
|
|
>
|
|
{btnElement}
|
|
</Popover.Button>
|
|
<Transition as={Fragment}>
|
|
<Popover.Panel
|
|
className={cn(
|
|
s.popupPanel,
|
|
position === 'bottom' && '-translate-x-1/2 left-1/2',
|
|
position === 'bl' && 'left-0',
|
|
position === 'br' && 'right-0',
|
|
className,
|
|
)}
|
|
{...(trigger !== 'hover'
|
|
? {}
|
|
: {
|
|
onMouseLeave: () => onMouseLeave(open),
|
|
onMouseEnter: () => onMouseEnter(open),
|
|
})
|
|
}
|
|
>
|
|
{({ close }) => (
|
|
<div
|
|
className={cn(s.panelContainer, popupClassName)}
|
|
{...(trigger !== 'hover'
|
|
? {}
|
|
: {
|
|
onMouseLeave: () => onMouseLeave(open),
|
|
onMouseEnter: () => onMouseEnter(open),
|
|
})
|
|
}
|
|
>
|
|
{cloneElement(htmlContent as React.ReactElement<HtmlContentProps>, {
|
|
onClose: () => onMouseLeave(open),
|
|
...(manualClose
|
|
? {
|
|
onClick: close,
|
|
}
|
|
: {}),
|
|
})}
|
|
</div>
|
|
)}
|
|
</Popover.Panel>
|
|
</Transition>
|
|
</div>
|
|
</>
|
|
)
|
|
}}
|
|
</Popover>
|
|
)
|
|
}
|