shadimage
lumiui
blockdialog-02source: ok · hit

Dialog 02

Image gallery with nested auth dialogs. Inspired by Unsplash.

npx shadcn add "https://www.lumiui.dev/r/dialog-02.json"
free

Where it goes

gallery

Interaction

modal

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
iconslucide-react

Registry dependencies

@lumi-ui/dialog@lumi-ui/alert-dialog@lumi-ui/dropdown-menu@lumi-ui/button@lumi-ui/select@lumi-ui/toast@lumi-ui/separator

Files8 · source cached 10/15/2026, 7:52:22 AM

registry/blocks/dialog-02/page.tsx
"use client";

import type { DialogRootChangeEventDetails } from "@base-ui/react";
import { ChevronLeft, ChevronRight, XIcon } from "lucide-react";
import * as React from "react";
import { Button } from "@/registry/ui/button";
import {
  createDialogHandle,
  Dialog,
  DialogBackdrop,
  DialogClose,
  DialogPopup,
  DialogPortal,
  DialogTrigger,
  DialogViewport,
} from "@/registry/ui/dialog";
import { InfoMenu } from "./components/info-menu";
import { ProfileCard } from "./components/profile-card";
import { ShareMenu } from "./components/share-menu";
import { TooltipGroup } from "./components/tooltip-group";

const galleryHandle = createDialogHandle<IImage>();

export default function UnsplashDialog() {
  const [open, setOpen] = React.useState(false);
  const [triggerId, setTriggerId] = React.useState<string | null>(null);

  const handleOpenChange = (
    isOpen: boolean,
    eventDetails: DialogRootChangeEventDetails,
  ) => {
    setOpen(isOpen);
    setTriggerId(eventDetails.trigger?.id ?? null);
  };
  const navigate = React.useCallback(
    (direction: "next" | "prev") => {
      if (!triggerId) return;
      const currentIndex = parseInt(triggerId.replace("trigger-", ""), 10);
      if (Number.isNaN(currentIndex)) return;

      let nextIndex = 0;
      if (direction === "next") {
        nextIndex = (currentIndex + 1) % IMAGES.length;
      } else {
        nextIndex = (currentIndex - 1 + IMAGES.length) % IMAGES.length;
      }

      setTriggerId(`trigger-${nextIndex}`);
    },
    [triggerId],
  );

  return (
    <>
      <div className="grid grid-cols-3 gap-4 max-w-4xl">
        {IMAGES.map((img, i) => (
          <DialogTrigger
            handle={galleryHandle}
            id={`trigger-${i}`}
            key={img.alt}
            payload={img}
            render={
              <button
                className="relative aspect-square overflow-hidden rounded-lg cursor-pointer"
                type="button"
              >
                <img
                  alt={img.alt}
                  className="h-full w-full object-cover"
                  src={img.src}
                />
              </button>
            }
          />
        ))}
      </div>
      <Dialog
        handle={galleryHandle}
        onOpenChange={handleOpenChange}
        open={open}
        triggerId={triggerId}
      >
        {({ payload }) => (
          <DialogPortal>
            <DialogBackdrop />
            <DialogViewport className="grid place-items-center overflow-y-auto">
              <DialogPopup className="flex h-full w-full justify-center pointer-events-none bg-transparent outline-0 animate-fade pt-12 sm:px-12">
                <div className="pointer-events-auto h-full w-full flex flex-col shadow-md bg-background sm:max-w-[calc(100vw-12rem)] rounded-2xl">
                  <DialogClose
                    className="fixed left-2 top-2 rounded-full"
                    render={
                      <Button
                        className="size-8 rounded-full text-white/70 hover:bg-white/20 hover:text-white"
                        size="icon"
                        variant="ghost"
                      >
                        <XIcon className="size-6" />
                        <span className="sr-only">Close</span>
                      </Button>
                    }
                  />
                  <DialogClose
                    className="absolute -top-12 right-0 md:hidden"
                    render={
                      <Button
                        className="rounded-full text-white/70 hover:bg-white/20 hover:text-white"
                        size="icon"
                        variant="ghost"
                      >
                        <XIcon className="size-6" />
                        <span className="sr-only">Close</span>
                      </Button>
                    }
                  />
                  <Button
                    className="fixed hidden md:flex left-4 top-1/2 -translate-y-1/2 rounded-full text-white/70 hover:bg-white/20 hover:text-white md:left-8 md:size-12"
                    onClick={() => navigate("prev")}
                    size="icon"
                    variant="ghost"
                  >
                    <ChevronLeft className="size-8" />
                    <span className="sr-only">Previous image</span>
                  </Button>

                  <Button
                    className="fixed hidden md:flex right-4 top-1/2 -translate-y-1/2 rounded-full text-white/70 hover:bg-white/20 hover:text-white md:right-8 md:size-12"
                    onClick={() => navigate("next")}
                    size="icon"
                    variant="ghost"
                  >
                    <ChevronRight className="size-8" />
                    <span className="sr-only">Next image</span>
                  </Button>
                  <div className="relative grid grid-rows-[auto_1fr_auto] h-[calc(100vh-3rem)]">
                    <div className="flex justify-between px-4 py-2">
                      <ProfileCard />
                      {payload && <TooltipGroup currentImage={payload} />}
                    </div>

                    {payload && (
                      <div className="flex flex-col max-w-4xl mx-auto items-center justify-center flex-1 w-full">
                        <img
                          alt={payload.alt}
                          className="h-full w-full h-[70vh] rounded-md object-contain"
                          key={payload.src}
                          src={payload.src}
                        />
                      </div>
                    )}

                    {payload && (
                      <div className="flex gap-8 p-4 text-sm justify-between">
                        <div className="flex gap-12">
                          <div className="flex flex-col gap-2">
                            <div className="text-muted-foreground font-semibold">
                              Views
                            </div>
                            <div>{payload?.views}</div>
                          </div>
                          <div className="flex flex-col gap-2">
                            <div className="text-muted-foreground font-semibold">
                              Downloads
                            </div>
                            <div>{payload?.downloads}</div>
                          </div>
                        </div>

                        <div className="flex gap-2">
                          <ShareMenu />
                          <InfoMenu />
                        </div>
                      </div>
                    )}
                  </div>
                  <div className="max-w-7xl flex-1 w-full mx-auto space-y-4 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 min-h-screen pt-6">
                    {Array.from({ length: 12 }).map((_, i) => (
                      <div
                        className="flex h-64 w-full shrink-0 items-center justify-center rounded-md bg-muted"
                        key={String(i)}
                      />
                    ))}
                  </div>
                </div>
              </DialogPopup>
            </DialogViewport>
          </DialogPortal>
        )}
      </Dialog>
    </>
  );
}

