NODEDC_TASKMANAGER/plane-src/packages/propel/src/dialog/root.tsx

143 lines
4.0 KiB
TypeScript

/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import { forwardRef, memo, useMemo } from "react";
import { Dialog as BaseDialog } from "@base-ui-components/react";
import { cn } from "../utils/classname";
// enums
export enum EDialogWidth {
SM = "sm:max-w-sm",
MD = "sm:max-w-md",
LG = "sm:max-w-lg",
XL = "sm:max-w-xl",
XXL = "sm:max-w-2xl",
XXXL = "sm:max-w-3xl",
XXXXL = "sm:max-w-4xl",
VXL = "sm:max-w-5xl",
VIXL = "sm:max-w-6xl",
VIIXL = "sm:max-w-7xl",
}
// Types
export type DialogPosition = "center" | "top";
export interface DialogProps extends React.ComponentProps<typeof BaseDialog.Root> {
children: React.ReactNode;
}
export interface DialogPanelProps extends React.ComponentProps<typeof BaseDialog.Popup> {
width?: EDialogWidth;
position?: DialogPosition;
children: React.ReactNode;
}
export interface DialogTitleProps extends React.ComponentProps<typeof BaseDialog.Title> {
children: React.ReactNode;
}
// Constants
const OVERLAY_CLASSNAME = cn("fixed inset-0 z-90 bg-backdrop/70 backdrop-blur-sm");
const BASE_CLASSNAME =
"nodedc-glass-modal relative w-full rounded-[28px] border border-subtle/70 bg-surface-1/78 text-left shadow-[0_16px_48px_rgba(0,0,0,0.34)] backdrop-blur-2xl z-100";
// Utility functions
const getPositionClassNames = (position: DialogPosition) =>
cn("fixed isolate z-100", {
"top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2": position === "center",
"top-8 left-1/2 -translate-x-1/2": position === "top",
});
const DialogPortal = memo(function DialogPortal({
children,
...props
}: React.ComponentProps<typeof BaseDialog.Portal>) {
return (
<BaseDialog.Portal data-slot="dialog-portal" {...props}>
{children}
</BaseDialog.Portal>
);
});
DialogPortal.displayName = "DialogPortal";
const DialogOverlay = memo(function DialogOverlay({
className,
...props
}: React.ComponentProps<typeof BaseDialog.Backdrop>) {
return <BaseDialog.Backdrop data-slot="dialog-overlay" className={cn(OVERLAY_CLASSNAME, className)} {...props} />;
});
DialogOverlay.displayName = "DialogOverlay";
const DialogComponent = memo(function DialogComponent({ children, ...props }: DialogProps) {
return (
<BaseDialog.Root data-slot="dialog" {...props}>
{children}
</BaseDialog.Root>
);
});
DialogComponent.displayName = "Dialog";
const DialogTrigger = memo(function DialogTrigger({
children,
...props
}: React.ComponentProps<typeof BaseDialog.Trigger>) {
return (
<BaseDialog.Trigger data-slot="dialog-trigger" {...props}>
{children}
</BaseDialog.Trigger>
);
});
DialogTrigger.displayName = "DialogTrigger";
const DialogPanel = forwardRef(function DialogPanel(
{ className, width = EDialogWidth.XXL, children, position = "center", ...props }: DialogPanelProps,
ref: React.ForwardedRef<React.ElementRef<typeof BaseDialog.Popup>>
) {
const positionClassNames = useMemo(() => getPositionClassNames(position), [position]);
return (
<DialogPortal>
<DialogOverlay />
<BaseDialog.Popup
ref={ref}
data-slot="dialog-content"
className={cn(BASE_CLASSNAME, positionClassNames, width, className)}
role="dialog"
aria-modal="true"
{...props}
>
{children}
</BaseDialog.Popup>
</DialogPortal>
);
});
DialogPanel.displayName = "DialogPanel";
const DialogTitle = memo(function DialogTitle({ className, children, ...props }: DialogTitleProps) {
return (
<BaseDialog.Title
data-slot="dialog-title"
className={cn("text-16 leading-none font-semibold", className)}
{...props}
>
{children}
</BaseDialog.Title>
);
});
DialogTitle.displayName = "DialogTitle";
// Create the compound Dialog component with proper typing
const Dialog = Object.assign(DialogComponent, {
Panel: DialogPanel,
Title: DialogTitle,
}) as typeof DialogComponent & {
Panel: typeof DialogPanel;
Title: typeof DialogTitle;
};
export { Dialog, DialogTitle, DialogPanel };