Skip to content
Lenso UI

AlertDialog 警告对话框

用于关键确认操作的模态对话框,需要用户关注并明确操作

用法

import { AlertDialog } from "@lenso/ui";

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";
// Adapted from HeroUI v3.2.6 alert-dialog-default (Apache-2.0).import * as stylex from "@stylexjs/stylex";import { AlertDialog, Button } from "@lenso/ui";
const styles = stylex.create({ popup: { maxWidth: 400 } });
export function Default() {  return (    <AlertDialog>      <AlertDialog.Trigger render={<Button variant="danger" />}>Delete Project</AlertDialog.Trigger>      <AlertDialog.Portal>        <AlertDialog.Backdrop />        <AlertDialog.Viewport>          <AlertDialog.Popup xstyle={styles.popup}>            <AlertDialog.Close aria-label="Close dialog" />            <AlertDialog.Header>              <AlertDialog.Icon variant="danger" />              <AlertDialog.Title>Delete project permanently?</AlertDialog.Title>            </AlertDialog.Header>            <AlertDialog.Body>              <AlertDialog.Description>                This will permanently delete <strong>My Awesome Project</strong> and all of its                data. This action cannot be undone.              </AlertDialog.Description>            </AlertDialog.Body>            <AlertDialog.Footer>              <AlertDialog.Close render={<Button variant="tertiary" />}>Cancel</AlertDialog.Close>              <AlertDialog.Close render={<Button variant="danger" />}>                Delete Project              </AlertDialog.Close>            </AlertDialog.Footer>          </AlertDialog.Popup>        </AlertDialog.Viewport>      </AlertDialog.Portal>    </AlertDialog>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

组件结构

import {AlertDialog, Button} from "@lenso/ui";
export default () => (  <AlertDialog>    <Button>Open Alert Dialog</Button>    <AlertDialog.Backdrop>      <AlertDialog.Container>        <AlertDialog.Dialog>          <AlertDialog.CloseTrigger /> {/* Optional: Close button */}          <AlertDialog.Header>            <AlertDialog.Icon /> {/* Optional: Status icon */}            <AlertDialog.Heading />          </AlertDialog.Header>          <AlertDialog.Body />          <AlertDialog.Footer />        </AlertDialog.Dialog>      </AlertDialog.Container>    </AlertDialog.Backdrop>  </AlertDialog>);

示例

状态类型

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 alert-dialog-statuses (Apache-2.0).import * as stylex from "@stylexjs/stylex";import { AlertDialog, Button } from "@lenso/ui";
const styles = stylex.create({  row: { display: "flex", flexWrap: "wrap", gap: 16 },  popup: { maxWidth: 400 },  accent: { backgroundColor: "var(--accent-soft)", color: "var(--accent-soft-foreground)" },  success: { backgroundColor: "var(--success-soft)", color: "var(--success-soft-foreground)" },  warning: { backgroundColor: "var(--warning-soft)", color: "var(--warning-soft-foreground)" },  danger: { backgroundColor: "var(--danger-soft)", color: "var(--danger-soft-foreground)" },});
export function Statuses() {  const examples = [    {      status: "accent",      trigger: "Sign Out",      header: "Sign out of your account?",      body: "You'll need to sign in again to access your account. Any unsaved changes will be lost.",      cancel: "Stay Signed In",      confirm: "Sign Out",    },    {      status: "success",      trigger: "Complete Task",      header: "Complete this task?",      body: "This will mark the task as complete and notify all team members. The task will be moved to your completed list.",      cancel: "Not Yet",      confirm: "Mark Complete",    },    {      status: "warning",      trigger: "Discard Changes",      header: "Discard unsaved changes?",      body: "You have unsaved changes that will be permanently lost. Are you sure you want to discard them?",      cancel: "Keep Editing",      confirm: "Discard",    },    {      status: "danger",      trigger: "Delete Account",      header: "Delete your account?",      body: "This will permanently delete your account and remove all your data from our servers. This action is irreversible.",      cancel: "Cancel",      confirm: "Delete Account",    },  ] as const;  return (    <div {...stylex.props(styles.row)}>      {examples.map(({ status, trigger, header, body, cancel, confirm }) => (        <AlertDialog key={status}>          <AlertDialog.Trigger render={<Button xstyle={styles[status]} />}>            {trigger}          </AlertDialog.Trigger>          <AlertDialog.Portal>            <AlertDialog.Backdrop />            <AlertDialog.Viewport>              <AlertDialog.Popup xstyle={styles.popup}>                <AlertDialog.Close aria-label="Close dialog" />                <AlertDialog.Header>                  <AlertDialog.Icon variant={status} />                  <AlertDialog.Title>{header}</AlertDialog.Title>                </AlertDialog.Header>                <AlertDialog.Body>                  <AlertDialog.Description>{body}</AlertDialog.Description>                </AlertDialog.Body>                <AlertDialog.Footer>                  <AlertDialog.Close render={<Button variant="tertiary" />}>                    {cancel}                  </AlertDialog.Close>                  <AlertDialog.Close                    render={<Button variant={status === "danger" ? "danger" : "primary"} />}                  >                    {confirm}                  </AlertDialog.Close>                </AlertDialog.Footer>              </AlertDialog.Popup>            </AlertDialog.Viewport>          </AlertDialog.Portal>        </AlertDialog>      ))}    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

弹出位置

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 alert-dialog-placements (Apache-2.0).import * as stylex from "@stylexjs/stylex";import { AlertDialog, Button } from "@lenso/ui";
const styles = stylex.create({  row: { display: "flex", flexWrap: "wrap", gap: 16 },  popup: { maxWidth: 400 },});
export function Placements() {  const placements = ["auto", "top", "center", "bottom"] as const;  return (    <div {...stylex.props(styles.row)}>      {placements.map((placement) => (        <AlertDialog key={placement}>          <AlertDialog.Trigger render={<Button variant="secondary" />}>            {placement.charAt(0).toUpperCase() + placement.slice(1)}          </AlertDialog.Trigger>          <AlertDialog.Portal>            <AlertDialog.Backdrop />            <AlertDialog.Viewport>              <AlertDialog.Popup placement={placement} xstyle={styles.popup}>                <AlertDialog.Close aria-label="Close dialog" />                <AlertDialog.Header>                  <AlertDialog.Icon variant="accent" />                  <AlertDialog.Title>                    {placement === "auto"                      ? "Auto Placement"                      : `${placement.charAt(0).toUpperCase() + placement.slice(1)} Position`}                  </AlertDialog.Title>                </AlertDialog.Header>                <AlertDialog.Body>                  <AlertDialog.Description>                    {placement === "auto"                      ? "Automatically positions at the bottom on mobile and center on desktop for optimal user experience."                      : `This dialog is positioned at the ${placement} of the viewport. Critical confirmations are typically centered for maximum attention.`}                  </AlertDialog.Description>                </AlertDialog.Body>                <AlertDialog.Footer>                  <AlertDialog.Close render={<Button variant="tertiary" />}>                    Cancel                  </AlertDialog.Close>                  <AlertDialog.Close render={<Button />}>Confirm</AlertDialog.Close>                </AlertDialog.Footer>              </AlertDialog.Popup>            </AlertDialog.Viewport>          </AlertDialog.Portal>        </AlertDialog>      ))}    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

尺寸

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 alert-dialog-sizes (Apache-2.0).import { Rocket } from "@gravity-ui/icons";import * as stylex from "@stylexjs/stylex";import { AlertDialog, Button } from "@lenso/ui";
const styles = stylex.create({  row: { display: "flex", flexWrap: "wrap", gap: 16 },  icon: { backgroundColor: "var(--default)", color: "var(--foreground)" },  rocket: { width: 20, height: 20 },});
export function Sizes() {  const sizes = ["xs", "sm", "md", "lg", "cover"] as const;  return (    <div {...stylex.props(styles.row)}>      {sizes.map((size) => (        <AlertDialog key={size}>          <AlertDialog.Trigger render={<Button variant="secondary" />}>            {size.charAt(0).toUpperCase() + size.slice(1)}          </AlertDialog.Trigger>          <AlertDialog.Portal>            <AlertDialog.Backdrop />            <AlertDialog.Viewport>              <AlertDialog.Popup size={size}>                <AlertDialog.Close aria-label="Close dialog" />                <AlertDialog.Header>                  <AlertDialog.Icon variant="default" xstyle={styles.icon}>                    <Rocket {...stylex.props(styles.rocket)} />                  </AlertDialog.Icon>                  <AlertDialog.Title>                    Size: {size.charAt(0).toUpperCase() + size.slice(1)}                  </AlertDialog.Title>                </AlertDialog.Header>                <AlertDialog.Body>                  <AlertDialog.Description>                    {size === "cover" ? (                      <>                        This alert dialog uses the <code>cover</code> size variant. It spans the                        full screen with margins: 16px on mobile and 40px on desktop. Maintains                        rounded corners and standard padding. Perfect for critical confirmations                        that need maximum width while preserving alert dialog aesthetics.                      </>                    ) : (                      <>                        This alert dialog uses the <code>{size}</code> size variant. On mobile                        devices, all sizes adapt to near full-width for optimal viewing. On desktop,                        each size provides a different maximum width to suit various content needs.                      </>                    )}                  </AlertDialog.Description>                </AlertDialog.Body>                <AlertDialog.Footer>                  <AlertDialog.Close render={<Button variant="tertiary" />}>                    Cancel                  </AlertDialog.Close>                  <AlertDialog.Close render={<Button />}>Confirm</AlertDialog.Close>                </AlertDialog.Footer>              </AlertDialog.Popup>            </AlertDialog.Viewport>          </AlertDialog.Portal>        </AlertDialog>      ))}    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

受控模式

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 alert-dialog-controlled (Apache-2.0).// The second controller uses a reducer instead of the React Aria overlay-state hook.import { useReducer, useState } from "react";import * as stylex from "@stylexjs/stylex";import { AlertDialog, Button } from "@lenso/ui";
const styles = stylex.create({  column: { display: "flex", maxWidth: 448, flexDirection: "column", gap: 32 },  section: { display: "flex", flexDirection: "column", gap: 12 },  heading: { fontSize: 18, fontWeight: 600, color: "var(--foreground)" },  caption: { fontSize: 14, lineHeight: 1.625, textWrap: "pretty", color: "var(--muted)" },  panel: {    display: "flex",    flexDirection: "column",    alignItems: "flex-start",    gap: 12,    borderRadius: 16,    backgroundColor: "var(--surface)",    padding: 16,    boxShadow: "var(--shadow-sm)",  },  status: { fontSize: 12, color: "var(--muted)" },  value: { fontFamily: "monospace", fontWeight: 500, color: "var(--foreground)" },  actions: { display: "flex", gap: 8 },  popup: { maxWidth: 400 },});
export function Controlled() {  const [isOpen, setIsOpen] = useState(false);  const [reducerOpen, dispatch] = useReducer(    (open: boolean, action: "open" | "close" | "toggle") =>      action === "toggle" ? !open : action === "open",    false,  );  return (    <div {...stylex.props(styles.column)}>      {([false, true] as const).map((reducer) => {        const open = reducer ? reducerOpen : isOpen;        const setOpen = (value: boolean) =>          reducer ? dispatch(value ? "open" : "close") : setIsOpen(value);        return (          <div key={String(reducer)} {...stylex.props(styles.section)}>            <h3 {...stylex.props(styles.heading)}>              {reducer ? "With useReducer()" : "With React.useState()"}            </h3>            <p {...stylex.props(styles.caption)}>              {reducer ? (                "Use a reducer to manage open, close, and toggle actions."              ) : (                <>                  Control the alert dialog using React&apos;s <code>useState</code> hook for simple                  state management. Perfect for basic use cases.                </>              )}            </p>            <div {...stylex.props(styles.panel)}>              <p {...stylex.props(styles.status)}>                Status: <span {...stylex.props(styles.value)}>{open ? "open" : "closed"}</span>              </p>              <div {...stylex.props(styles.actions)}>                <Button size="sm" variant="secondary" onClick={() => setOpen(true)}>                  Open Dialog                </Button>                <Button                  size="sm"                  variant="tertiary"                  onClick={() => (reducer ? dispatch("toggle") : setIsOpen((value) => !value))}                >                  Toggle                </Button>              </div>            </div>            <AlertDialog open={open} onOpenChange={setOpen}>              <AlertDialog.Portal>                <AlertDialog.Backdrop />                <AlertDialog.Viewport>                  <AlertDialog.Popup xstyle={styles.popup}>                    <AlertDialog.Close aria-label="Close dialog" />                    <AlertDialog.Header>                      <AlertDialog.Icon variant={reducer ? "success" : "accent"} />                      <AlertDialog.Title>                        Controlled with {reducer ? "useReducer()" : "useState()"}                      </AlertDialog.Title>                    </AlertDialog.Header>                    <AlertDialog.Body>                      <AlertDialog.Description>                        {reducer ? (                          "The reducer handles open, close, and toggle actions while the native Root owns focus and dismissal."                        ) : (                          <>                            This alert dialog is controlled by React&apos;s <code>useState</code>{" "}                            hook. Pass <code>open</code> and <code>onOpenChange</code> props to                            manage the dialog state externally.                          </>                        )}                      </AlertDialog.Description>                    </AlertDialog.Body>                    <AlertDialog.Footer>                      <AlertDialog.Close render={<Button variant="tertiary" />}>                        Cancel                      </AlertDialog.Close>                      <AlertDialog.Close render={<Button />}>Confirm</AlertDialog.Close>                    </AlertDialog.Footer>                  </AlertDialog.Popup>                </AlertDialog.Viewport>              </AlertDialog.Portal>            </AlertDialog>          </div>        );      })}    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

自定义图标

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 alert-dialog-custom-icon (Apache-2.0).import { LockOpen } from "@gravity-ui/icons";import * as stylex from "@stylexjs/stylex";import { AlertDialog, Button } from "@lenso/ui";
const styles = stylex.create({  popup: { maxWidth: 400 },  lock: { width: 20, height: 20 },});
export function CustomIcon() {  return (    <AlertDialog>      <AlertDialog.Trigger render={<Button variant="secondary" />}>        Reset Password      </AlertDialog.Trigger>      <AlertDialog.Portal>        <AlertDialog.Backdrop />        <AlertDialog.Viewport>          <AlertDialog.Popup xstyle={styles.popup}>            <AlertDialog.Close aria-label="Close dialog" />            <AlertDialog.Header>              <AlertDialog.Icon variant="warning">                <LockOpen {...stylex.props(styles.lock)} />              </AlertDialog.Icon>              <AlertDialog.Title>Reset your password?</AlertDialog.Title>            </AlertDialog.Header>            <AlertDialog.Body>              <AlertDialog.Description>                We&apos;ll send a password reset link to your email address. You&apos;ll need to                create a new password to regain access to your account.              </AlertDialog.Description>            </AlertDialog.Body>            <AlertDialog.Footer>              <AlertDialog.Close render={<Button variant="tertiary" />}>Cancel</AlertDialog.Close>              <AlertDialog.Close render={<Button />}>Send Reset Link</AlertDialog.Close>            </AlertDialog.Footer>          </AlertDialog.Popup>        </AlertDialog.Viewport>      </AlertDialog.Portal>    </AlertDialog>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

自定义触发器

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 alert-dialog-custom-trigger (Apache-2.0).import { TrashBin } from "@gravity-ui/icons";import * as stylex from "@stylexjs/stylex";import { AlertDialog, Button } from "@lenso/ui";
const styles = stylex.create({  trigger: {    display: "flex",    alignItems: "center",    gap: 12,    borderRadius: 16,    backgroundColor: { default: "var(--surface)", ":hover": "var(--surface-secondary)" },    padding: 16,    boxShadow: "var(--shadow-xs)",    userSelect: "none",  },  iconBox: {    display: "flex",    width: 48,    height: 48,    flexShrink: 0,    alignItems: "center",    justifyContent: "center",    borderRadius: 12,    backgroundColor: "var(--danger-soft)",    color: "var(--danger-soft-foreground)",  },  copy: { display: "flex", flex: 1, flexDirection: "column", gap: 2, textAlign: "start" },  heading: { fontSize: 14, fontWeight: 600 },  caption: { fontSize: 12, color: "var(--muted)" },  popup: { maxWidth: 400 },  largeGlyph: { width: 24, height: 24 },  glyph: { width: 20, height: 20 },});
export function CustomTrigger() {  return (    <AlertDialog>      <AlertDialog.Trigger xstyle={styles.trigger}>        <div {...stylex.props(styles.iconBox)}>          <TrashBin {...stylex.props(styles.largeGlyph)} />        </div>        <div {...stylex.props(styles.copy)}>          <p {...stylex.props(styles.heading)}>Delete Item</p>          <p {...stylex.props(styles.caption)}>Permanently remove this item</p>        </div>      </AlertDialog.Trigger>      <AlertDialog.Portal>        <AlertDialog.Backdrop />        <AlertDialog.Viewport>          <AlertDialog.Popup xstyle={styles.popup}>            <AlertDialog.Close aria-label="Close dialog" />            <AlertDialog.Header>              <AlertDialog.Icon variant="danger">                <TrashBin {...stylex.props(styles.glyph)} />              </AlertDialog.Icon>              <AlertDialog.Title>Delete this item?</AlertDialog.Title>            </AlertDialog.Header>            <AlertDialog.Body>              <AlertDialog.Description>                Use <code>AlertDialog.Trigger</code> to create custom trigger elements beyond                standard buttons. This example shows a card-style trigger with icons and descriptive                text.              </AlertDialog.Description>            </AlertDialog.Body>            <AlertDialog.Footer>              <AlertDialog.Close render={<Button variant="tertiary" />}>Cancel</AlertDialog.Close>              <AlertDialog.Close render={<Button variant="danger" />}>                Delete Item              </AlertDialog.Close>            </AlertDialog.Footer>          </AlertDialog.Popup>        </AlertDialog.Viewport>      </AlertDialog.Portal>    </AlertDialog>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

遮罩变体

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 alert-dialog-backdrop-variants (Apache-2.0).import * as stylex from "@stylexjs/stylex";import { AlertDialog, Button } from "@lenso/ui";
const styles = stylex.create({  row: { display: "flex", flexWrap: "wrap", gap: 16 },  popup: { maxWidth: 400 },});
export function BackdropVariants() {  const variants = ["opaque", "blur", "transparent"] as const;  return (    <div {...stylex.props(styles.row)}>      {variants.map((variant) => (        <AlertDialog key={variant}>          <AlertDialog.Trigger render={<Button variant="secondary" />}>            {variant.charAt(0).toUpperCase() + variant.slice(1)}          </AlertDialog.Trigger>          <AlertDialog.Portal>            <AlertDialog.Backdrop variant={variant} />            <AlertDialog.Viewport>              <AlertDialog.Popup xstyle={styles.popup}>                <AlertDialog.Close aria-label="Close dialog" />                <AlertDialog.Header>                  <AlertDialog.Icon variant="accent" />                  <AlertDialog.Title>                    Backdrop: {variant.charAt(0).toUpperCase() + variant.slice(1)}                  </AlertDialog.Title>                </AlertDialog.Header>                <AlertDialog.Body>                  <AlertDialog.Description>                    {variant === "opaque"                      ? "An opaque dark backdrop that completely obscures the background, providing maximum focus on the dialog."                      : variant === "blur"                        ? "A blurred backdrop that softly obscures the background while maintaining visual context."                        : "A transparent backdrop that keeps the background fully visible, useful for less critical confirmations."}                  </AlertDialog.Description>                </AlertDialog.Body>                <AlertDialog.Footer>                  <AlertDialog.Close render={<Button variant="tertiary" />}>                    Cancel                  </AlertDialog.Close>                  <AlertDialog.Close render={<Button />}>Confirm</AlertDialog.Close>                </AlertDialog.Footer>              </AlertDialog.Popup>            </AlertDialog.Viewport>          </AlertDialog.Portal>        </AlertDialog>      ))}    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

自定义遮罩

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 alert-dialog-custom-backdrop (Apache-2.0).import { TriangleExclamation } from "@gravity-ui/icons";import * as stylex from "@stylexjs/stylex";import { AlertDialog, Button } from "@lenso/ui";
const styles = stylex.create({  backdrop: {    backgroundColor: "transparent",    backgroundImage: {      default: "linear-gradient(to top, rgb(69 10 10 / 90%), rgb(69 10 10 / 50%), transparent)",      ':where([data-theme="dark"]) &':        "linear-gradient(to top, rgb(69 10 10 / 95%), rgb(69 10 10 / 60%), transparent)",    },  },  popup: { maxWidth: 420 },  header: { alignItems: "center", textAlign: "center" },  glyph: { width: 20, height: 20 },  footer: { flexDirection: "column-reverse" },  action: { width: "100%" },});
export function CustomBackdrop() {  return (    <AlertDialog>      <AlertDialog.Trigger render={<Button variant="danger" />}>Delete Account</AlertDialog.Trigger>      <AlertDialog.Portal>        <AlertDialog.Backdrop variant="blur" xstyle={styles.backdrop} />        <AlertDialog.Viewport>          <AlertDialog.Popup xstyle={styles.popup}>            <AlertDialog.Close aria-label="Close dialog" />            <AlertDialog.Header xstyle={styles.header}>              <AlertDialog.Icon variant="danger">                <TriangleExclamation {...stylex.props(styles.glyph)} />              </AlertDialog.Icon>              <AlertDialog.Title>Permanently delete your account?</AlertDialog.Title>            </AlertDialog.Header>            <AlertDialog.Body>              <AlertDialog.Description>                This action cannot be undone. All your data, settings, and content will be                permanently removed from our servers. The dramatic red backdrop emphasizes the                severity and irreversibility of this decision.              </AlertDialog.Description>            </AlertDialog.Body>            <AlertDialog.Footer xstyle={styles.footer}>              <AlertDialog.Close render={<Button xstyle={styles.action} />}>                Keep Account              </AlertDialog.Close>              <AlertDialog.Close render={<Button variant="danger" xstyle={styles.action} />}>                Delete Forever              </AlertDialog.Close>            </AlertDialog.Footer>          </AlertDialog.Popup>        </AlertDialog.Viewport>      </AlertDialog.Portal>    </AlertDialog>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

关闭行为

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 alert-dialog-dismiss-behavior (Apache-2.0).import { CircleInfo } from "@gravity-ui/icons";import * as stylex from "@stylexjs/stylex";import { AlertDialog, Button } from "@lenso/ui";
const styles = stylex.create({  column: { display: "flex", maxWidth: 384, flexDirection: "column", gap: 24 },  section: { display: "flex", flexDirection: "column", gap: 8 },  heading: { fontSize: 18, fontWeight: 600 },  caption: { fontSize: 14, lineHeight: "20px", color: "var(--muted)" },  popup: { maxWidth: 400 },  circle: { width: 20, height: 20 },});
export function DismissBehavior() {  return (    <div {...stylex.props(styles.column)}>      {([false, true] as const).map((blockEscape) => (        <div key={String(blockEscape)} {...stylex.props(styles.section)}>          <h3 {...stylex.props(styles.heading)}>            {blockEscape ? "Keyboard dismissal" : "Backdrop dismissal"}          </h3>          <p {...stylex.props(styles.caption)}>            {blockEscape              ? "ESC is disabled for this critical confirmation. Use the action buttons to dismiss it."              : "Alert dialogs require explicit action. Backdrop clicks do not dismiss the dialog."}          </p>          <AlertDialog            onOpenChange={(open, details) => {              if (!open && blockEscape && details.reason === "escape-key") details.cancel();            }}          >            <AlertDialog.Trigger render={<Button variant="secondary" />}>              Open Alert Dialog            </AlertDialog.Trigger>            <AlertDialog.Portal>              <AlertDialog.Backdrop />              <AlertDialog.Viewport>                <AlertDialog.Popup xstyle={styles.popup}>                  <AlertDialog.Close aria-label="Close dialog" />                  <AlertDialog.Header>                    <AlertDialog.Icon variant={blockEscape ? "accent" : "danger"}>                      <CircleInfo {...stylex.props(styles.circle)} />                    </AlertDialog.Icon>                    <AlertDialog.Title>                      {blockEscape ? "Keyboard dismissal disabled" : "Backdrop dismissal disabled"}                    </AlertDialog.Title>                    <AlertDialog.Description xstyle={styles.caption}>                      {blockEscape                        ? "ESC key is disabled"                        : "Clicking the backdrop won't close this alert dialog"}                    </AlertDialog.Description>                  </AlertDialog.Header>                  <AlertDialog.Body>                    <p>                      {blockEscape                        ? "Press ESC - nothing happens. You must use the action buttons to dismiss this alert dialog."                        : "Try clicking outside this alert dialog on the overlay - it won't close. You must use the action buttons to dismiss it."}                    </p>                  </AlertDialog.Body>                  <AlertDialog.Footer>                    <AlertDialog.Close render={<Button variant="tertiary" />}>                      Cancel                    </AlertDialog.Close>                    <AlertDialog.Close render={<Button />}>Confirm</AlertDialog.Close>                  </AlertDialog.Footer>                </AlertDialog.Popup>              </AlertDialog.Viewport>            </AlertDialog.Portal>          </AlertDialog>        </div>      ))}    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

关闭方式

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 alert-dialog-close-methods (Apache-2.0).import { useRef } from "react";import { AlertDialog as BaseAlertDialog } from "@base-ui/react/alert-dialog";import * as stylex from "@stylexjs/stylex";import { AlertDialog, Button } from "@lenso/ui";
const styles = stylex.create({  column: { display: "flex", maxWidth: 672, flexDirection: "column", gap: 32 },  section: { display: "flex", flexDirection: "column", gap: 8 },  heading: { fontSize: 18, fontWeight: 600 },  caption: { fontSize: 14, color: "var(--muted)" },  popup: { maxWidth: 400 },});
export function CloseMethods() {  const actionsRef = useRef<BaseAlertDialog.Root.Actions | null>(null);  return (    <div {...stylex.props(styles.column)}>      <div {...stylex.props(styles.section)}>        <h3 {...stylex.props(styles.heading)}>Using AlertDialog.Close</h3>        <p {...stylex.props(styles.caption)}>          Compose a Button with <code>AlertDialog.Close</code> to close the dialog automatically.        </p>        <AlertDialog>          <AlertDialog.Trigger render={<Button variant="secondary" />}>            Open Dialog          </AlertDialog.Trigger>          <AlertDialog.Portal>            <AlertDialog.Backdrop />            <AlertDialog.Viewport>              <AlertDialog.Popup xstyle={styles.popup}>                <AlertDialog.Header>                  <AlertDialog.Icon variant="accent" />                  <AlertDialog.Title>Using AlertDialog.Close</AlertDialog.Title>                </AlertDialog.Header>                <AlertDialog.Body>                  <AlertDialog.Description>                    Click either button below - both use <code>AlertDialog.Close</code> and will                    close the dialog automatically.                  </AlertDialog.Description>                </AlertDialog.Body>                <AlertDialog.Footer>                  <AlertDialog.Close render={<Button variant="tertiary" />}>                    Cancel                  </AlertDialog.Close>                  <AlertDialog.Close render={<Button />}>Confirm</AlertDialog.Close>                </AlertDialog.Footer>              </AlertDialog.Popup>            </AlertDialog.Viewport>          </AlertDialog.Portal>        </AlertDialog>      </div>      <div {...stylex.props(styles.section)}>        <h3 {...stylex.props(styles.heading)}>Using Root actions</h3>        <p {...stylex.props(styles.caption)}>          Access the native <code>close</code> method through the Root&apos;s{" "}          <code>actionsRef</code>. This gives you full control over when and how to close the          dialog, allowing you to add custom logic before closing.        </p>        <AlertDialog actionsRef={actionsRef}>          <AlertDialog.Trigger render={<Button variant="secondary" />}>            Open Dialog          </AlertDialog.Trigger>          <AlertDialog.Portal>            <AlertDialog.Backdrop />            <AlertDialog.Viewport>              <AlertDialog.Popup xstyle={styles.popup}>                <AlertDialog.Header>                  <AlertDialog.Icon variant="success" />                  <AlertDialog.Title>Using Root actions</AlertDialog.Title>                </AlertDialog.Header>                <AlertDialog.Body>                  <AlertDialog.Description>                    The buttons below use the <code>close</code> method from Root actions. You can                    add validation or other logic before calling{" "}                    <code>actionsRef.current.close()</code>.                  </AlertDialog.Description>                </AlertDialog.Body>                <AlertDialog.Footer>                  <Button variant="tertiary" onClick={() => actionsRef.current?.close()}>                    Cancel                  </Button>                  <Button onClick={() => actionsRef.current?.close()}>Confirm</Button>                </AlertDialog.Footer>              </AlertDialog.Popup>            </AlertDialog.Viewport>          </AlertDialog.Portal>        </AlertDialog>      </div>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

自定义动画

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 alert-dialog-custom-animations (Apache-2.0).import { ArrowUpFromLine, Sparkles } from "@gravity-ui/icons";import * as stylex from "@stylexjs/stylex";import { AlertDialog, Button } from "@lenso/ui";
const scaleIn = stylex.keyframes({  from: { opacity: 0, transform: "scale(.95)" },  to: { opacity: 1, transform: "scale(1)" },});const scaleOut = stylex.keyframes({  from: { opacity: 1, transform: "scale(1)" },  to: { opacity: 0, transform: "scale(.95)" },});const slideIn = stylex.keyframes({  from: { opacity: 0, transform: "translateY(16px)" },  to: { opacity: 1, transform: "translateY(0)" },});const slideOut = stylex.keyframes({  from: { opacity: 1, transform: "translateY(0)" },  to: { opacity: 0, transform: "translateY(8px)" },});const styles = stylex.create({  row: { display: "flex", flexWrap: "wrap", gap: 16 },  popup: { maxWidth: 400 },  glyph: { width: 20, height: 20 },  description: { marginTop: 4 },  scaleBackdrop: {    transitionDuration: {      default: "400ms",      ":is([data-ending-style])": "200ms",      "@media (prefers-reduced-motion: reduce)": "0ms",    },    transitionTimingFunction: {      default: "cubic-bezier(.16,1,.3,1)",      ":is([data-ending-style])": "cubic-bezier(.7,0,.84,0)",    },  },  slideBackdrop: {    transitionDuration: {      default: "500ms",      ":is([data-ending-style])": "200ms",      "@media (prefers-reduced-motion: reduce)": "0ms",    },    transitionTimingFunction: {      default: "cubic-bezier(.25,1,.5,1)",      ":is([data-ending-style])": "cubic-bezier(.5,0,.75,0)",    },  },  scale: {    animationName: {      default: scaleIn,      ":is([data-ending-style])": scaleOut,      "@media (prefers-reduced-motion: reduce)": "none",    },    animationDuration: { default: "400ms", ":is([data-ending-style])": "200ms" },    animationTimingFunction: {      default: "cubic-bezier(.16,1,.3,1)",      ":is([data-ending-style])": "cubic-bezier(.7,0,.84,0)",    },    transitionProperty: "none",  },  slide: {    animationName: {      default: slideIn,      ":is([data-ending-style])": slideOut,      "@media (prefers-reduced-motion: reduce)": "none",    },    animationDuration: { default: "500ms", ":is([data-ending-style])": "200ms" },    animationTimingFunction: {      default: "cubic-bezier(.25,1,.5,1)",      ":is([data-ending-style])": "cubic-bezier(.5,0,.75,0)",    },    transitionProperty: "none",  },});
export function CustomAnimations() {  const animations = [    {      name: "Kinematic Scale",      Icon: Sparkles,      popup: styles.scale,      backdrop: styles.scaleBackdrop,      description:        "Physics-based elastic scaling. Simulates a high-damping spring system with fast transient response and prolonged settling time. Ideal for Alert Dialogs and Modals.",    },    {      name: "Fluid Slide",      Icon: ArrowUpFromLine,      popup: styles.slide,      backdrop: styles.slideBackdrop,      description:        "Simulates movement through a medium with fluid resistance. Eliminates mechanical linearity for a natural, grounded feel. Perfect for Bottom Sheets or Toasts.",    },  ];  return (    <div {...stylex.props(styles.row)}>      {animations.map(({ name, Icon, popup, backdrop, description }) => (        <AlertDialog key={name}>          <AlertDialog.Trigger render={<Button variant="secondary" />}>{name}</AlertDialog.Trigger>          <AlertDialog.Portal>            <AlertDialog.Backdrop xstyle={backdrop} />            <AlertDialog.Viewport>              <AlertDialog.Popup xstyle={[styles.popup, popup]}>                <AlertDialog.Close aria-label="Close dialog" />                <AlertDialog.Header>                  <AlertDialog.Icon variant="accent">                    <Icon {...stylex.props(styles.glyph)} />                  </AlertDialog.Icon>                  <AlertDialog.Title>{name} Animation</AlertDialog.Title>                </AlertDialog.Header>                <AlertDialog.Body>                  <AlertDialog.Description xstyle={styles.description}>                    {description}                  </AlertDialog.Description>                </AlertDialog.Body>                <AlertDialog.Footer>                  <AlertDialog.Close render={<Button variant="tertiary" />}>                    Close                  </AlertDialog.Close>                  <AlertDialog.Close render={<Button />}>Try Again</AlertDialog.Close>                </AlertDialog.Footer>              </AlertDialog.Popup>            </AlertDialog.Viewport>          </AlertDialog.Portal>        </AlertDialog>      ))}    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

自定义 Portal

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 alert-dialog-custom-portal (Apache-2.0).import { useCallback, useState } from "react";import * as stylex from "@stylexjs/stylex";import { AlertDialog, Button } from "@lenso/ui";
const styles = stylex.create({  column: { display: "flex", flexDirection: "column", gap: 16 },  text: { fontSize: 14 },  caption: { fontSize: 14, color: "var(--muted)" },  code: { borderRadius: 4, paddingInline: 4, paddingBlock: 2, fontSize: 12 },  container: {    position: "relative",    display: "flex",    height: 380,    alignItems: "center",    justifyContent: "center",    overflow: "hidden",    borderRadius: "var(--radius)",    backgroundColor: "color-mix(in oklab, var(--muted) 20%, transparent)",    transform: "translateZ(0)",  },  backdrop: { height: "100%" },  viewport: { height: "100%", maxHeight: "100%" },  popup: { height: "100%", maxHeight: "100%", maxWidth: 448 },});
export function CustomPortal() {  const [portalContainer, setPortalContainer] = useState<HTMLDivElement | null>(null);  const setPortalRef = useCallback((node: HTMLDivElement | null) => setPortalContainer(node), []);  return (    <div {...stylex.props(styles.column)}>      <div>        <p {...stylex.props(styles.text)}>          Render alert dialogs inside a custom container instead of <code>document.body</code>        </p>        <p {...stylex.props(styles.caption)}>          Apply <code {...stylex.props(styles.code)}>transform: translateZ(0)</code> to the          container to create a new stacking context.        </p>      </div>      <div ref={setPortalRef} {...stylex.props(styles.container)}>        {!!portalContainer && (          <AlertDialog>            <AlertDialog.Trigger render={<Button />}>Open Alert Dialog</AlertDialog.Trigger>            <AlertDialog.Portal container={portalContainer}>              <AlertDialog.Backdrop xstyle={styles.backdrop} />              <AlertDialog.Viewport xstyle={styles.viewport}>                <AlertDialog.Popup xstyle={styles.popup}>                  <AlertDialog.Close aria-label="Close dialog" />                  <AlertDialog.Header>                    <AlertDialog.Icon variant="accent" />                    <AlertDialog.Title>Custom Portal</AlertDialog.Title>                  </AlertDialog.Header>                  <AlertDialog.Body>                    {Array.from({ length: 3 }, (_, index) => (                      <p key={index} {...stylex.props(styles.caption)}>                        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod                        tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,                        quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo                        consequat.                      </p>                    ))}                  </AlertDialog.Body>                  <AlertDialog.Footer>                    <AlertDialog.Close render={<Button variant="tertiary" />}>                      Cancel                    </AlertDialog.Close>                    <AlertDialog.Close render={<Button />}>Confirm</AlertDialog.Close>                  </AlertDialog.Footer>                </AlertDialog.Popup>              </AlertDialog.Viewport>            </AlertDialog.Portal>          </AlertDialog>        )}      </div>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

自定义样式

Tailwind CSS

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 alert-dialog-custom-styles (Apache-2.0).import * as stylex from "@stylexjs/stylex";import { AlertDialog, Button } from "@lenso/ui";
const styles = stylex.create({  backdrop: {    backgroundColor: {      default: "color-mix(in oklab, var(--overlay) 50%, transparent)",      ':where([data-theme="dark"]) &': "color-mix(in oklab, var(--overlay) 60%, transparent)",    },  },  popup: {    position: "relative",    overflow: "hidden",    maxWidth: 400,    borderWidth: 1,    borderStyle: "solid",    borderColor: {      default: "color-mix(in oklab, var(--border) 80%, transparent)",      ':where([data-theme="dark"]) &': "color-mix(in oklab, var(--border) 90%, transparent)",    },    backgroundColor: "var(--surface)",    boxShadow: {      default:        "0 25px 50px -12px rgb(0 0 0 / 25%), 0 0 0 1px color-mix(in oklab, var(--accent) 10%, transparent)",      ':where([data-theme="dark"]) &':        "0 25px 50px -12px rgb(0 0 0 / 25%), 0 0 0 1px color-mix(in oklab, var(--accent) 15%, transparent)",    },  },  glow: {    pointerEvents: "none",    position: "absolute",    insetInline: 0,    top: 0,    height: 96,    backgroundImage: {      default:        "linear-gradient(to bottom, color-mix(in oklab, var(--accent) 6%, transparent), transparent)",      ':where([data-theme="dark"]) &':        "linear-gradient(to bottom, color-mix(in oklab, var(--accent) 10%, transparent), transparent)",    },  },  line: {    pointerEvents: "none",    position: "absolute",    insetInline: 32,    top: 0,    height: 1,    backgroundImage: {      default:        "linear-gradient(to right, transparent, color-mix(in oklab, var(--accent) 35%, transparent), transparent)",      ':where([data-theme="dark"]) &':        "linear-gradient(to right, transparent, color-mix(in oklab, var(--accent) 45%, transparent), transparent)",    },  },  relative: { position: "relative" },  caption: { color: "var(--muted)" },  foreground: { color: "var(--foreground)" },});
export function CustomStyles() {  return (    <AlertDialog>      <AlertDialog.Trigger render={<Button variant="secondary" />}>Sign out</AlertDialog.Trigger>      <AlertDialog.Portal>        <AlertDialog.Backdrop variant="blur" xstyle={styles.backdrop} />        <AlertDialog.Viewport>          <AlertDialog.Popup xstyle={styles.popup}>            <div aria-hidden="true" {...stylex.props(styles.glow)} />            <div aria-hidden="true" {...stylex.props(styles.line)} />            <AlertDialog.Header xstyle={styles.relative}>              <AlertDialog.Icon variant="accent" />              <AlertDialog.Title>Sign out of your account?</AlertDialog.Title>            </AlertDialog.Header>            <AlertDialog.Body xstyle={styles.relative}>              <AlertDialog.Description xstyle={styles.caption}>                You will be signed out on this device. Unsaved work in{" "}                <strong {...stylex.props(styles.foreground)}>Acme Workspace</strong> may be lost                unless it was saved to the cloud.              </AlertDialog.Description>            </AlertDialog.Body>            <AlertDialog.Footer>              <AlertDialog.Close render={<Button variant="tertiary" />}>                Stay signed in              </AlertDialog.Close>              <AlertDialog.Close render={<Button />}>Sign out</AlertDialog.Close>            </AlertDialog.Footer>          </AlertDialog.Popup>        </AlertDialog.Viewport>      </AlertDialog.Portal>    </AlertDialog>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

全局 CSS

若要自定义 AlertDialog 组件类,可使用 @layer components 指令。

了解更多。

@layer components {  .alert-dialog__backdrop {    @apply bg-gradient-to-br from-black/60 to-black/80;  }
  .alert-dialog__dialog {    @apply rounded-2xl border border-red-500/20 shadow-2xl;  }
  .alert-dialog__header {    @apply gap-4;  }
  .alert-dialog__icon {    @apply size-16;  }
  .alert-dialog__close-trigger {    @apply rounded-full bg-white/10 hover:bg-white/20;  }}

样式参考

HeroUI 遵循 BEM 方法论,确保组件变体与状态可复用且易于自定义。

CSS 类

AlertDialog 组件使用以下 CSS 类(查看源码样式):

基础类 [!toc]

  • .alert-dialog__trigger - 打开警告对话框的触发元素
  • .alert-dialog__backdrop - 对话框背后的遮罩层
  • .alert-dialog__container - 支持 placement 的定位包裹层
  • .alert-dialog__dialog - 对话框内容容器
  • .alert-dialog__header - 图标与标题的头部区域
  • .alert-dialog__heading - 标题文本样式
  • .alert-dialog__body - 主内容区域
  • .alert-dialog__footer - 操作按钮的底部区域
  • .alert-dialog__icon - 带状态色的图标容器
  • .alert-dialog__close-trigger - 关闭按钮元素

遮罩变体 [!toc]

  • .alert-dialog__backdrop--opaque - 不透明彩色遮罩(默认)
  • .alert-dialog__backdrop--blur - 带玻璃效果的模糊遮罩
  • .alert-dialog__backdrop--transparent - 透明遮罩(无覆盖层)

状态变体(图标)[!toc]

  • .alert-dialog__icon--default - 默认灰色状态
  • .alert-dialog__icon--accent - 强调蓝色状态
  • .alert-dialog__icon--success - 成功绿色状态
  • .alert-dialog__icon--warning - 警告橙色状态
  • .alert-dialog__icon--danger - 危险红色状态

交互状态

该组件支持以下交互状态:

  • Focus::focus-visible 或 [data-focus-visible="true"] — 应用于 trigger、dialog 和 close 按钮
  • Hover::hover 或 [data-hovered="true"] — 应用于 close 按钮悬停时
  • Active::active 或 [data-pressed="true"] — 应用于 close 按钮按下时
  • Entering:[data-entering] — 对话框打开动画期间
  • Exiting:[data-exiting] — 对话框关闭动画期间
  • Placement:[data-placement="*"] — 根据对话框位置应用(auto、top、center、bottom)

API 参考

AlertDialog

Prop类型默认值描述
childrenReactNode-触发器与容器元素

AlertDialog.Trigger

Prop类型默认值描述
childrenReactNode-自定义触发器内容
classNamestring-CSS 类

AlertDialog.Backdrop

Prop类型默认值描述
variant"opaque" | "blur" | "transparent""opaque"遮罩覆盖层样式
isDismissablebooleanfalse点击遮罩是否关闭
isKeyboardDismissDisabledbooleantrue是否禁用 ESC 键关闭
isOpenboolean-受控的打开状态
onOpenChange(isOpen: boolean) => void-打开状态变化处理函数
classNamestring | (values) => string-遮罩 CSS 类
UNSTABLE_portalContainerHTMLElement-自定义 portal 容器

AlertDialog.Container

Prop类型默认值描述
placement"auto" | "center" | "top" | "bottom""auto"对话框在屏幕上的位置
size"xs" | "sm" | "md" | "lg" | "cover""md"Alert Dialog 尺寸变体
classNamestring | (values) => string-容器 CSS 类

AlertDialog.Dialog

Prop类型默认值描述
childrenReactNode | ({close}) => ReactNode-内容或渲染函数
classNamestring-CSS 类
rolestring"alertdialog"ARIA role
aria-labelstring-无障碍标签
aria-labelledbystring-标签元素的 ID
aria-describedbystring-描述元素的 ID

AlertDialog.Header

Prop类型默认值描述
childrenReactNode-头部内容(通常为 Icon 和 Heading)
classNamestring-CSS 类

AlertDialog.Heading

Prop类型默认值描述
childrenReactNode-标题文本
classNamestring-CSS 类

AlertDialog.Body

Prop类型默认值描述
childrenReactNode-正文内容
classNamestring-CSS 类

AlertDialog.Footer

Prop类型默认值描述
childrenReactNode-底部内容(通常为操作按钮)
classNamestring-CSS 类

AlertDialog.Icon

Prop类型默认值描述
childrenReactNode-自定义图标元素
status"default" | "accent" | "success" | "warning" | "danger""danger"状态颜色变体
classNamestring-CSS 类

AlertDialog.CloseTrigger

Prop类型默认值描述
childrenReactNode-自定义关闭按钮
classNamestring | (values) => string-CSS 类

useOverlayState Hook

import {useOverlayState} from "@lenso/ui";
const state = useOverlayState({  defaultOpen: false,  onOpenChange: (isOpen) => console.log(isOpen),});
state.isOpen; // Current statestate.open(); // Open dialogstate.close(); // Close dialogstate.toggle(); // Toggle statestate.setOpen(); // Set state directly

无障碍

实现 WAI-ARIA AlertDialog 模式:

  • 焦点陷阱:焦点锁定在警告对话框内
  • 键盘:ESC 关闭(启用时)、Tab 循环切换元素
  • 屏幕阅读器:使用 role="alertdialog" 的正确 ARIA 属性
  • 滚动锁定:打开时禁用 body 滚动
  • 必需操作:默认需要用户明确操作(不可通过遮罩/ESC 关闭)

相关组件