const IMAGES: IImage[] = [
  {
    alt: "Brown trees beside river under blue sky during daytime",
    downloads: 3578,
    src: "https://images.unsplash.com/photo-1579353977828-2a4eab540b9a?q=80&w=1000&auto=format&fit=crop",
    views: 5726,
  },
  {
    alt: "People watching illuminated japanese lanterns",
    downloads: 373,
    src: "https://images.unsplash.com/photo-1765614244329-22ca4157345a?q=80&w=1000&auto=format&fit=crop",
    views: 1253,
  },
  {
    alt: "Abstract colorful waves",
    downloads: 2354,
    src: "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&w=1000&auto=format&fit=crop",
    views: 7676,
  },
];

export type IImage = {
  alt: string;
  src: string;
  views: number;
  downloads: number;
};
registry/blocks/dialog-02/components/info-menu.tsx
import { InfoIcon } from "lucide-react";
import { Button } from "@/registry/ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "@/registry/ui/popover";
import { Tabs, TabsListContent, TabsPanel, TabsTab } from "@/registry/ui/tabs";
import { StatsChart } from "./stats-chart";

export const InfoMenu = () => {
  return (
    <Popover>
      <PopoverTrigger render={<Button variant="outline" />}>
        <InfoIcon /> Info
      </PopoverTrigger>
      <PopoverContent align="end" className="bg-secondary" side="top">
        <Tabs className="w-72" defaultValue="views">
          <TabsPanel value="downloads">
            <StatsChart data="downloads" />
          </TabsPanel>
          <TabsPanel value="views">
            <StatsChart data="views" />
          </TabsPanel>
          <TabsListContent>
            <TabsTab value="downloads">Downloads</TabsTab>
            <TabsTab value="views">Views</TabsTab>
          </TabsListContent>
        </Tabs>
      </PopoverContent>
    </Popover>
  );
};
registry/blocks/dialog-02/components/stats-chart.tsx
"use client";

import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
import {
  type ChartConfig,
  ChartContainer,
  ChartTooltip,
  ChartTooltipContent,
} from "@/registry/ui/chart";

