Modal

作者 lobehub6a3eba96b010无许可证83K 个星标收录于 2026年10月8日更新于 2026年10月8日仓库今天更新

Use for modals, dialogs and confirmations with createModal, confirmModal, ModalHost or base-ui modal APIs.

AI 生成的概览

指导开发者使用 @lobehub/ui base-ui 的命令式模态框 API 实现对话框与确认框。

功能
该技能是一份参考指南,说明如何使用 @lobehub/ui 的 base-ui 模态框方案构建模态框、对话框和确认框。内容涵盖 createModal、confirmModal、ModalHost 与 useModalContext,包括必需的宿主挂载、文件结构、国际化处理、常用选项以及关闭回调行为。同时指出根包中旧版 createModal 已弃用,不应重新引入。
适用场景
适用于在使用 @lobehub/ui 的项目中实现或迁移命令式模态框、对话框或确认提示时。也适合排查模态框不显示或关闭后调用方清理逻辑未执行的问题。
运行要求
需要 React 项目并安装 @lobehub/ui 及其 base-ui 入口,示例中的翻译写法还需要 i18next/react-i18next。不包含脚本,仅为说明文档。

Modal Imperative API Guide

Recommended: @lobehub/ui/base-ui

New code should use the base-ui modal stack (headless primitives, not antd Modal):

  • createModal, confirmModal, ModalHost from @lobehub/ui/base-ui
  • useModalContext from @lobehub/ui/base-ui inside modal content

Body slot: pass content (or children; runtime uses content ?? children).

Global ModalHost (required)

Base-ui createModal renders through a separate host from the root package. The app must mount ModalHost from @lobehub/ui/base-ui once near the root (e.g. next to other global hosts). Without it, createModal calls will not appear.

If the project only mounts ModalHost from @lobehub/ui, add a second lazy ModalHost from @lobehub/ui/base-ui until all imperative modals are migrated.

Why imperative?

ModeCharacteristicsRecommended
Declarativeopen state + <Modal />❌
ImperativeCall createModal(), no local state✅

File structure

features/└── MyFeatureModal/    ├── index.tsx            # export createXxxModal    └── MyFeatureContent.tsx # modal body

1. Content (MyFeatureContent.tsx)

tsx
'use client';
import { useModalContext } from '@lobehub/ui/base-ui';import { useTranslation } from 'react-i18next';
export const MyFeatureContent = () => {  const { t } = useTranslation('namespace');  const { close } = useModalContext();
  return <div>{/* ... */}</div>;};

2. createModal (index.tsx)

tsx
'use client';
import { createModal } from '@lobehub/ui/base-ui';import { t } from 'i18next';
import { MyFeatureContent } from './MyFeatureContent';
export const createMyFeatureModal = () =>  createModal({    content: <MyFeatureContent />,    footer: null,    maskClosable: true,    styles: {      content: { overflow: 'hidden', padding: 0 },    },    title: t('myFeature.title', { ns: 'setting' }),    width: 'min(80%, 800px)',  });

3. Usage

tsx
import { createMyFeatureModal } from '@/features/MyFeatureModal';
const handleOpen = useCallback(() => {  createMyFeatureModal();}, []);
return <Button onClick={handleOpen}>Open</Button>;

i18n

  • Content: useTranslation in components.
  • createModal options: import { t } from 'i18next' where hooks are unavailable.

useModalContext

tsx
const { close, setCanDismissByClickOutside } = useModalContext();

Closing: which callback actually fires

close() — from useModalContext() inside the content, or from the returned ModalInstance — only flips the stack entry to open: false. It does not go through base-ui's dismissal path, so:

callbackuser dismissal (Esc / backdrop / header ✕)close() from content or instance
onOpenChangefiresdoes not fire
onOpenChangeCompletefires with falsefires with false

Put caller-side cleanup (clearing an editing flag, resetting the provider's open state) on onOpenChangeComplete. Wiring it to onOpenChange looks correct until a footer button closes the modal, and then the caller never learns it went away — typically leaving a flag set so the modal cannot be reopened.

createModal only ever completes with false (the imperative renderer supplies the argument itself and never forwards the prop to base-ui), but still guard on it — other base-ui primitives such as DropdownMenu do report both directions, and the guard keeps the call site from depending on that difference:

tsx
onOpenChangeComplete: (open) => {  if (!open) onClosed?.();},

Common options (base-ui)

ImperativeModalProps builds on BaseModalProps: title, width, maskClosable, open, onOpenChange, footer, styles / classNames (keys: backdrop, popup, header, title, close, content, …).

PropertyNotes
contentMain body (preferred name vs children)
maskClosableClick outside to dismiss
styles.*Semantic regions, not antd styles.body

Confirm

tsx
import { confirmModal } from '@lobehub/ui/base-ui';
confirmModal({  title: '…',  content: '…',  okText: '…',  cancelText: '…',  onOk: async () => {},});

Legacy: @lobehub/ui (root)

createModal from the root @lobehub/ui entry is typed as antd Modal props (children, allowFullscreen, getContainer, destroyOnHidden, styles.body, etc.). App code no longer imports it; do not reintroduce it — use @lobehub/ui/base-ui.


Examples

  • Base-ui (preferred): follow sections above; ensure base-ui ModalHost is mounted.
  • Base-ui call sites: src/features/SkillStore/index.tsx, src/features/LibraryModal/CreateNew/index.tsx

来源与署名

来源:lobehub/lobehub位于.agents/skills/modal提交6a3eba9

许可证: 无许可证

内容归原作者所有。SourceWeft 从公开仓库中收录这些内容。

举报或申请下架