"use client"; import * as DialogPrimitive from "@radix-ui/react-dialog"; import { cva, type VariantProps } from "class-variance-authority"; import { usePathname, useRouter } from "next/navigation"; import type { ForwardRefExoticComponent, ReactElement, ReactNode } from "react"; import React, { useMemo, useState } from "react"; import { Dialog as PlatformDialogPrimitives, useIsPlatform } from "@calcom/atoms/monorepo"; import { classNames } from "@calcom/lib"; import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import type { ButtonProps } from "../button"; import { Button } from "../button"; import type { IconName } from "../icon"; import { Icon } from "../icon"; const dialogClasses = cva( "fadeIn bg-default scroll-bar fixed left-1/2 top-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 rounded-2xl text-left shadow-xl focus-visible:outline-none sm:align-middle", { variants: { size: { xl: "px-8 pt-8 sm:max-w-[90rem]", lg: "px-8 pt-8 sm:max-w-[70rem]", md: "px-8 pt-8 sm:max-w-[48rem]", default: "px-8 pt-8 sm:max-w-[35rem]", }, }, defaultVariants: { size: "default", }, } ); export type DialogProps = React.ComponentProps<(typeof DialogPrimitive)["Root"]> & { name?: string; clearQueryParamsOnClose?: string[]; }; const enum DIALOG_STATE { // Dialog is there in the DOM but not visible. CLOSED = "CLOSED", // State from the time b/w the Dialog is dismissed and the time the "dialog" query param is removed from the URL. CLOSING = "CLOSING", // Dialog is visible. OPEN = "OPEN", } export function Dialog(props: DialogProps) { const isPlatform = useIsPlatform(); return !isPlatform ? : ; } function WebDialog(props: DialogProps) { const router = useRouter(); const pathname = usePathname(); const searchParams = useCompatSearchParams(); const newSearchParams = new URLSearchParams(searchParams ?? undefined); const { children, name, ...dialogProps } = props; // only used if name is set const [dialogState, setDialogState] = useState(dialogProps.open ? DIALOG_STATE.OPEN : DIALOG_STATE.CLOSED); const shouldOpenDialog = newSearchParams.get("dialog") === name; if (name) { const clearQueryParamsOnClose = ["dialog", ...(props.clearQueryParamsOnClose || [])]; dialogProps.onOpenChange = (open) => { if (props.onOpenChange) { props.onOpenChange(open); } // toggles "dialog" query param if (open) { newSearchParams.set("dialog", name); } else { clearQueryParamsOnClose.forEach((queryParam) => { newSearchParams.delete(queryParam); }); router.push(`${pathname}?${newSearchParams.toString()}`); } setDialogState(open ? DIALOG_STATE.OPEN : DIALOG_STATE.CLOSING); }; if (dialogState === DIALOG_STATE.CLOSED && shouldOpenDialog) { setDialogState(DIALOG_STATE.OPEN); } if (dialogState === DIALOG_STATE.CLOSING && !shouldOpenDialog) { setDialogState(DIALOG_STATE.CLOSED); } // allow overriding if (!("open" in dialogProps)) { dialogProps.open = dialogState === DIALOG_STATE.OPEN ? true : false; } } return {children}; } type DialogContentProps = React.ComponentProps<(typeof DialogPrimitive)["Content"]> & { type?: "creation" | "confirmation"; title?: string; description?: string | JSX.Element | null; closeText?: string; actionDisabled?: boolean; Icon?: IconName; enableOverflow?: boolean; forceOverlayWhenNoModal?: boolean; /** * Disables the ability to close the dialog by clicking outside of it. Could be useful when the dialog is doing something critical which might be in progress. */ preventCloseOnOutsideClick?: boolean; } & VariantProps; // enableOverflow:- use this prop whenever content inside DialogContent could overflow and require scrollbar export const DialogContent = React.forwardRef( ( { children, title, Icon: icon, enableOverflow, forceOverlayWhenNoModal, type = "creation", preventCloseOnOutsideClick, ...props }, forwardedRef ) => { const isPlatform = useIsPlatform(); const [Portal, Overlay, Content] = useMemo( () => isPlatform ? [ ({ children }: { children: ReactElement | ReactElement[] }) => <>{children}, PlatformDialogPrimitives.DialogOverlay, PlatformDialogPrimitives.DialogContent, ] : [DialogPrimitive.Portal, DialogPrimitive.Overlay, DialogPrimitive.Content], [isPlatform] ); return ( {forceOverlayWhenNoModal ? (
) : ( )} { if (preventCloseOnOutsideClick) { e.preventDefault(); } }} className={classNames( dialogClasses({ size: props.size }), "max-h-[95vh]", enableOverflow ? "overflow-y-auto" : "overflow-visible", `${props.className || ""}` )} ref={forwardedRef}> {type === "creation" && (
{children}
)} {type === "confirmation" && (
{icon && (
)}
{children}
)} {!type && children}
); } ); type DialogHeaderProps = { title: React.ReactNode; subtitle?: React.ReactNode; } & React.HTMLAttributes; export function DialogHeader(props: DialogHeaderProps) { if (!props.title) return null; return (
{props.subtitle && (

{props.subtitle}

)}
); } type DialogFooterProps = { children: React.ReactNode; showDivider?: boolean; noSticky?: boolean; } & React.HTMLAttributes; export function DialogFooter(props: DialogFooterProps) { return (
{props.showDivider &&
}
{props.children}
); } DialogContent.displayName = "DialogContent"; export const DialogTrigger: ForwardRefExoticComponent< DialogPrimitive.DialogTriggerProps & React.RefAttributes > = React.forwardRef((props, ref) => { const isPlatform = useIsPlatform(); return !isPlatform ? ( ) : ( ); }); DialogTrigger.displayName = "DialogTrigger"; type DialogCloseProps = { "data-testid"?: string; dialogCloseProps?: React.ComponentProps<(typeof DialogPrimitive)["Close"]>; children?: ReactNode; onClick?: (e: React.MouseEvent) => void; disabled?: boolean; color?: ButtonProps["color"]; } & React.ComponentProps; export function DialogClose( props: { "data-testid"?: string; dialogCloseProps?: React.ComponentProps<(typeof DialogPrimitive)["Close"]>; children?: ReactNode; onClick?: (e: React.MouseEvent) => void; disabled?: boolean; color?: ButtonProps["color"]; } & React.ComponentProps ) { const { t } = useLocale(); const isPlatform = useIsPlatform(); const Close = useMemo( () => (isPlatform ? PlatformDialogPrimitives.DialogClose : DialogPrimitive.Close), [isPlatform] ); const { className, ...buttonProps } = props; return ( ); } DialogClose.displayName = "WebDialogClose";