"use client"; // packages/react/dialog/src/Dialog.tsx import * as React from "react"; import { composeEventHandlers } from "@radix-ui/primitive"; import { useComposedRefs } from "@radix-ui/react-compose-refs"; import { createContext, createContextScope } from "@radix-ui/react-context"; import { useId } from "@radix-ui/react-id"; import { useControllableState } from "@radix-ui/react-use-controllable-state"; import { DismissableLayer } from "@radix-ui/react-dismissable-layer"; import { FocusScope } from "@radix-ui/react-focus-scope"; import { Portal as PortalPrimitive } from "@radix-ui/react-portal"; import { Presence } from "@radix-ui/react-presence"; import { Primitive } from "@radix-ui/react-primitive"; import { useFocusGuards } from "@radix-ui/react-focus-guards"; import { RemoveScroll } from "react-remove-scroll"; import { hideOthers } from "aria-hidden"; import { Slot } from "@radix-ui/react-slot"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; var DIALOG_NAME = "Dialog"; var [createDialogContext, createDialogScope] = createContextScope(DIALOG_NAME); var [DialogProvider, useDialogContext] = createDialogContext(DIALOG_NAME); var Dialog = (props) => { const { __scopeDialog, children, open: openProp, defaultOpen, onOpenChange, modal = true } = props; const triggerRef = React.useRef(null); const contentRef = React.useRef(null); const [open = false, setOpen] = useControllableState({ prop: openProp, defaultProp: defaultOpen, onChange: onOpenChange }); return /* @__PURE__ */ jsx( DialogProvider, { scope: __scopeDialog, triggerRef, contentRef, contentId: useId(), titleId: useId(), descriptionId: useId(), open, onOpenChange: setOpen, onOpenToggle: React.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]), modal, children } ); }; Dialog.displayName = DIALOG_NAME; var TRIGGER_NAME = "DialogTrigger"; var DialogTrigger = React.forwardRef( (props, forwardedRef) => { const { __scopeDialog, ...triggerProps } = props; const context = useDialogContext(TRIGGER_NAME, __scopeDialog); const composedTriggerRef = useComposedRefs(forwardedRef, context.triggerRef); return /* @__PURE__ */ jsx( Primitive.button, { type: "button", "aria-haspopup": "dialog", "aria-expanded": context.open, "aria-controls": context.contentId, "data-state": getState(context.open), ...triggerProps, ref: composedTriggerRef, onClick: composeEventHandlers(props.onClick, context.onOpenToggle) } ); } ); DialogTrigger.displayName = TRIGGER_NAME; var PORTAL_NAME = "DialogPortal"; var [PortalProvider, usePortalContext] = createDialogContext(PORTAL_NAME, { forceMount: void 0 }); var DialogPortal = (props) => { const { __scopeDialog, forceMount, children, container } = props; const context = useDialogContext(PORTAL_NAME, __scopeDialog); return /* @__PURE__ */ jsx(PortalProvider, { scope: __scopeDialog, forceMount, children: React.Children.map(children, (child) => /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: /* @__PURE__ */ jsx(PortalPrimitive, { asChild: true, container, children: child }) })) }); }; DialogPortal.displayName = PORTAL_NAME; var OVERLAY_NAME = "DialogOverlay"; var DialogOverlay = React.forwardRef( (props, forwardedRef) => { const portalContext = usePortalContext(OVERLAY_NAME, props.__scopeDialog); const { forceMount = portalContext.forceMount, ...overlayProps } = props; const context = useDialogContext(OVERLAY_NAME, props.__scopeDialog); return context.modal ? /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: /* @__PURE__ */ jsx(DialogOverlayImpl, { ...overlayProps, ref: forwardedRef }) }) : null; } ); DialogOverlay.displayName = OVERLAY_NAME; var DialogOverlayImpl = React.forwardRef( (props, forwardedRef) => { const { __scopeDialog, ...overlayProps } = props; const context = useDialogContext(OVERLAY_NAME, __scopeDialog); return ( // Make sure `Content` is scrollable even when it doesn't live inside `RemoveScroll` // ie. when `Overlay` and `Content` are siblings /* @__PURE__ */ jsx(RemoveScroll, { as: Slot, allowPinchZoom: true, shards: [context.contentRef], children: /* @__PURE__ */ jsx( Primitive.div, { "data-state": getState(context.open), ...overlayProps, ref: forwardedRef, style: { pointerEvents: "auto", ...overlayProps.style } } ) }) ); } ); var CONTENT_NAME = "DialogContent"; var DialogContent = React.forwardRef( (props, forwardedRef) => { const portalContext = usePortalContext(CONTENT_NAME, props.__scopeDialog); const { forceMount = portalContext.forceMount, ...contentProps } = props; const context = useDialogContext(CONTENT_NAME, props.__scopeDialog); return /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: context.modal ? /* @__PURE__ */ jsx(DialogContentModal, { ...contentProps, ref: forwardedRef }) : /* @__PURE__ */ jsx(DialogContentNonModal, { ...contentProps, ref: forwardedRef }) }); } ); DialogContent.displayName = CONTENT_NAME; var DialogContentModal = React.forwardRef( (props, forwardedRef) => { const context = useDialogContext(CONTENT_NAME, props.__scopeDialog); const contentRef = React.useRef(null); const composedRefs = useComposedRefs(forwardedRef, context.contentRef, contentRef); React.useEffect(() => { const content = contentRef.current; if (content) return hideOthers(content); }, []); return /* @__PURE__ */ jsx( DialogContentImpl, { ...props, ref: composedRefs, trapFocus: context.open, disableOutsidePointerEvents: true, onCloseAutoFocus: composeEventHandlers(props.onCloseAutoFocus, (event) => { event.preventDefault(); context.triggerRef.current?.focus(); }), onPointerDownOutside: composeEventHandlers(props.onPointerDownOutside, (event) => { const originalEvent = event.detail.originalEvent; const ctrlLeftClick = originalEvent.button === 0 && originalEvent.ctrlKey === true; const isRightClick = originalEvent.button === 2 || ctrlLeftClick; if (isRightClick) event.preventDefault(); }), onFocusOutside: composeEventHandlers( props.onFocusOutside, (event) => event.preventDefault() ) } ); } ); var DialogContentNonModal = React.forwardRef( (props, forwardedRef) => { const context = useDialogContext(CONTENT_NAME, props.__scopeDialog); const hasInteractedOutsideRef = React.useRef(false); const hasPointerDownOutsideRef = React.useRef(false); return /* @__PURE__ */ jsx( DialogContentImpl, { ...props, ref: forwardedRef, trapFocus: false, disableOutsidePointerEvents: false, onCloseAutoFocus: (event) => { props.onCloseAutoFocus?.(event); if (!event.defaultPrevented) { if (!hasInteractedOutsideRef.current) context.triggerRef.current?.focus(); event.preventDefault(); } hasInteractedOutsideRef.current = false; hasPointerDownOutsideRef.current = false; }, onInteractOutside: (event) => { props.onInteractOutside?.(event); if (!event.defaultPrevented) { hasInteractedOutsideRef.current = true; if (event.detail.originalEvent.type === "pointerdown") { hasPointerDownOutsideRef.current = true; } } const target = event.target; const targetIsTrigger = context.triggerRef.current?.contains(target); if (targetIsTrigger) event.preventDefault(); if (event.detail.originalEvent.type === "focusin" && hasPointerDownOutsideRef.current) { event.preventDefault(); } } } ); } ); var DialogContentImpl = React.forwardRef( (props, forwardedRef) => { const { __scopeDialog, trapFocus, onOpenAutoFocus, onCloseAutoFocus, ...contentProps } = props; const context = useDialogContext(CONTENT_NAME, __scopeDialog); const contentRef = React.useRef(null); const composedRefs = useComposedRefs(forwardedRef, contentRef); useFocusGuards(); return /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx( FocusScope, { asChild: true, loop: true, trapped: trapFocus, onMountAutoFocus: onOpenAutoFocus, onUnmountAutoFocus: onCloseAutoFocus, children: /* @__PURE__ */ jsx( DismissableLayer, { role: "dialog", id: context.contentId, "aria-describedby": context.descriptionId, "aria-labelledby": context.titleId, "data-state": getState(context.open), ...contentProps, ref: composedRefs, onDismiss: () => context.onOpenChange(false) } ) } ), /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx(TitleWarning, { titleId: context.titleId }), /* @__PURE__ */ jsx(DescriptionWarning, { contentRef, descriptionId: context.descriptionId }) ] }) ] }); } ); var TITLE_NAME = "DialogTitle"; var DialogTitle = React.forwardRef( (props, forwardedRef) => { const { __scopeDialog, ...titleProps } = props; const context = useDialogContext(TITLE_NAME, __scopeDialog); return /* @__PURE__ */ jsx(Primitive.h2, { id: context.titleId, ...titleProps, ref: forwardedRef }); } ); DialogTitle.displayName = TITLE_NAME; var DESCRIPTION_NAME = "DialogDescription"; var DialogDescription = React.forwardRef( (props, forwardedRef) => { const { __scopeDialog, ...descriptionProps } = props; const context = useDialogContext(DESCRIPTION_NAME, __scopeDialog); return /* @__PURE__ */ jsx(Primitive.p, { id: context.descriptionId, ...descriptionProps, ref: forwardedRef }); } ); DialogDescription.displayName = DESCRIPTION_NAME; var CLOSE_NAME = "DialogClose"; var DialogClose = React.forwardRef( (props, forwardedRef) => { const { __scopeDialog, ...closeProps } = props; const context = useDialogContext(CLOSE_NAME, __scopeDialog); return /* @__PURE__ */ jsx( Primitive.button, { type: "button", ...closeProps, ref: forwardedRef, onClick: composeEventHandlers(props.onClick, () => context.onOpenChange(false)) } ); } ); DialogClose.displayName = CLOSE_NAME; function getState(open) { return open ? "open" : "closed"; } var TITLE_WARNING_NAME = "DialogTitleWarning"; var [WarningProvider, useWarningContext] = createContext(TITLE_WARNING_NAME, { contentName: CONTENT_NAME, titleName: TITLE_NAME, docsSlug: "dialog" }); var TitleWarning = ({ titleId }) => { const titleWarningContext = useWarningContext(TITLE_WARNING_NAME); const MESSAGE = `\`${titleWarningContext.contentName}\` requires a \`${titleWarningContext.titleName}\` for the component to be accessible for screen reader users. If you want to hide the \`${titleWarningContext.titleName}\`, you can wrap it with our VisuallyHidden component. For more information, see https://radix-ui.com/primitives/docs/components/${titleWarningContext.docsSlug}`; React.useEffect(() => { if (titleId) { const hasTitle = document.getElementById(titleId); if (!hasTitle) console.error(MESSAGE); } }, [MESSAGE, titleId]); return null; }; var DESCRIPTION_WARNING_NAME = "DialogDescriptionWarning"; var DescriptionWarning = ({ contentRef, descriptionId }) => { const descriptionWarningContext = useWarningContext(DESCRIPTION_WARNING_NAME); const MESSAGE = `Warning: Missing \`Description\` or \`aria-describedby={undefined}\` for {${descriptionWarningContext.contentName}}.`; React.useEffect(() => { const describedById = contentRef.current?.getAttribute("aria-describedby"); if (descriptionId && describedById) { const hasDescription = document.getElementById(descriptionId); if (!hasDescription) console.warn(MESSAGE); } }, [MESSAGE, contentRef, descriptionId]); return null; }; var Root = Dialog; var Trigger = DialogTrigger; var Portal = DialogPortal; var Overlay = DialogOverlay; var Content = DialogContent; var Title = DialogTitle; var Description = DialogDescription; var Close = DialogClose; export { Close, Content, Description, Dialog, DialogClose, DialogContent, DialogDescription, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Overlay, Portal, Root, Title, Trigger, WarningProvider, createDialogScope }; //# sourceMappingURL=index.mjs.map