{"version":3,"file":"Popover.cjs","sources":["../../../../src/components/popover/Popover.tsx"],"sourcesContent":["import {\n    FloatingFocusManager,\n    FloatingPortal,\n    type ReferenceElement,\n    type VirtualElement,\n    autoUpdate,\n    flip,\n    offset,\n    shift,\n    size,\n    useClick,\n    useDismiss,\n    useFloating,\n    useFocus,\n    useHover,\n    useInteractions,\n    useMergeRefs,\n    useRole,\n} from \"@floating-ui/react\";\nimport clsx from \"clsx\";\nimport * as React from \"react\";\nimport { getThemeAndSize } from \"../../utilities/getThemeAndSize.js\";\nimport type { PopoverOptions } from \"./types.js\";\n\nconst usePopover = ({\n    open: _open,\n    onOpenChange: _onOpenChange,\n    placement = \"bottom-start\",\n    strategy = \"absolute\",\n    modal = true,\n    offset: _offset = 4,\n    positionReference,\n    onPlacementChange,\n    matchReferenceWidth = false,\n    hoverOptions,\n    focusOptions,\n    clickOptions,\n    roleOptions,\n    dismissOptions,\n}: PopoverOptions) => {\n    const [uncontrolledOpen, setUncontrolledOpen] = React.useState(_open);\n\n    const open = _open ?? uncontrolledOpen;\n    const onOpenChange = _onOpenChange ?? setUncontrolledOpen;\n\n    const data = useFloating({\n        open,\n        onOpenChange,\n        placement,\n        strategy,\n        middleware: [\n            offset(_offset),\n            flip({ padding: 5, fallbackPlacements: [\"bottom\", \"top\"] }),\n            shift({ padding: 12 }),\n            ...(matchReferenceWidth\n                ? [\n                      size({\n                          apply({ rects, elements }) {\n                              elements.floating.style.width = `${rects.reference.width}px`;\n                          },\n                      }),\n                  ]\n                : []),\n        ],\n        whileElementsMounted: autoUpdate,\n    });\n\n    const context = data.context;\n\n    const click = useClick(context, {\n        enabled: false,\n        ...clickOptions,\n    });\n    const hover = useHover(context, { enabled: false, ...hoverOptions });\n    const focus = useFocus(context, { enabled: false, ...focusOptions });\n    const dismiss = useDismiss(context, dismissOptions);\n    const role = useRole(context, roleOptions);\n\n    const interactions = useInteractions([click, dismiss, focus, hover, role]);\n\n    React.useLayoutEffect(() => {\n        if (positionReference) {\n            data.refs.setPositionReference(positionReference?.current);\n        }\n    }, [positionReference, data.refs]);\n\n    // useLayoutEffect (i stedet for useEffect) sikrer at konsumenter får\n    // riktig placement før paint — ellers ville første frame bruke default\n    // placement og deretter snappe over til den faktiske, noe som gir et\n    // synlig \"glitch\" på styling som er placement-avhengig (f.eks. flat side\n    // / border-radius på Select-listboxen ved flip).\n    React.useLayoutEffect(() => {\n        if (!open) return;\n        onPlacementChange?.(data.placement);\n    }, [open, data.placement, onPlacementChange]);\n\n    return React.useMemo(\n        () => ({\n            open,\n            onOpenChange,\n            modal,\n            ...interactions,\n            ...data,\n        }),\n        [open, onOpenChange, modal, interactions, data],\n    );\n};\n\ntype PopoverContextType = ReturnType<typeof usePopover> | null;\n\nconst PopoverContext = React.createContext<PopoverContextType>(null);\n\nconst usePopoverContext = () => {\n    const context = React.useContext(PopoverContext);\n\n    if (context == null) {\n        throw new Error(\n            \"Popover komponenter må brukes innenfor en <Popover /> komponent\",\n        );\n    }\n\n    return context;\n};\n\nexport const Popover = ({\n    children,\n    ...restOptions\n}: {\n    children: React.ReactNode;\n} & PopoverOptions) => {\n    const popover = usePopover({ ...restOptions });\n    return (\n        <PopoverContext.Provider value={popover}>\n            {children}\n        </PopoverContext.Provider>\n    );\n};\n\ninterface PopoverTriggerProps {\n    children: React.ReactNode;\n    /**\n     * Rendrer komponenten som child-elementet sitt, og slår sammen egenskaper og props.\n     *\n     * Default er `false`.\n     *\n     * @example\n     * ```tsx\n     * <Component asChild foo=\"bar\">\n     *    <Child baz=\"qux\" />\n     * </Component>\n     *\n     * // Rendrer følgende:\n     * <Child foo=\"bar\" baz=\"qux\" />\n     * ```\n     */\n    asChild?: boolean;\n}\n\nconst PopoverTrigger = React.forwardRef<\n    HTMLElement,\n    React.HTMLProps<HTMLElement> & PopoverTriggerProps\n>(function PopoverTrigger({ children, asChild = false, ...props }, propRef) {\n    const { refs, getReferenceProps, open, onOpenChange } = usePopoverContext();\n    const childrenRef = (children as any).ref;\n    const ref = useMergeRefs([refs.setReference, propRef, childrenRef]);\n\n    if (asChild && React.isValidElement(children)) {\n        return React.cloneElement(\n            children,\n            getReferenceProps({\n                ref,\n                ...props,\n                ...children.props,\n            }),\n        );\n    }\n\n    return (\n        <button\n            ref={ref}\n            onClick={() => onOpenChange?.(!open)}\n            aria-expanded={open}\n            {...getReferenceProps({\n                ...props,\n                className: clsx(\"jkl-popover-trigger\", props.className),\n            })}\n        >\n            {children}\n        </button>\n    );\n});\n\ninterface PopoverContentProps {\n    /**\n     * Padding rundt innholdet i popoveren.\n     *\n     * Default er `0`.\n     */\n    padding?: 0 | 8 | 16 | 24;\n    /**\n     *\n     * Angir hvilket element som skal motta fokus ved åpning.\n     * Kan være en tabbar index eller en referanse til et element.\"\n     *\n     * Default er `0`, som betyr at det første fokuserbare elementet i popoveren får fokus.\n     * @see https://floating-ui.com/docs/FloatingFocusManager#initialfocus\n     */\n    initialFocus?: number | React.RefObject<HTMLElement>;\n    /**\n     * Angir om fokus skal returneres til triggeren når popoveren lukkes.\n     *\n     * Default er `true`.\n     * @see https://floating-ui.com/docs/FloatingFocusManager#returnfocus\n     */\n    returnFocus?: boolean;\n}\n\n// Er popover-elementet posisjonert i forhold til et annet element enn triggeren?\nconst isCustomPositioned = (\n    referenceElement: ReferenceElement,\n): referenceElement is VirtualElement => {\n    if (!referenceElement) return false;\n\n    return \"contextElement\" in referenceElement;\n};\n\nconst PopoverContent = React.forwardRef<\n    HTMLDivElement,\n    React.HTMLProps<HTMLDivElement> & PopoverContentProps\n>(function PopoverContent(\n    {\n        style,\n        className,\n        padding = 0,\n        initialFocus = 0,\n        returnFocus = true,\n        ...props\n    },\n    propRef,\n) {\n    const {\n        context,\n        modal,\n        refs,\n        open,\n        floatingStyles,\n        getFloatingProps,\n        isPositioned,\n    } = usePopoverContext();\n    const ref = useMergeRefs([refs.setFloating, propRef]);\n\n    const referenceElement = refs.reference.current as ReferenceElement;\n\n    const { theme, size } = isCustomPositioned(referenceElement)\n        ? getThemeAndSize(referenceElement.contextElement)\n        : getThemeAndSize(referenceElement);\n\n    const floatingPortalRef = React.useRef<HTMLElement | null>(null);\n\n    // TODO: Løser et problem hvor nestede portaler ikke \"fester\" seg til det nærmeste portal-elementet. Fjernes når alle komponenter som rendres i en portal tar i bruk popover komponenten da den håndterer dette internt. Issue: https://github.com/fremtind/jokul/issues/4356\n    React.useEffect(() => {\n        floatingPortalRef.current =\n            context.elements.domReference?.closest<HTMLElement>(\n                \"[data-portal]\",\n            ) || document.body;\n    }, [context.elements.domReference]);\n\n    if (!open) return null;\n\n    return (\n        <FloatingPortal root={floatingPortalRef.current}>\n            <FloatingFocusManager\n                context={context}\n                modal={modal}\n                initialFocus={initialFocus}\n                returnFocus={returnFocus}\n            >\n                <div\n                    data-theme={theme}\n                    data-size={size}\n                    className={clsx(\"jkl jkl-popover\", className)}\n                    ref={ref}\n                    style={\n                        {\n                            ...style,\n                            ...floatingStyles,\n                            \"--popover-padding\": `var(--jkl-spacing-${padding})`,\n                            // Skjul popoveren inntil floating-ui har regnet\n                            // ut første posisjon — ellers blinker den\n                            // kort i (0,0) før den hopper på plass.\n                            // Bare for ikke-modale popovere; modale bruker\n                            // umiddelbar fokus-trap som forutsetter at\n                            // innholdet er fokuserbart fra første render.\n                            visibility:\n                                modal || isPositioned ? \"visible\" : \"hidden\",\n                        } as React.CSSProperties\n                    }\n                    {...getFloatingProps(props)}\n                >\n                    {props.children}\n                </div>\n            </FloatingFocusManager>\n        </FloatingPortal>\n    );\n});\n\nPopover.Trigger = PopoverTrigger;\nPopover.Content = PopoverContent;\n\nexport default Popover;\n"],"names":["PopoverContext","React","createContext","usePopoverContext","context","useContext","Error","Popover","children","restOptions","popover","open","_open","onOpenChange","_onOpenChange","placement","strategy","modal","offset","_offset","positionReference","onPlacementChange","matchReferenceWidth","hoverOptions","focusOptions","clickOptions","roleOptions","dismissOptions","uncontrolledOpen","setUncontrolledOpen","useState","data","useFloating","middleware","flip","padding","fallbackPlacements","shift","size","apply","rects","elements","floating","style","width","reference","whileElementsMounted","autoUpdate","click","useClick","enabled","hover","useHover","focus","useFocus","dismiss","useDismiss","role","useRole","interactions","useInteractions","useLayoutEffect","refs","setPositionReference","current","useMemo","usePopover","Provider","value","PopoverTrigger","forwardRef","asChild","props","propRef","getReferenceProps","childrenRef","ref","useMergeRefs","setReference","isValidElement","cloneElement","jsx","onClick","className","clsx","PopoverContent","initialFocus","returnFocus","floatingStyles","getFloatingProps","isPositioned","setFloating","referenceElement","theme","getThemeAndSize","contextElement","floatingPortalRef","useRef","useEffect","domReference","closest","document","body","FloatingPortal","root","FloatingFocusManager","visibility","Trigger","Content"],"mappings":"kjBA8GMA,EAAiBC,EAAMC,cAAkC,MAEzDC,EAAoB,KACtB,MAAMC,EAAUH,EAAMI,WAAWL,GAEjC,GAAe,MAAXI,EACA,MAAM,IAAIE,MACN,mEAIR,OAAOF,GAGEG,EAAU,EACnBC,SAAAA,KACGC,MAIH,MAAMC,EA1GS,GACfC,KAAMC,EACNC,aAAcC,EACdC,UAAAA,EAAY,eACZC,SAAAA,EAAW,WACXC,MAAAA,GAAQ,EACRC,OAAQC,EAAU,EAClBC,kBAAAA,EACAC,kBAAAA,EACAC,oBAAAA,GAAsB,EACtBC,aAAAA,EACAC,aAAAA,EACAC,aAAAA,EACAC,YAAAA,EACAC,eAAAA,MAEA,MAAOC,EAAkBC,GAAuB5B,EAAM6B,SAASlB,GAEzDD,EAAOC,GAASgB,EAChBf,EAAeC,GAAiBe,EAEhCE,EAAOC,EAAAA,YAAY,CACrBrB,KAAAA,EACAE,aAAAA,EACAE,UAAAA,EACAC,SAAAA,EACAiB,WAAY,CACRf,EAAAA,OAAOC,GACPe,EAAAA,KAAK,CAAEC,QAAS,EAAGC,mBAAoB,CAAC,SAAU,SAClDC,QAAM,CAAEF,QAAS,QACbb,EACE,CACIgB,OAAK,CACD,KAAAC,EAAQC,MAAAA,EAAOC,SAAAA,IACXA,EAASC,SAASC,MAAMC,MAAQ,GAAGJ,EAAMK,UAAUD,SACvD,KAGR,IAEVE,qBAAsBC,EAAAA,aAGpB3C,EAAU2B,EAAK3B,QAEf4C,EAAQC,EAAAA,SAAS7C,EAAS,CAC5B8C,SAAS,KACNzB,IAED0B,EAAQC,EAAAA,SAAShD,EAAS,CAAE8C,SAAS,KAAU3B,IAC/C8B,EAAQC,EAAAA,SAASlD,EAAS,CAAE8C,SAAS,KAAU1B,IAC/C+B,EAAUC,EAAAA,WAAWpD,EAASuB,GAC9B8B,EAAOC,EAAAA,QAAQtD,EAASsB,GAExBiC,EAAeC,EAAAA,gBAAgB,CAACZ,EAAOO,EAASF,EAAOF,EAAOM,IAEpExD,OAAAA,EAAM4D,gBAAgB,KACdzC,GACAW,EAAK+B,KAAKC,qBAAqB3C,GAAmB4C,UAEvD,CAAC5C,EAAmBW,EAAK+B,OAO5B7D,EAAM4D,gBAAgB,KACblD,GACLU,IAAoBU,EAAKhB,YAC1B,CAACJ,EAAMoB,EAAKhB,UAAWM,IAEnBpB,EAAMgE,QACT,KAAA,CACItD,KAAAA,EACAE,aAAAA,EACAI,MAAAA,KACG0C,KACA5B,IAEP,CAACpB,EAAME,EAAcI,EAAO0C,EAAc5B,KA0B9BmC,CAAW,IAAKzD,IAChC,aACKT,EAAemE,SAAf,CAAwBC,MAAO1D,EAC3BF,SAAAA,KAyBP6D,EAAiBpE,EAAMqE,WAG3B,UAA0B9D,SAAAA,EAAU+D,QAAAA,GAAU,KAAUC,GAASC,GAC/D,MAAQX,KAAAA,EAAMY,kBAAAA,EAAmB/D,KAAAA,EAAME,aAAAA,GAAiBV,IAClDwE,EAAenE,EAAiBoE,IAChCA,EAAMC,EAAAA,aAAa,CAACf,EAAKgB,aAAcL,EAASE,IAEtD,OAAIJ,GAAWtE,EAAM8E,eAAevE,GACzBP,EAAM+E,aACTxE,EACAkE,EAAkB,CACdE,IAAAA,KACGJ,KACAhE,EAASgE,SAMpBS,EAAAA,IAAC,SAAA,CACGL,IAAAA,EACAM,QAAS,IAAMrE,KAAgBF,GAC/B,gBAAeA,KACX+D,EAAkB,IACfF,EACHW,UAAWC,EAAAA,KAAK,sBAAuBZ,EAAMW,aAGhD3E,SAAAA,GAGb,GAoCM6E,EAAiBpF,EAAMqE,WAG3B,UAEM3B,MAAAA,EACAwC,UAAAA,EACAhD,QAAAA,EAAU,EACVmD,aAAAA,EAAe,EACfC,YAAAA,GAAc,KACXf,GAEPC,GAEA,MACIrE,QAAAA,EACAa,MAAAA,EACA6C,KAAAA,EACAnD,KAAAA,EACA6E,eAAAA,EACAC,iBAAAA,EACAC,aAAAA,GACAvF,IACEyE,EAAMC,EAAAA,aAAa,CAACf,EAAK6B,YAAalB,IAEtCmB,EAAmB9B,EAAKjB,UAAUmB,SAEhC6B,MAAAA,EAAOvD,KAAAA,IAlCfsD,EAkC2CA,IA9BpC,mBAAoBA,EA+BrBE,EAAAA,gBAAgBF,EAAiBG,gBACjCD,EAAAA,gBAAgBF,GAEhBI,EAAoB/F,EAAMgG,OAA2B,MAtC3DL,MAgDA,OAPA3F,EAAMiG,UAAU,KACZF,EAAkBhC,QACd5D,EAAQqC,SAAS0D,cAAcC,QAC3B,kBACCC,SAASC,MACnB,CAAClG,EAAQqC,SAAS0D,eAEhBxF,EAGDsE,EAAAA,IAACsB,EAAAA,eAAA,CAAeC,KAAMR,EAAkBhC,QACpCxD,SAAAyE,EAAAA,IAACwB,EAAAA,qBAAA,CACGrG,QAAAA,EACAa,MAAAA,EACAqE,aAAAA,EACAC,YAAAA,EAEA/E,SAAAyE,EAAAA,IAAC,MAAA,CACG,aAAYY,EACZ,YAAWvD,EACX6C,UAAWC,EAAAA,KAAK,kBAAmBD,GACnCP,IAAAA,EACAjC,MACI,IACOA,KACA6C,EACH,oBAAqB,qBAAqBrD,KAO1CuE,WACIzF,GAASyE,EAAe,UAAY,aAG5CD,EAAiBjB,GAEpBhE,SAAAgE,EAAMhE,eAhCL,IAqCtB,GAEAD,EAAQoG,QAAUtC,EAClB9D,EAAQqG,QAAUvB"}