const chartConfig: ChartConfig = {
  downloads: {
    color: "var(--color-green-100)",
    label: "Downloads",
  },
  views: {
    color: "var(--color-green-100)",
    label: "Views",
  },
};

export const StatsChart = ({
  data = "views",
}: {
  data?: "views" | "downloads";
}) => {
  return (
    <ChartContainer
      className="aspect-auto h-[120px] w-full"
      config={chartConfig}
    >
      <BarChart
        accessibilityLayer
        data={chartData}
        margin={{
          left: 12,
          right: 12,
        }}
      >
        <CartesianGrid vertical={false} />
        <XAxis dataKey="date" hide />
        <ChartTooltip
          content={
            <ChartTooltipContent
              className="bg-foreground text-background border-transparent shadow-none w-fit"
              hideIndicator
              labelFormatter={(value) => {
                return new Date(value as string).toLocaleDateString("en-US", {
                  day: "numeric",
                  month: "short",
                  year: "numeric",
                });
              }}
            />
          }
          cursor={false}
        />
        <Bar
          className="hover:fill-green-600 transition-colors duration-50"
          dataKey={data}
          fill={`var(--color-${data})`}
          radius={4}
        />
      </BarChart>
    </ChartContainer>
  );
};

const chartData = [
  { date: "2025-12-24", downloads: 350, views: 6452 },
  { date: "2025-12-25", downloads: 120, views: 1945 },
  { date: "2025-12-26", downloads: 420, views: 6576 },
  { date: "2025-12-27", downloads: 180, views: 2371 },
  { date: "2025-12-28", downloads: 150, views: 2335 },
  { date: "2025-12-29", downloads: 380, views: 6237 },
  { date: "2025-12-30", downloads: 520, views: 7528 },
  { date: "2025-12-31", downloads: 110, views: 1944 },
  { date: "2026-01-01", downloads: 290, views: 4837 },
  { date: "2026-01-02", downloads: 160, views: 2358 },
  { date: "2026-01-03", downloads: 140, views: 2350 },
  { date: "2026-01-04", downloads: 410, views: 6734 },
  { date: "2026-01-05", downloads: 90, views: 1232 },
  { date: "2026-01-06", downloads: 230, views: 3677 },
  { date: "2026-01-07", downloads: 85, views: 1269 },
  { date: "2026-01-08", downloads: 115, views: 1658 },
  {
    date: "2026-01-09",
    downloads: 145,
    views: 2001,
  },
  {
    date: "2026-01-10",
    downloads: 395,
    views: 6352,
  },
  {
    date: "2026-01-11",
    downloads: 190,
    views: 2954,
  },
  {
    date: "2026-01-12",
    downloads: 310,
    views: 4590,
  },
  {
    date: "2026-01-13",
    downloads: 110,
    views: 1843,
  },
];
registry/blocks/dialog-02/components/login-form.tsx
"use client";

import { Chrome, Eye, EyeOff, Github } from "lucide-react";
import * as React from "react";
import { Button } from "@/registry/ui/button";
import {
  Field,
  FieldControl,
  FieldError,
  FieldLabel,
} from "@/registry/ui/field";
import { Form } from "@/registry/ui/form";
import { Input } from "@/registry/ui/input";
import { Tabs, TabsListContent, TabsPanel, TabsTab } from "@/registry/ui/tabs";

