"use client"; import { createContext, forwardRef, useContext, useState, type CSSProperties, type ReactNode, } from "react"; import * as DialogPrimitive from "@radix-ui/react-dialog"; import { AnimatePresence, motion } from "framer-motion"; import { useAnimate } from "../animations"; import { useResolvedSeed } from "../hooks/useResolvedSeed"; import { useSketchTheme } from "../hooks/useSketchTheme"; import { SketchBox } from "../primitives/SketchBox"; import type { SketchProps } from "../types"; import { cn, doodleUiFontFamily, doodleUiFontWeight } from "../utils"; interface DialogSketchContextValue extends SketchProps { resolvedSeed: number; ink: string; paper: string; isDark: boolean; /** * Play sketch draw-in animations. Defaults to {@link DoodleUIProvider} `animate` (true). * @default undefined (follow provider) */ animate?: boolean; open: boolean; } const DialogSketchContext = createContext( null, ); function useDialogSketch(): DialogSketchContextValue { const ctx = useContext(DialogSketchContext); if (!ctx) { throw new Error("Dialog parts must be used inside ."); } return ctx; } /** * Props for {@link Dialog}. */ export interface DialogProps extends Omit, SketchProps { children?: ReactNode; fill?: string; /** * Backdrop fade, panel enter/exit, and border draw-in on open. * Defaults to the DoodleUIProvider value (true). */ /** * Play sketch draw-in animations. Defaults to {@link DoodleUIProvider} `animate` (true). * @default undefined (follow provider) */ animate?: boolean; } /** * Lighter-weight sibling to `Modal`: a composable, shadcn-style dialog built * from `Dialog` / `DialogTrigger` / `DialogContent` / `DialogHeader` / * `DialogFooter` / `DialogTitle` / `DialogDescription` / `DialogClose`. Unlike * `Modal`, `DialogContent` has no forced width or shadow and no built-in * close button — compose your own layout inside it. */ /** * Composable dialog with sketch framing. * * @example * */ export function Dialog({ children, open, defaultOpen, onOpenChange, fill, roughness, seed, sketchColor, bowing, fillStyle, strokeWidth, hachureGap, hachureAngle, fillWeight, animate, ...rest }: DialogProps) { const resolvedSeed = useResolvedSeed(seed); const theme = useSketchTheme(sketchColor); const ink = sketchColor ?? theme.ink; const [uncontrolled, setUncontrolled] = useState(defaultOpen === true); const isOpen = open ?? uncontrolled; return ( { setUncontrolled(next); onOpenChange?.(next); }} {...rest} > {children} ); } export const DialogTrigger = DialogPrimitive.Trigger; export const DialogPortal = DialogPrimitive.Portal; export const DialogClose = DialogPrimitive.Close; /** * Props for {@link DialogOverlay}. */ export interface DialogOverlayProps extends Omit {} export const DialogOverlay = forwardRef( function DialogOverlay({ style, ...rest }, ref) { const sketch = useDialogSketch(); const shouldAnimate = useAnimate(sketch.animate); return ( ); }, ); /** * Props for {@link DialogContent}. */ export interface DialogContentProps extends Omit { children?: ReactNode; fill?: string; /** Panel width. Defaults to `min(420px, calc(100vw - 32px))`. */ contentStyle?: CSSProperties; } export const DialogContent = forwardRef( function DialogContent( { className, style, contentStyle, fill, children, ...rest }, ref, ) { const sketch = useDialogSketch(); const shouldAnimate = useAnimate(sketch.animate); return ( {sketch.open ? (
{children}
) : null}
); }, ); /** * Props for {@link DialogHeader}. */ export interface DialogHeaderProps { children?: ReactNode; className?: string; style?: CSSProperties; } export function DialogHeader({ children, className, style }: DialogHeaderProps) { return (
{children}
); } /** * Props for {@link DialogFooter}. */ export interface DialogFooterProps { children?: ReactNode; className?: string; style?: CSSProperties; } export function DialogFooter({ children, className, style }: DialogFooterProps) { return (
{children}
); } /** * Props for {@link DialogTitle}. */ export interface DialogTitleProps extends Omit {} export const DialogTitle = forwardRef( function DialogTitle({ style, ...rest }, ref) { const sketch = useDialogSketch(); return ( ); }, ); /** * Props for {@link DialogDescription}. */ export interface DialogDescriptionProps extends Omit {} export const DialogDescription = forwardRef< HTMLParagraphElement, DialogDescriptionProps >(function DialogDescription({ style, ...rest }, ref) { const sketch = useDialogSketch(); return ( ); });