59 lines
1.5 KiB
TypeScript
59 lines
1.5 KiB
TypeScript
'use client'
|
|
|
|
import React, { useState, useRef, useEffect } from 'react'
|
|
|
|
interface PopoverProps {
|
|
text: string
|
|
children: React.ReactNode
|
|
size?: 'sm' | 'md' | 'lg' | 'xl'
|
|
}
|
|
|
|
export default function Popover({ text, children, size = 'sm' }: PopoverProps) {
|
|
const [open, setOpen] = useState(false)
|
|
const buttonRef = useRef<HTMLButtonElement>(null)
|
|
const popoverRef = useRef<HTMLDivElement>(null)
|
|
|
|
useEffect(() => {
|
|
const handleClickOutside = (event: MouseEvent) => {
|
|
if (
|
|
popoverRef.current &&
|
|
!popoverRef.current.contains(event.target as Node) &&
|
|
!buttonRef.current?.contains(event.target as Node)
|
|
) {
|
|
setOpen(false)
|
|
}
|
|
}
|
|
document.addEventListener('mousedown', handleClickOutside)
|
|
return () => document.removeEventListener('mousedown', handleClickOutside)
|
|
}, [])
|
|
|
|
const sizeClass = {
|
|
sm: 'max-w-xs',
|
|
md: 'max-w-sm',
|
|
lg: 'max-w-md',
|
|
xl: 'max-w-lg',
|
|
}[size]
|
|
|
|
return (
|
|
<div className="relative inline-block">
|
|
<button
|
|
ref={buttonRef}
|
|
type="button"
|
|
onClick={() => setOpen((prev) => !prev)}
|
|
className="mt-1 text-xs text-gray-400 dark:text-neutral-500"
|
|
>
|
|
{text}
|
|
</button>
|
|
|
|
{open && (
|
|
<div
|
|
ref={popoverRef}
|
|
className={`fixed z-10 mt-2 ${sizeClass} rounded-lg border border-gray-200 bg-white px-4 py-3 text-sm text-gray-700 shadow-md dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-300`}
|
|
>
|
|
{children}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|