export function LoginForm({ onSuccess }: { onSuccess: () => void }) {
  const [isLoading, setIsLoading] = React.useState(false);
  const [showPassword, setShowPassword] = React.useState(false);

  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setIsLoading(true);
    setTimeout(() => {
      setIsLoading(false);
      onSuccess();
    }, 2000);
  };

  return (
    <div className="p-4 sm:p-8">
      <div className="text-center space-y-2">
        <h1 className="text-3xl font-bold tracking-tight text-foreground">
          Welcome back
        </h1>
        <p className="text-sm text-muted-foreground">
          Enter your credentials to access your account
        </p>
      </div>
      <Tabs className="w-full mt-6" defaultValue="email">
        <TabsListContent>
          <TabsTab value="email">Email</TabsTab>
          <TabsTab value="phone">Phone</TabsTab>
        </TabsListContent>

        <TabsPanel className="py-4" value="email">
          <Form onSubmit={handleSubmit}>
            <Field>
              <FieldLabel>Email</FieldLabel>
              <FieldControl
                name="email"
                placeholder="name@example.com"
                required
                type="email"
              />
              <FieldError />
            </Field>
            <Field>
              <div className="flex items-center justify-between">
                <FieldLabel>Password</FieldLabel>
                <Button
                  className="p-0 h-auto font-normal text-xs"
                  variant="link"
                >
                  Forgot password?
                </Button>
              </div>
              <div className="relative">
                <Input
                  className="pr-10"
                  name="password"
                  placeholder="Enter your password"
                  required
                  type={showPassword ? "text" : "password"}
                />
                <Button
                  className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent"
                  onClick={() => setShowPassword(!showPassword)}
                  size="icon"
                  type="button"
                  variant="ghost"
                >
                  {showPassword ? (
                    <EyeOff className="size-4 text-muted-foreground" />
                  ) : (
                    <Eye className="size-4 text-muted-foreground" />
                  )}
                  <span className="sr-only">
                    {showPassword ? "Hide password" : "Show password"}
                  </span>
                </Button>
              </div>
              <FieldError />
            </Field>
            <Button className="w-full" disabled={isLoading} type="submit">
              {isLoading ? "Signing in..." : "Sign In"}
            </Button>
          </Form>
        </TabsPanel>

        <TabsPanel className="py-4" value="phone">
          <Form onSubmit={handleSubmit}>
            <Field>
              <FieldLabel>Phone Number</FieldLabel>
              <FieldControl
                name="phone"
                placeholder="+1 (555) 000-0000"
                required
                type="tel"
              />
              <FieldError />
            </Field>
            <Field>
              <div className="flex items-center justify-between">
                <FieldLabel>Verification Code</FieldLabel>
                <Button
                  className="p-0 h-auto text-xs"
                  type="button"
                  variant="link"
                >
                  Send code
                </Button>
              </div>
              <FieldControl
                maxLength={6}
                name="code"
                placeholder="Enter 6-digit code"
                required
                type="text"
              />
              <FieldError />
            </Field>
            <Button className="w-full" disabled={isLoading} type="submit">
              {isLoading ? "Signing in..." : "Sign In"}
            </Button>
          </Form>
        </TabsPanel>
      </Tabs>
      <div className="space-y-4">
        <div className="relative">
          <div className="relative flex justify-center text-xs uppercase">
            <span className="px-2 text-muted-foreground">Or continue with</span>
          </div>
        </div>
        <div className="grid grid-cols-2 gap-4">
          <Button className="w-full" type="button" variant="outline">
            <Github className="mr-2 size-4" />
            GitHub
          </Button>
          <Button className="w-full" type="button" variant="outline">
            <Chrome className="mr-2 size-4" />
            Google
          </Button>
        </div>
      </div>
    </div>
  );
}
registry/blocks/dialog-02/components/signup-form.tsx
"use client";

import * as React from "react";
import { Button } from "@/registry/ui/button";
import {
  Field,
  FieldControl,
  FieldError,
  FieldLabel,
} from "@/registry/ui/field";
import { Form } from "@/registry/ui/form";
import { Input } from "@/registry/ui/input";

export type IImage = {
  alt: string;
  src: string;
  views: number;
  downloads: number;
};

interface SignUpFormProps {
  image: IImage;
  onSuccess?: () => void;
}

