Modal

lobehub/lobehub/.agents/skills/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 從公開儲存庫中收錄這些內容。

檢舉或申請下架