"use client"
import React, {
createContext,
useContext,
useEffect,
useId,
useRef,
useState,
} from "react"
import { X } from "lucide-react"
import { AnimatePresence, motion, MotionConfig } from "motion/react"
import { cn } from "@/lib/utils"
const TRANSITION = {
type: "spring" as const,
bounce: 0.05,
duration: 0.3,
}
function useClickOutside(
ref: React.RefObject<HTMLElement | null>,
handler: () => void
) {
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
handler()
}
}
document.addEventListener("mousedown", handleClickOutside)
return () => {
document.removeEventListener("mousedown", handleClickOutside)
}
}, [ref, handler])
}
interface PopoverContextType {
isOpen: boolean
openPopover: () => void
closePopover: () => void
uniqueId: string
note: string
setNote: (note: string) => void
}
const PopoverContext = createContext<PopoverContextType | undefined>(undefined)
function usePopover() {
const context = useContext(PopoverContext)
if (!context) {
throw new Error("usePopover must be used within a PopoverProvider")
}
return context
}
function usePopoverLogic() {
const uniqueId = useId()
const [isOpen, setIsOpen] = useState(false)
const [note, setNote] = useState("")
const openPopover = () => setIsOpen(true)
const closePopover = () => {
setIsOpen(false)
setNote("")
}
return { isOpen, openPopover, closePopover, uniqueId, note, setNote }
}
interface PopoverRootProps {
children: React.ReactNode
className?: string
}
export function PopoverRoot({ children, className }: PopoverRootProps) {
const popoverLogic = usePopoverLogic()
return (
<PopoverContext.Provider value={popoverLogic}>
<MotionConfig transition={TRANSITION}>
<div
className={cn(
"relative flex items-center justify-center isolate",
className
)}
>
{children}
</div>
</MotionConfig>
</PopoverContext.Provider>
)
}
interface PopoverTriggerProps {
children: React.ReactNode
className?: string
}
export function PopoverTrigger({ children, className }: PopoverTriggerProps) {
const { openPopover, uniqueId } = usePopover()
return (
<motion.button
key="button"
layoutId={`popover-${uniqueId}`}
className={cn(
"flex h-9 items-center border border-zinc-950/10 bg-white px-3 text-zinc-950 dark:border-zinc-50/10 dark:bg-zinc-700 dark:text-zinc-50",
className
)}
style={{
borderRadius: 8,
}}
onClick={openPopover}
>
<motion.span layoutId={`popover-label-${uniqueId}`} className="text-sm">
{children}
</motion.span>
</motion.button>
)
}
interface PopoverContentProps {
children: React.ReactNode
className?: string
}
export function PopoverContent({ children, className }: PopoverContentProps) {
const { isOpen, closePopover, uniqueId } = usePopover()
const formContainerRef = useRef<HTMLDivElement>(null)
useClickOutside(formContainerRef, closePopover)
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape") {
closePopover()
}
}
document.addEventListener("keydown", handleKeyDown)
return () => {
document.removeEventListener("keydown", handleKeyDown)
}
}, [closePopover])
return (
<AnimatePresence>
{isOpen && (
<motion.div
ref={formContainerRef}
layoutId={`popover-${uniqueId}`}
className={cn(
"absolute h-[200px] w-[364px] overflow-hidden border border-zinc-950/10 bg-white outline-none dark:bg-zinc-700 z-50", // Changed z-90 to z-50
className
)}
style={{
borderRadius: 12,
top: "auto", // Remove any top positioning
left: "auto", // Remove any left positioning
transform: "none", // Remove any transform
}}
>
{children}
</motion.div>
)}
</AnimatePresence>
)
}
interface PopoverFormProps {
children: React.ReactNode
onSubmit?: (note: string) => void
className?: string
}
export function PopoverForm({
children,
onSubmit,
className,
}: PopoverFormProps) {
const { note, closePopover } = usePopover()
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
onSubmit?.(note)
closePopover()
}
return (
<form
className={cn("flex h-full flex-col", className)}
onSubmit={handleSubmit}
>
{children}
</form>
)
}
interface PopoverLabelProps {
children: React.ReactNode
className?: string
}
export function PopoverLabel({ children, className }: PopoverLabelProps) {
const { uniqueId, note } = usePopover()
return (
<motion.span
layoutId={`popover-label-${uniqueId}`}
aria-hidden="true"
style={{
opacity: note ? 0 : 1,
}}
className={cn(
"absolute left-4 top-3 select-none text-sm text-zinc-500 dark:text-zinc-400",
className
)}
>
{children}
</motion.span>
)
}
interface PopoverTextareaProps {
className?: string
}
export function PopoverTextarea({ className }: PopoverTextareaProps) {
const { note, setNote } = usePopover()
return (
<textarea
className={cn(
"h-full w-full resize-none rounded-md bg-transparent px-4 py-3 text-sm outline-none",
className
)}
autoFocus
value={note}
onChange={(e) => setNote(e.target.value)}
/>
)
}
interface PopoverFooterProps {
children: React.ReactNode
className?: string
}
export function PopoverFooter({ children, className }: PopoverFooterProps) {
return (
<div
key="close"
className={cn("flex justify-between px-4 py-3", className)}
>
{children}
</div>
)
}
interface PopoverCloseButtonProps {
className?: string
}
export function PopoverCloseButton({ className }: PopoverCloseButtonProps) {
const { closePopover } = usePopover()
return (
<button
type="button"
className={cn("flex items-center", className)}
onClick={closePopover}
aria-label="Close popover"
>
<X size={16} className="text-zinc-900 dark:text-zinc-100" />
</button>
)
}
interface PopoverSubmitButtonProps {
className?: string
}
export function PopoverSubmitButton({ className }: PopoverSubmitButtonProps) {
return (
<button
className={cn(
"relative ml-1 flex h-8 shrink-0 scale-100 select-none appearance-none items-center justify-center rounded-lg border border-zinc-950/10 bg-transparent px-2 text-sm text-zinc-500 transition-colors hover:bg-zinc-100 hover:text-zinc-800 focus-visible:ring-2 active:scale-[0.98] dark:border-zinc-50/10 dark:text-zinc-50 dark:hover:bg-zinc-800",
className
)}
type="submit"
aria-label="Submit note"
>
Submit
</button>
)
}
export function PopoverHeader({
children,
className,
}: {
children: React.ReactNode
className?: string
}) {
return (
<div
className={cn(
"px-4 py-2 font-semibold text-zinc-900 dark:text-zinc-100",
className
)}
>
{children}
</div>
)
}
export function PopoverBody({
children,
className,
}: {
children: React.ReactNode
className?: string
}) {
return <div className={cn("p-4", className)}>{children}</div>
}
// New component: PopoverButton
export function PopoverButton({
children,
onClick,
className,
}: {
children: React.ReactNode
onClick?: () => void
className?: string
}) {
return (
<button
className={cn(
"flex w-full items-center gap-2 rounded-md px-4 py-2 text-left text-sm hover:bg-zinc-100 dark:hover:bg-zinc-700",
className
)}
onClick={onClick}
>
{children}
</button>
)
}