export function SignUpForm({ image, onSuccess }: SignUpFormProps) {
  const [isLoading, setIsLoading] = React.useState(false);

  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setIsLoading(true);

    setTimeout(() => {
      setIsLoading(false);
      if (onSuccess) onSuccess();
    }, 2000);
  };

  return (
    <div className="flex max-h-full">
      <div className="hidden w-1/3 lg:block relative">
        <img
          alt={image.alt}
          className="h-full w-full object-cover rounded-l-md"
          src={image.src}
        />
        <div className="absolute bottom-4 left-4 text-white/80 text-xs bg-black/20 p-2 rounded backdrop-blur-sm">
          Photo viewed {image.views.toLocaleString()} times
        </div>
      </div>

      <div className="flex w-full flex-col justify-center p-4 sm:p-8 lg:w-2/3">
        <div className="mx-auto w-full max-w-md space-y-8">
          <div className="text-center">
            <h1 className="text-4xl font-bold tracking-tight text-foreground">
              Join Us
            </h1>
            <p className="mt-2 text-muted-foreground">
              Already have an account? <Button variant={"link"}>Login</Button>
            </p>
          </div>

          <Form className="space-y-6" onSubmit={handleSubmit}>
            <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
              <Field>
                <FieldLabel>First name</FieldLabel>
                <FieldControl name="firstName" placeholder="" required />
                <FieldError />
              </Field>
              <Field>
                <FieldLabel>Last name</FieldLabel>
                <FieldControl name="lastName" placeholder="" required />
                <FieldError />
              </Field>
            </div>

            <Field>
              <FieldLabel>Email</FieldLabel>
              <FieldControl name="email" required type="email" />
              <FieldError />
            </Field>

            <Field>
              <FieldLabel className="flex gap-1">
                Username
                <span className="font-normal text-muted-foreground">
                  (only letters, numbers and underscores)
                </span>
              </FieldLabel>
              <FieldControl name="username" pattern="[a-zA-Z0-9_]+" required />
              <FieldError />
            </Field>

            <Field>
              <FieldLabel className="flex gap-1">
                Password
                <span className="font-normal text-muted-foreground">
                  (min. 8 char)
                </span>
              </FieldLabel>
              <Input minLength={8} name="password" required type="password" />
              <FieldError />
            </Field>

            <Button disabled={isLoading} type="submit">
              {isLoading ? "Joining..." : "Join"}
            </Button>
          </Form>

          <p className="text-center text-sm text-muted-foreground">
            By joining, you agree to the{" "}
            <Button className="px-0" variant={"link"}>
              Terms
            </Button>{" "}
            and{" "}
            <Button className="px-0" variant={"link"}>
              Privacy Policy
            </Button>
            .
          </p>
        </div>
      </div>
    </div>
  );
}
registry/blocks/dialog-02/components/share-menu.tsx
"use client";

import {
  FacebookIcon,
  InstagramIcon,
  LinkIcon,
  ShareIcon,
  TwitterIcon,
} from "lucide-react";
import { Button } from "@/registry/ui/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuGroupLabel,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/registry/ui/dropdown-menu";
import { toast } from "@/registry/ui/toast";

