npx skills add https://github.com/wix/skills --skill wix-cli-dashboard-modal仪表板模态框是从仪表板页面或插件触发的弹出对话框。它们由三个文件组成,并使用 Dashboard SDK 通过 openModal() 和 closeModal() 进行生命周期控制。
| 任务 | 方法 | 示例 |
|---|---|---|
| 创建模态框 | 在 src/extensions/dashboard/modals/<folder>/ 中创建 3 个文件 | 参见下面的文件结构 |
| 打开模态框 | dashboard.openModal() | openModal({ modalId: "modal-id" }) |
| 向模态框传递数据 | 中的 |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
openModal()paramsparams: { userId: "123" } |
| 在模态框中读取数据 | observeState() | dashboard.observeState((state) => { ... }) |
| 关闭模态框 | dashboard.closeModal() | closeModal() |
| 向父级返回数据 | 将数据传递给 closeModal() | closeModal({ ... }) |
| 等待模态框关闭 | modalClosed Promise | const { modalClosed } = openModal(...); |
在 src/extensions/dashboard/modals/<folder-name>/ 中创建 三个文件:
extensions.ts - 包含模态框 ID、标题、尺寸、组件路径的构建器配置<modal-name>.tsx - 渲染模态框内容的 React 组件<modal-name>.config.ts - 可配置的模态框属性(标题、宽度、高度)创建所需的三个文件:
1.extensions.ts - 模态框构建器配置:
import { extensions } from '@wix/astro/builders';
import config from './<modal-name>.config.ts';
export default extensions.dashboardModal({
id: "{{GENERATE_UUID}}",
title: config.title,
width: config.width,
height: config.height,
component: './extensions/dashboard/modals/<modal-name>/<modal-name>.tsx',
});
关键:UUID 生成
id 必须是一个唯一的、静态的 UUID v4 字符串。为每个扩展生成一个新的 UUID - 请勿使用 randomUUID() 或复制示例中的 UUID。将 {{GENERATE_UUID}} 替换为新生成的 UUID,例如 "a1b2c3d4-e5f6-7890-abcd-ef1234567890"。
构建器字段:
| 字段 | 类型 | 描述 |
|---|---|---|
| id | string | 唯一的模态框 ID (GUID)。与 openModal() 一起使用 |
| title | string | 在项目仪表板中显示的模态框标题 |
| width | number | 加载时的初始宽度 |
| height | number | 加载时的初始高度 |
| component | string | 指向模态框内容 .tsx 文件的路径 |
2.<modal-name>.tsx - 模态框内容组件:
import type { FC } from 'react';
import { dashboard } from '@wix/dashboard';
import {
WixDesignSystemProvider,
Text,
Box,
CustomModalLayout,
} from '@wix/design-system';
import '@wix/design-system/styles.global.css';
import config from './<modal-name>.config.ts';
const { width, height, title } = config;
// 要打开你的模态框,请使用你的模态框 id 调用 `openModal`。
// 例如:
// import { dashboard } from '@wix/dashboard';
// function MyComponent() {
// return <button onClick={() => dashboard.openModal({ modalId: '8ef4d434-9c80-44f5-a3f5-6f15f3a34be7' })}>打开模态框</button>;
// }
const Modal: FC = () => {
return (
<WixDesignSystemProvider features={{ newColorsBranding: true }}>
<CustomModalLayout
width={width}
maxHeight={height}
primaryButtonText="保存"
secondaryButtonText="取消"
primaryButtonOnClick={() => dashboard.closeModal()}
secondaryButtonOnClick={() => dashboard.closeModal()}
title={title}
subtitle="编辑此文件以自定义你的模态框"
content={
<Box direction="vertical" align="center">
<Text>Wix CLI 模态框</Text>
</Box>
}
/>
</WixDesignSystemProvider>
);
};
export default Modal;
3.<modal-name>.config.ts - 可配置属性:
export default {
title: "我的模态框",
width: 600,
height: 400,
};
然后在 src/extensions.ts 中注册:
import { dashboardmodalYourModal } from './extensions/dashboard/modals/<modal-name>/extensions.ts';
export default app()
.use(dashboardmodalYourModal)
// ... 其他扩展
import { dashboard } from "@wix/dashboard";
// 简单打开
const result = await dashboard.openModal({
modalId: "your-modal-id", // 来自 .extension.ts 的 id 字段
});
// 通过 params 向模态框传递数据
const result = await dashboard.openModal({
modalId: "your-modal-id",
params: {
userId: user.id,
itemData: complexObject, // 对象直接传递,无需编码
},
});
// 获取模态框关闭时的通知
const { modalClosed } = dashboard.openModal({
modalId: "your-modal-id",
});
const result = await modalClosed; // 使用来自 closeModal() 的数据进行解析
使用 observeState() 来访问通过 openModal() 中的 params 传递的数据:
import { dashboard } from "@wix/dashboard";
import { useEffect, useState } from "react";
function MyModal() {
const [modalData, setModalData] = useState<{ userId?: string; itemData?: any }>({});
useEffect(() => {
dashboard.observeState((state) => {
// 访问通过 openModal params 传递的自定义数据
if (state.userId) {
setModalData({
userId: state.userId,
itemData: state.itemData,
});
}
});
}, []);
return <div>用户 ID: {modalData.userId}</div>;
}
从模态框扩展内部调用 closeModal() 来关闭它。可以选择性地将数据传递回打开者。
import { dashboard } from "@wix/dashboard";
// 关闭而不返回数据
dashboard.closeModal();
// 关闭并返回自定义数据
dashboard.closeModal({ saved: true, itemId: "123" });
| 参数 | 类型 | 描述 |
|---|---|---|
| closeData | 可序列化 (可选) | 要传递回模态框打开者的数据。必须可以通过结构化克隆算法进行克隆。 |
支持的类型: 字符串、数字、布尔值、普通对象、数组、日期、映射、集合、ArrayBuffers。不支持: 函数、DOM 节点、带有方法的类实例、符号、Promise。
返回: void
编辑 .config.ts 以进行有组织的设置:
export default {
title: '用户设置',
width: 600,
height: 500,
}
在 .tsx 中导入:
import config from './modal.config.ts';
export default function MyModal() {
return (
<CustomModalLayout
title={config.title}
// ... 组件的其余部分
/>
);
}
| 错误 | 修复方法 |
|---|---|
| 找不到模态框 ID | 检查 .extension.ts 文件的 id 字段 |
忘记在 extensions.ts 中注册 | 导入并 .use() 模态框 |
使用 extensionId 而不是 modalId | 在 openModal() 中使用 modalId |
| 无法在模态框中访问 params | 使用 dashboard.observeState() 来读取传递的数据 |
| 模态框无法关闭 | 使用来自 @wix/dashboard 的 dashboard.closeModal() |
// 仪表板页面:打开编辑模态框
const handleEdit = async (item: Item) => {
dashboard.openModal({
modalId: "edit-item-modal-guid",
params: {
itemId: item._id,
item: item, // 对象通过 params 直接传递
},
});
};
// 模态框:接收和保存数据
export default function ItemEditModal() {
const [formData, setFormData] = useState<Item | null>(null);
useEffect(() => {
dashboard.observeState((state) => {
if (state.item) {
setFormData(state.item);
}
});
}, []);
const handleSave = async () => {
// 保存逻辑
dashboard.showToast({ message: "已保存!", type: "success" });
dashboard.closeModal();
};
return (
<CustomModalLayout
title="编辑项目"
primaryButtonText="保存"
onCloseButtonClick={() => dashboard.closeModal()}
primaryButtonOnClick={handleSave}
content={/* 表单字段 */}
/>
);
}
每周安装量
195
代码仓库
GitHub 星标数
3
首次出现
2026年1月26日
安全审计
安装于
opencode173
cursor95
codex90
gemini-cli88
github-copilot84
kimi-cli79
Dashboard modals are popup dialogs triggered from dashboard pages or plugins. They consist of three files and use the Dashboard SDK for lifecycle control via openModal() and closeModal().
| Task | Method | Example |
|---|---|---|
| Create modal | Create 3 files in src/extensions/dashboard/modals/<folder>/ | See File Structure below |
| Open modal | dashboard.openModal() | openModal({ modalId: "modal-id" }) |
| Pass data to modal | params in openModal() | params: { userId: "123" } |
| Read data in modal | observeState() | dashboard.observeState((state) => { ... }) |
| Close modal | dashboard.closeModal() | closeModal() |
| Return data to parent | Pass data to closeModal() | closeModal({ ... }) |
| Wait for modal close | modalClosed Promise | const { modalClosed } = openModal(...); |
Create three files in src/extensions/dashboard/modals/<folder-name>/:
extensions.ts - Builder configuration with modal ID, title, dimensions, component path<modal-name>.tsx - React component rendering modal content<modal-name>.config.ts - Configurable modal properties (title, width, height)Create the three required files:
1.extensions.ts - Modal builder configuration:
import { extensions } from '@wix/astro/builders';
import config from './<modal-name>.config.ts';
export default extensions.dashboardModal({
id: "{{GENERATE_UUID}}",
title: config.title,
width: config.width,
height: config.height,
component: './extensions/dashboard/modals/<modal-name>/<modal-name>.tsx',
});
CRITICAL: UUID Generation
The id must be a unique, static UUID v4 string. Generate a fresh UUID for each extension - do NOT use randomUUID() or copy UUIDs from examples. Replace {{GENERATE_UUID}} with a freshly generated UUID like "a1b2c3d4-e5f6-7890-abcd-ef1234567890".
Builder fields:
| Field | Type | Description |
|---|---|---|
| id | string | Unique modal ID (GUID). Used with openModal() |
| title | string | Modal title shown in project dashboard |
| width | number | Initial width while loading |
| height | number | Initial height while loading |
| component | string | Path to the modal content .tsx file |
2.<modal-name>.tsx - Modal content component:
import type { FC } from 'react';
import { dashboard } from '@wix/dashboard';
import {
WixDesignSystemProvider,
Text,
Box,
CustomModalLayout,
} from '@wix/design-system';
import '@wix/design-system/styles.global.css';
import config from './<modal-name>.config.ts';
const { width, height, title } = config;
// To open your modal, call `openModal` with your modal id.
// e.g.
// import { dashboard } from '@wix/dashboard';
// function MyComponent() {
// return <button onClick={() => dashboard.openModal({ modalId: '8ef4d434-9c80-44f5-a3f5-6f15f3a34be7' })}>Open Modal</button>;
// }
const Modal: FC = () => {
return (
<WixDesignSystemProvider features={{ newColorsBranding: true }}>
<CustomModalLayout
width={width}
maxHeight={height}
primaryButtonText="Save"
secondaryButtonText="Cancel"
primaryButtonOnClick={() => dashboard.closeModal()}
secondaryButtonOnClick={() => dashboard.closeModal()}
title={title}
subtitle="Edit this file to customize your modal"
content={
<Box direction="vertical" align="center">
<Text>Wix CLI Modal</Text>
</Box>
}
/>
</WixDesignSystemProvider>
);
};
export default Modal;
3.<modal-name>.config.ts - Configurable properties:
export default {
title: "My Modal",
width: 600,
height: 400,
};
Then register in src/extensions.ts:
import { dashboardmodalYourModal } from './extensions/dashboard/modals/<modal-name>/extensions.ts';
export default app()
.use(dashboardmodalYourModal)
// ... other extensions
import { dashboard } from "@wix/dashboard";
// Simple open
const result = await dashboard.openModal({
modalId: "your-modal-id", // From .extension.ts id field
});
// Pass data to modal via params
const result = await dashboard.openModal({
modalId: "your-modal-id",
params: {
userId: user.id,
itemData: complexObject, // Objects are passed directly, no encoding needed
},
});
// Get notified when the modal is closed
const { modalClosed } = dashboard.openModal({
modalId: "your-modal-id",
});
const result = await modalClosed; // Resolves with data from closeModal()
Use observeState() to access data passed via params in openModal():
import { dashboard } from "@wix/dashboard";
import { useEffect, useState } from "react";
function MyModal() {
const [modalData, setModalData] = useState<{ userId?: string; itemData?: any }>({});
useEffect(() => {
dashboard.observeState((state) => {
// Access custom data passed through openModal params
if (state.userId) {
setModalData({
userId: state.userId,
itemData: state.itemData,
});
}
});
}, []);
return <div>User ID: {modalData.userId}</div>;
}
Call closeModal() from within the modal extension to close it. Optionally pass data back to the opener.
import { dashboard } from "@wix/dashboard";
// Close without returning data
dashboard.closeModal();
// Close with custom return data
dashboard.closeModal({ saved: true, itemId: "123" });
| Parameter | Type | Description |
|---|---|---|
| closeData | Serializable (optional) | Data to pass back to the modal opener. Must be cloneable via structured clone algorithm. |
Supported types: strings, numbers, booleans, plain objects, arrays, Dates, Maps, Sets, ArrayBuffers. Not supported: functions, DOM nodes, class instances with methods, Symbols, Promises.
Returns: void
Edit .config.ts for organized settings:
export default {
title: 'User Settings',
width: 600,
height: 500,
}
Import in .tsx:
import config from './modal.config.ts';
export default function MyModal() {
return (
<CustomModalLayout
title={config.title}
// ... rest of component
/>
);
}
| Mistake | Fix |
|---|---|
| Can't find modal ID | Check .extension.ts file's id field |
Forgetting to register in extensions.ts | Import and .use() the modal |
Using extensionId instead of modalId | Use modalId in openModal() |
// Dashboard Page: Opening edit modal
const handleEdit = async (item: Item) => {
dashboard.openModal({
modalId: "edit-item-modal-guid",
params: {
itemId: item._id,
item: item, // Objects passed directly via params
},
});
};
// Modal: Receiving and saving data
export default function ItemEditModal() {
const [formData, setFormData] = useState<Item | null>(null);
useEffect(() => {
dashboard.observeState((state) => {
if (state.item) {
setFormData(state.item);
}
});
}, []);
const handleSave = async () => {
// Save logic
dashboard.showToast({ message: "Saved!", type: "success" });
dashboard.closeModal();
};
return (
<CustomModalLayout
title="Edit Item"
primaryButtonText="Save"
onCloseButtonClick={() => dashboard.closeModal()}
primaryButtonOnClick={handleSave}
content={/* form fields */}
/>
);
}
Weekly Installs
195
Repository
GitHub Stars
3
First Seen
Jan 26, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode173
cursor95
codex90
gemini-cli88
github-copilot84
kimi-cli79
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
116,600 周安装
| Can't access params in modal | Use dashboard.observeState() to read passed data |
| Modal won't close | Use dashboard.closeModal() from @wix/dashboard |