# All files / src/components/ui popover.tsx

0% Statements 0/124
0% Branches 0/1
0% Functions 0/1
0% Lines 0/124

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125

     "use client"

    import React, { ComponentProps, useState, useRef, useEffect, createContext, useContext, isValidElement, cloneElement } from "react"
    import { createPortal } from "react-dom"
    import { cn } from "@/lib/utils"

    interface PopoverContextValue {
      open: boolean
      setOpen: (open: boolean) => void
    }

    const PopoverContext = createContext<PopoverContextValue | null>(null)

    function Popover({ children }: { children: React.ReactNode }) {
      const [open, setOpen] = useState(false)
      return (
        <PopoverContext.Provider value={{ open, setOpen }}>
          {children}
        </PopoverContext.Provider>
      )
    }

    function PopoverTrigger({ children, asChild, ...props }: ComponentProps<"button"> & { asChild?: boolean }) {
      const context = useContext(PopoverContext)

      const handleClick = () => {
        context?.setOpen(!context.open)
      }

      if (asChild && isValidElement(children)) {
        return cloneElement(children as React.ReactElement, {
          onClick: handleClick,
          ...props,
        })
      }

      return (
        <button type="button" onClick={handleClick} {...props}>
          {children}
        </button>
      )
    }

    function PopoverContent({
      className,
      children,
      ...props
    }: ComponentProps<"div"> & {
      align?: "start" | "center" | "end"
      sideOffset?: number
    }) {
      const context = useContext(PopoverContext)
      const contentRef = useRef<HTMLDivElement>(null)
      const [mounted, setMounted] = useState(false)

      useEffect(() => {
        setMounted(true)
      }, [])

      useEffect(() => {
        if (!context?.open) return

        const handleClickOutside = (e: MouseEvent) => {
          if (contentRef.current && !contentRef.current.contains(e.target as Node)) {
            context.setOpen(false)
          }
        }

        const handleEscape = (e: KeyboardEvent) => {
          if (e.key === "Escape") {
            context.setOpen(false)
          }
        }

        document.addEventListener("mousedown", handleClickOutside)
        document.addEventListener("keydown", handleEscape)

        return () => {
          document.removeEventListener("mousedown", handleClickOutside)
          document.removeEventListener("keydown", handleEscape)
        }
      }, [context])

      if (!context?.open || !mounted) return null

      return createPortal(
        <div className="cdk-overlay-container">
          <div className="cdk-overlay-pane">
            <div
              ref={contentRef}
              className={cn("mat-mdc-menu-panel", className)}
              role="dialog"
              {...props}
            >
              <div className="mat-mdc-menu-content">
                {children}
              </div>
            </div>
          </div>
        </div>,
        document.body
      )
    }

    function PopoverAnchor({ children }: { children: React.ReactNode }) {
      return <>{children}</>
    }

    function PopoverClose({ children, className, ...props }: ComponentProps<"button">) {
      const context = useContext(PopoverContext)

      return (
        <button
          type="button"
          onClick={() => context?.setOpen(false)}
          className={cn("mat-mdc-icon-button", className)}
          {...props}
        >
          {children}
        </button>
      )
    }

    export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor, PopoverClose }