export const ShareMenu = () => {
  const handleCopy = () => {
    navigator.clipboard.writeText("https://lumiui.dev");

    toast.success({
      title: "Copied",
    });
  };
  return (
    <DropdownMenu>
      <DropdownMenuTrigger
        className="w-20"
        render={
          <Button variant="outline">
            <ShareIcon className="size-4" /> Share
          </Button>
        }
      />
      <DropdownMenuContent align="end" matchAnchorWidth={false} side="top">
        <DropdownMenuGroup>
          <DropdownMenuGroupLabel>Socials</DropdownMenuGroupLabel>
          <DropdownMenuItem>
            <FacebookIcon className="size-4" /> <span>Facebook</span>
          </DropdownMenuItem>
          <DropdownMenuItem>
            <TwitterIcon className="size-4" /> <span>Twitter</span>
          </DropdownMenuItem>
          <DropdownMenuItem>
            <InstagramIcon className="size-4" /> <span>Instagram</span>
          </DropdownMenuItem>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuItem onClick={handleCopy}>
          <LinkIcon className="size-4" /> Copy link
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
};
registry/blocks/dialog-02/components/tooltip-group.tsx
import type { DialogRootChangeEventDetails } from "@base-ui/react";
import { BookmarkIcon, PlusIcon } from "lucide-react";
import React from "react";
import { cn } from "@/registry/lib/utils";
import { Button } from "@/registry/ui/button";
import {
  createDialogHandle,
  Dialog,
  DialogBackdrop,
  DialogPopup,
  DialogPortal,
  DialogTrigger,
} from "@/registry/ui/dialog";
import { toast } from "@/registry/ui/toast";
import {
  createTooltipHandle,
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/registry/ui/tooltip";
import type { IImage } from "../page";
import { LoginForm } from "./login-form";
import { SignUpForm } from "./signup-form";

type UserAction = "bookmark" | "add-collection";
type FormType = "signin" | "signup";

type DialogPayload = {
  formType: FormType;
  action: UserAction;
  title: string;
};

const dialogHandle = createDialogHandle<DialogPayload>();
const tooltipHandle = createTooltipHandle<{ label: string }>();

export const TooltipGroup = ({ currentImage }: { currentImage: IImage }) => {
  const [isOpen, setIsOpen] = React.useState(false);
  const [triggerId, setTriggerId] = React.useState<string | null>(null);

  const handleOpenChange = (
    open: boolean,
    eventDetails: DialogRootChangeEventDetails,
  ) => {
    setIsOpen(open);
    if (eventDetails.trigger) {
      setTriggerId(eventDetails.trigger.id);
    }
  };

  const handleSuccess = (action: UserAction) => {
    setIsOpen(false);

    const messages: Record<UserAction, string> = {
      "add-collection": `Added ${currentImage.alt} to collection`,
      bookmark: `Bookmarked ${currentImage.alt}`,
    };

    toast.success({
      description: messages[action] || "Action completed successfully",
      title: "Success",
    });
  };

  return (
    <>
      <TooltipProvider>
        <div className="flex gap-2">
          <TooltipTrigger
            handle={tooltipHandle}
            id="trigger-bookmark"
            payload={{ label: "Bookmark this page" }}
            render={
              <DialogTrigger
                handle={dialogHandle}
                payload={{
                  action: "bookmark",
                  formType: "signin",
                  title: "Welcome Back",
                }}
                render={<Button size={"icon-sm"} variant={"outline"} />}
              >
                <BookmarkIcon aria-label="Bookmark" className="size-5" />
              </DialogTrigger>
            }
          />

          <TooltipTrigger
            handle={tooltipHandle}
            id="trigger-collection"
            payload={{ label: "Add to collection" }}
            render={
              <DialogTrigger
                handle={dialogHandle}
                payload={{
                  action: "add-collection",
                  formType: "signup",
                  title: "Create Account",
                }}
                render={<Button size={"icon-sm"} variant={"outline"} />}
              >
                <PlusIcon aria-label="Add to collection" className="size-5" />
              </DialogTrigger>
            }
          />

          <Tooltip handle={tooltipHandle}>
            {({ payload }) => (
              <TooltipContent showArrow={false} side="bottom" sideOffset={5}>
                <p>{payload?.label}</p>
              </TooltipContent>
            )}
          </Tooltip>
        </div>
      </TooltipProvider>

      <Dialog
        handle={dialogHandle}
        onOpenChange={handleOpenChange}
        open={isOpen}
        triggerId={triggerId}
      >
        {({ payload }) => {
          return (
            <DialogPortal>
              <DialogBackdrop
                className="backdrop-blur-none bg-black/40"
                forceRender
              />
              <DialogPopup
                className={cn(
                  "fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-background rounded-md shadow-md dark:shadow-xs w-full",
                  payload?.formType === "signin" && "max-w-md",
                  payload?.formType === "signup" && "max-w-2xl",
                )}
              >
                {payload?.formType === "signin" && (
                  <LoginForm onSuccess={() => handleSuccess(payload.action)} />
                )}
                {payload?.formType === "signup" && (
                  <SignUpForm
                    image={currentImage}
                    onSuccess={() => handleSuccess(payload.action)}
                  />
                )}
              </DialogPopup>
            </DialogPortal>
          );
        }}
      </Dialog>
    </>
  );
};
registry/blocks/dialog-02/components/profile-card.tsx
import { Avatar, AvatarFallback, AvatarImage } from "@/registry/ui/avatar";
import { Button } from "@/registry/ui/button";
import {
  PreviewCard,
  PreviewCardContent,
  PreviewCardTrigger,
} from "@/registry/ui/preview-card";

export function ProfileCard() {
  return (
    <PreviewCard>
      <PreviewCardTrigger
        delay={300}
        render={
          <Button className="cursor-pointer" variant="unstyled">
            <Avatar>
              <AvatarImage src="https://github.com/mui.png" />
              <AvatarFallback>VC</AvatarFallback>
            </Avatar>
          </Button>
        }
      />
      <PreviewCardContent align="start" className="w-80">
        <div className="flex justify-between gap-4">
          <Avatar>
            <AvatarImage src="https://github.com/mui.png" />
            <AvatarFallback>VC</AvatarFallback>
          </Avatar>
          <div className="space-y-1">
            <h4 className="text-sm font-semibold">@base-ui</h4>
            <p className="text-sm">
              Unstyled UI components for building accessible web apps and design
              systems. From the creators of Radix, Floating UI, and Material UI.
            </p>
            <div className="text-muted-foreground text-xs">
              Joined December 2021
            </div>
          </div>
        </div>
      </PreviewCardContent>
    </PreviewCard>
  );
}

More from lumiui

Browse all components in this library →