Dialog component
Installation
npx shadcn@latest add @caprice/dialognpm install @caprice-ui/reactInstall the following dependencies:
npm install @base-ui/react lucide-reactAdd a cn helper
import { defineConfig, type VariantProps } from 'cva';import { twMerge } from 'tailwind-merge';export const { compose, cva, cx: cn,} = defineConfig({ hooks: { onComplete: (className: string) => twMerge(className), },});export type { VariantProps };Copy and paste the following code into your project.
'use client';import { mergePropsN, useRender } from '@base-ui/react';import { Dialog as DialogPrimitive } from '@base-ui/react/dialog';import { XIcon } from 'lucide-react';import type * as React from 'react';import { cn } from '@/lib/utils';/** * Groups all parts of the dialog. * Doesn’t render its own HTML element. * * Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog) * * API Reference: [Base UI Dialog](https://base-ui.com/react/components/dialog#root) */export namespace Root { export type Props = React.ComponentProps<typeof DialogPrimitive.Root>; export type Actions = DialogPrimitive.Root.Actions; export type ChangeEventDetails = DialogPrimitive.Root.ChangeEventDetails; export type ChangeEventReason = DialogPrimitive.Root.ChangeEventReason;}export function Root({ ...props }: Root.Props) { return <DialogPrimitive.Root data-slot="dialog" {...props} />;}/** * A button that opens the dialog. * Renders a `<button>` element. * * Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog) * * API Reference: [Base UI Dialog](https://base-ui.com/react/components/dialog#trigger) */export namespace Trigger { export type State = DialogPrimitive.Trigger.State; export type Props = React.ComponentProps<typeof DialogPrimitive.Trigger>;}export function Trigger({ ...props }: Trigger.Props) { return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;}/** * A portal element that moves the popup to a different part of the DOM. * By default, the portal element is appended to `<body>`. * Renders a `<div>` element. * * Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog) * * API Reference: [Base UI Dialog](https://base-ui.com/react/components/dialog#portal) */export namespace Portal { export interface State extends DialogPrimitive.Portal.State {} export type Props = React.ComponentProps<typeof DialogPrimitive.Portal>;}export function Portal({ ...props }: Portal.Props) { return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;}/** * A button that closes the dialog. * Renders a `<button>` element. * * Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog) * * API Reference: [Base UI Dialog](https://base-ui.com/react/components/dialog#close) */export namespace Close { export type State = DialogPrimitive.Close.State; export type Props = React.ComponentProps<typeof DialogPrimitive.Close>;}export function Close({ ...props }: Close.Props) { return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;}/** * An overlay displayed beneath the popup. * Renders a `<div>` element. * * Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog) * * API Reference: [Base UI Dialog](https://base-ui.com/react/components/dialog#backdrop) */export namespace Backdrop { export type State = DialogPrimitive.Backdrop.State; export type Props = React.ComponentProps<typeof DialogPrimitive.Backdrop>;}export function Backdrop({ className, ...props }: Backdrop.Props) { return ( <DialogPrimitive.Backdrop className={cn( 'fixed inset-0 z-50 bg-black/40 backdrop-blur-[10px] transition-all duration-200 ease-in-out [[data-starting-style],[data-ending-style]]:opacity-0', className )} data-slot="dialog-backdrop" {...props} /> );}/** * A container for the dialog contents. * Renders a `<div>` element. * * Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog) * * API Reference: [Base UI Dialog](https://base-ui.com/react/components/dialog#popup) */export namespace Popup { export type State = DialogPrimitive.Popup.State; export type Props = React.ComponentProps<typeof DialogPrimitive.Popup> & { /** * Whether to show the close button * @default true */ showCloseButton?: boolean; };}export function Popup({ className, showCloseButton = true, children, ...props }: Popup.Props) { return ( <Portal> <Backdrop /> <DialogPrimitive.Popup className={cn( // Positioning 'fixed start-1/2 z-50 -translate-x-1/2 max-sm:bottom-2 sm:-translate-y-1/2', // Sizing 'max-h-[calc(100vh-1rem)] w-full max-w-[calc(100%-4rem)] overflow-y-auto sm:max-w-lg', // Display 'grid gap-4 rounded-2xl border bg-popover p-6 text-popover-foreground shadow-lg', // Animation 'origin-top transition-all duration-200 ease-in-out will-change-transform motion-reduce:transition-none [[data-starting-style],[data-ending-style]]:scale-98 [[data-starting-style],[data-ending-style]]:opacity-0 max-sm:[[data-starting-style],[data-ending-style]]:translate-y-4', // Nested dialogs 'data-nested-dialog-open:after:absolute data-nested-dialog-open:after:inset-0 data-nested-dialog-open:after:backdrop-blur-xs sm:top-[calc(50%+1.25rem*var(--nested-dialogs))] sm:scale-[calc(1-0.1*var(--nested-dialogs))] sm:data-nested:[[data-starting-style],[data-ending-style]]:translate-y-8', className )} data-slot="dialog-popup" {...props} > {children} {showCloseButton && ( <Close className="pointer-coarse:touch-hitbox absolute end-4 top-4 rounded-md p-1.5 opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0" data-slot="dialog-close" > <XIcon /> <span className="sr-only">Close</span> </Close> )} </DialogPrimitive.Popup> </Portal> );}/** * @deprecated Use {@link Popup} instead * @see {@link https://caprice-ui.com/docs/components/dialog} */export const Content = Popup;/** * A container for the dialog header. * Renders a `<div>` element. * * Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog) */export namespace Header { export type Props = useRender.ComponentProps<'div'>;}export function Header({ className, render, ...props }: Header.Props) { const defaultProps: useRender.ElementProps<'div'> & { 'data-slot'?: string } = { 'data-slot': 'dialog-header', className: cn('flex flex-col gap-2 text-center sm:text-start', className), }; return useRender({ defaultTagName: 'div', render, props: mergePropsN<'div'>([defaultProps, props]), });}/** * A heading that labels the dialog. * Renders an `<h2>` element. * * Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog) * * API Reference: [Base UI Dialog](https://base-ui.com/react/components/dialog#title) */export namespace Title { export type State = DialogPrimitive.Title.State; export type Props = React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>;}export function Title({ className, ...props }: Title.Props) { return ( <DialogPrimitive.Title className={cn('font-semibold text-lg leading-none', className)} data-slot="dialog-title" {...props} /> );}/** * A paragraph with additional information about the dialog. * Renders a `<p>` element. * * Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog) * * API Reference: [Base UI Dialog](https://base-ui.com/react/components/dialog#description) */export namespace Description { export type State = DialogPrimitive.Description.State; export type Props = React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>;}export function Description({ className, ...props }: Description.Props) { return ( <DialogPrimitive.Description className={cn('text-muted-foreground text-sm', className)} data-slot="dialog-description" {...props} /> );}/** * A container for the dialog footer. * Renders a `<div>` element. * * Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog) */export namespace Footer { export type Props = useRender.ComponentProps<'div'>;}export function Footer({ className, render, ...props }: Footer.Props) { const defaultProps: useRender.ElementProps<'div'> & { 'data-slot'?: string } = { 'data-slot': 'dialog-footer', className: cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className), }; return useRender({ defaultTagName: 'div', render, props: mergePropsN<'div'>([defaultProps, props]), });}Update the import paths to match your project setup.
Usage
import * as Dialog from "@/components/caprice-ui/dialog"import * as Dialog from "@caprice-ui/react/dialog"