> Leaf UI 0.6.0 · Package: @ifhover/leaf-ui · Source: b30c3d3a8fa9c5e15d0d0522cebfbd33f60c6b06
> Browser documentation: https://ifhover.github.io/leaf-ui/v/0.6.0/components/modal.html

> For AI agents: the complete documentation index is available at https://ifhover.github.io/leaf-ui/v/0.6.0/llms.txt, the full documentation bundle is available at https://ifhover.github.io/leaf-ui/v/0.6.0/llms-full.txt.

# Modal 弹窗

在弹窗中展示内容或完成操作。

## 编辑内容


编辑项目

basic.tsx

复制代码

```tsx lineNumbers file="../../src/examples/modal/basic.tsx"
import { Button, Form, FormField, Input, Modal, Select } from '@ifhover/leaf-ui';

import { useRef, useState } from 'react';

export function ModalBasic() {
  const [open, setOpen] = useState(false);
  const formRef = useRef<HTMLFormElement>(null);
  return (
    <>
      <Button onClick={() => setOpen(true)}>编辑项目</Button>
      <Modal
        open={open}
        title="项目设置"
        confirmText="保存"
        onClose={() => setOpen(false)}
        onConfirm={() => formRef.current?.requestSubmit()}
      >
        <Form
          ref={formRef}
          layout="vertical"
          onSubmit={(event) => {
            event.preventDefault();
            setOpen(false);
          }}
        >
          <FormField label="项目名称" required>
            <Input name="name" defaultValue="Leaf UI" />
          </FormField>
          <FormField label="负责人">
            <Select
              name="owner"
              options={[
                { value: 'design', label: '设计团队' },
                { value: 'engineering', label: '工程团队' },
              ]}
            />
          </FormField>
        </Form>
      </Modal>
    </>
  );
}
```

展开代码

## 自定义底部


自定义底部

footer.tsx

复制代码

```tsx lineNumbers file="../../src/examples/modal/footer.tsx"
import { Button, Modal } from '@ifhover/leaf-ui';

import { useState } from 'react';

export function ModalFooter() {
  const [open, setOpen] = useState(false);
  const [extra, setExtra] = useState(false);
  return (
    <>
      <Button variant="outline" onClick={() => setOpen(true)}>
        自定义底部
      </Button>
      <Modal
        open={open}
        title="扩展操作"
        onClose={() => setOpen(false)}
        footer={({ cancelButton, confirmButton }) => (
          <>
            <Button
              variant="ghost"
              style={{ marginRight: 'auto' }}
              onClick={() => setExtra(!extra)}
            >
              查看详情
            </Button>
            {cancelButton}
            {confirmButton}
          </>
        )}
      >
        <p>通过底部函数增加操作，保留默认取消与确定按钮。</p>
        {extra && <p>这里展示额外的信息。</p>}
      </Modal>
    </>
  );
}
```

展开代码

## 状态销毁与初始焦点


打开编辑器

capabilities.tsx

复制代码

```tsx lineNumbers file="../../src/examples/modal/capabilities.tsx"
import { Button, Input, Modal } from '@ifhover/leaf-ui';

import { useRef, useState } from 'react';

export function Capabilities() {
  const [open, setOpen] = useState(false);
  const focus = useRef<HTMLInputElement>(null);
  return (
    <>
      <Button onClick={() => setOpen(true)}>{'打开编辑器'}</Button>
      <Modal
        open={open}
        onClose={() => setOpen(false)}
        title={'临时草稿'}
        destroyOnClose
        initialFocus={focus}
        footer={({ cancelButton, confirmButton }) => (
          <>
            {cancelButton}
            {confirmButton}
          </>
        )}
      >
        <Input
          ref={focus}
          aria-label={'草稿标题'}
          placeholder={'关闭后不保留草稿'}
        />
      </Modal>
    </>
  );
}
```

展开代码

## 图标与说明

使用 `icon` 和 `description` 为操作补充上下文。


带图标与说明

description.tsx

复制代码

```tsx lineNumbers file="../../src/examples/modal/description.tsx"
import { Button, Modal } from '@ifhover/leaf-ui';

import { Save } from 'lucide-react';

import { useState } from 'react';

export function ModalDescription() {
  const [open, setOpen] = useState(false);
  return (
    <>
      <Button variant="outline" onClick={() => setOpen(true)}>
        带图标与说明
      </Button>
      <Modal
        open={open}
        title="保存设置"
        description="保存后，新设置将立即生效。"
        icon={<Save />}
        confirmText="保存"
        onClose={() => setOpen(false)}
        onConfirm={() => setOpen(false)}
      >
        确认保存当前设置？
      </Modal>
    </>
  );
}
```

展开代码

正文使用 Scrollbar，内容较长时内部滚动，标题和操作按钮始终可见。没有副标题时头部更紧凑，底部使用较小的纵向留白。

## Modal API

| 属性                   | 类型                                                                                         | 默认值     | 说明                                |
| -------------------- | ------------------------------------------------------------------------------------------ | ------- | --------------------------------- |
| `initialFocus`       | `RefObject<HTMLElement \| null> \| (() => HTMLElement \| null)`                            | —       | 指定打开后的初始焦点。                       |
| `returnFocus`        | `boolean \| RefObject<HTMLElement \| null> \| (() => HTMLElement \| null)`                 | —       | 关闭后返回原焦点；也可指定目标或设为 false。         |
| `open`               | `boolean`                                                                                  | 必填      | 必需。是否显示弹窗                         |
| `title`              | `ReactNode`                                                                                | —       | 标题 / 内容                           |
| `description`        | `ReactNode`                                                                                | —       | 补充说明。                             |
| `icon`               | `ReactNode`                                                                                | —       | 标题左侧的装饰图标。                        |
| `divided`            | `boolean`                                                                                  | `true`  | 用浅分隔线划分标题、内容和底部操作区。               |
| `footer`             | `ReactNode \| ((actions: `[`ModalFooterActions`](#modalfooteractions)`) => ReactNode)`     | 取消 + 确定 | 自定义底部；null 隐藏；函数接收默认按钮            |
| `confirmText`        | `ReactNode`                                                                                | 语言默认值   | 按钮文字                              |
| `cancelText`         | `ReactNode`                                                                                | 语言默认值   | 按钮文字                              |
| `confirmLoading`     | `boolean`                                                                                  | `false` | 确认加载状态                            |
| `confirmButtonProps` | [`ButtonProps`](https://ifhover.github.io/leaf-ui/v/0.6.0/components/button.md#button-api) | —       | 定制默认按钮属性                          |
| `cancelButtonProps`  | [`ButtonProps`](https://ifhover.github.io/leaf-ui/v/0.6.0/components/button.md#button-api) | —       | 定制默认按钮属性                          |
| `onConfirm`          | `() => void \| Promise<void>`                                                              | —       | 由应用控制 open；Promise 拒绝时显示错误        |
| `onCancel`           | `() => void`                                                                               | —       | 取消或关闭请求，未指定时使用 onClose            |
| `width`              | `number \| string`                                                                         | `480`   | 弹窗宽度，数字为像素                        |
| `closable`           | `boolean`                                                                                  | `true`  | 关闭按钮 / 点击遮罩关闭 / Escape 关闭         |
| `maskClosable`       | `boolean`                                                                                  | `true`  | 关闭按钮 / 点击遮罩关闭 / Escape 关闭         |
| `keyboard`           | `boolean`                                                                                  | `true`  | 关闭按钮 / 点击遮罩关闭 / Escape 关闭         |
| `onClose`            | `() => void`                                                                               | —       | 请求关闭，由应用更新 open                   |
| `afterClose`         | `() => void`                                                                               | —       | 关闭动画完成后触发                         |
| `forceRender`        | `boolean`                                                                                  | `false` | 打开前也挂载内容，关闭时保持隐藏。                 |
| `destroyOnClose`     | `boolean`                                                                                  | `false` | 关闭过渡完成后卸载内容；默认保留。                 |
| `preserve`           | `boolean`                                                                                  | —       | 显式控制关闭后是否保留内容，优先于 destroyOnClose。 |
| `container`          | `Element \| DocumentFragment \| (() => Element \| DocumentFragment)`                       | —       | 自定义挂载或滚动容器。                       |

## ModalFooterActions

| 属性              | 类型          | 默认值 | 说明               |
| --------------- | ----------- | --- | ---------------- |
| `confirmButton` | `ReactNode` | —   | 必需。默认确认按钮，保留加载状态 |
| `cancelButton`  | `ReactNode` | —   | 必需。默认取消按钮        |

## 类型

定义组件属性或组装变量时可导入相关类型。

```ts
import type { ModalProps, ModalFooterActions } from '@ifhover/leaf-ui';
```

## 使用说明

弹窗打开后聚焦内部控件，Tab 在弹窗中移动；Escape 关闭后恢复触发前的焦点。maskClosable、keyboard 可分别关闭遮罩、键盘退出。没有 title 时请提供 aria-label。

footer 可放确认、取消等操作；也可以在内容中使用 Form。弹窗中的选择器浮层沿用区域主题。

默认底部按取消、确定顺序靠右排列。传入 onConfirm 时，完成操作后自行更新 open；未传入时确定调用 onClose。Promise 执行期间显示加载状态并避免重复操作。提交表单可在 onConfirm 中调用 form.requestSubmit()，或通过 confirmButtonProps 设置 type="submit" 与 form ID，保留表单校验。footer 函数可扩展默认按钮，`footer={null}` 隐藏底部。

## DialogFocusOptions

| 属性             | 类型                                                                         | 默认值 | 说明                        |
| -------------- | -------------------------------------------------------------------------- | --- | ------------------------- |
| `initialFocus` | `RefObject<HTMLElement \| null> \| (() => HTMLElement \| null)`            | —   | 指定打开后的初始焦点。               |
| `returnFocus`  | `boolean \| RefObject<HTMLElement \| null> \| (() => HTMLElement \| null)` | —   | 关闭后返回原焦点；也可指定目标或设为 false。 |

## 外观与丰富内容

`description` 为标题提供补充说明，`icon` 放在标题左侧。默认 `divided` 使用浅分隔线和淡灰操作区；`divided={false}` 使用留白分区。`footer` 函数可在左侧加入状态文字，使用 `Space` 把按钮放在同一组，窄屏时整组换行并靠右排列。
