a2ui-auto-layout
v0.2.3
Published
A2UI 0.9 布局编辑器 — 单包,setup.css(Less + scoped Tailwind 工具类)全局样式
Maintainers
Readme
a2ui-auto-layout
A2UI 0.9 可视化布局编辑器 — 单 npm 包,提供布局编辑、Card Studio 卡片编辑、声明式 Surface 渲染与 Registry 注册能力。
- 布局编辑器
A2UILayoutEditor:资源库 + 画布 + 检查器,输出A2UIDocument - 卡片编辑器
A2UICardEditor:编辑声明式卡片 Surface,输出CardStudioEnvelope - Surface 渲染
AdaptiveDeclarativeSurface等:卡片 JSON 纯回显 - 样式 全局
setup.css(Less 层 + 作用域化 Tailwind 工具类),宿主入口引入一次即可;无需在宿主配置 Tailwind 扫描本包;utility 不泄漏到编辑器外
环境要求
| 依赖 | 版本 |
|------|------|
| react / react-dom | ^18.0.0 \|\| ^19.0.0(peer) |
| 构建工具 | Vite / Webpack / Umi 等支持 ESM 的 bundler |
安装
pnpm add a2ui-auto-layout
# 或 npm install a2ui-auto-layout在应用入口引入全局样式(npm 消费路径下 必须,否则编辑器与 Surface 样式会缺失):
import 'a2ui-auto-layout/setup.css';TypeScript 宿主无需额外配置:package.json 的 exports / typesVersions 已为 setup.css 提供类型。若旧版 bundler 仍报「找不到模块」,在宿主增加 declare module 'a2ui-auto-layout/setup.css';(见 PUBLISHING.md)。
再按需 import 组件:
import {
A2UILayoutEditor,
A2UICardEditor,
AdaptiveDeclarativeSurface,
createA2UIRegistry,
registerDeclarativeCardFromEnvelope,
} from 'a2ui-auto-layout';快速开始
布局编辑器
编辑器默认占满父容器,宿主需为父级提供明确高度(如 h-screen / height: 100vh):
import { A2UILayoutEditor, createA2UIRegistry } from 'a2ui-auto-layout';
import 'a2ui-auto-layout/setup.css';
const registry = createA2UIRegistry();
export function LayoutStudioPage() {
return (
<div className="flex h-screen flex-col">
<div className="min-h-0 flex-1">
<A2UILayoutEditor
registry={registry}
className="h-full min-h-0"
onEvent={(payload) => console.log('surface event', payload)}
onSave={(doc) => console.log('saved', doc)}
/>
</div>
</div>
);
}上述示例
registry为空,侧栏无业务卡片。将卡片编辑器产出的CardStudioEnvelope注册进 registry 后,资源库即可拖入,见下文 Registry 配置。
卡片编辑器
A2UICardEditor 无内置顶栏,保存 / 导入等操作由宿主通过 ref 触发:
import { useRef } from 'react';
import {
A2UICardEditor,
type A2UICardEditorHandle,
type CardStudioEnvelope,
} from 'a2ui-auto-layout';
import 'a2ui-auto-layout/setup.css';
export function CardStudioPage({ envelope }: { envelope: CardStudioEnvelope }) {
const cardRef = useRef<A2UICardEditorHandle>(null);
return (
<div className="flex h-screen flex-col">
<header className="flex gap-2 border-b p-2">
<button type="button" onClick={() => cardRef.current?.save()}>
保存
</button>
<button type="button" onClick={() => cardRef.current?.openComposerImport()}>
导入 Composer JSON
</button>
</header>
<div className="min-h-0 flex-1">
<A2UICardEditor
ref={cardRef}
initialEnvelope={envelope}
categoryOptions={[{ key: 'card', label: '卡片' }]}
className="h-full min-h-0"
/>
</div>
</div>
);
}Registry 配置(布局资源库)
布局编辑器左侧「资源库」的可拖模板来自 A2UIRegistry。宿主通常只有卡片编辑器产出的 CardStudioEnvelope(与 A2UICardEditor 的 onSave / exportEnvelope() 同形)。
推荐:一行信封数组(0.2.3+):
import { useEffect, useState } from 'react';
import { A2UILayoutEditor, type CardStudioEnvelope } from 'a2ui-auto-layout';
import 'a2ui-auto-layout/setup.css';
export function LayoutStudioPage() {
const [cards, setCards] = useState<CardStudioEnvelope[]>([]);
useEffect(() => {
fetch('/api/cards').then((r) => r.json()).then(setCards);
}, []);
return (
<A2UILayoutEditor
cardEnvelopes={cards}
className="h-full min-h-0"
mode={{ workMode: 'edit', layoutMode: 'flow' }}
onSave={(doc) => saveLayout(doc)}
/>
);
}cardEnvelopes 会自动:
- 调用
createA2UIRegistryFromEnvelopes注册资源库 - 从
template.category推导templateGroups(可选手动覆盖)
也支持纯 surface JSON({ components, data }),内部会 normalizeCardStudioEnvelope 容错。
高级:手动组装 registry(自定义合并逻辑时):
CardStudioEnvelope[](API / 本地存储)
│
▼
createA2UIRegistryFromEnvelopes(envelopes) 或 registerDeclarativeCardFromEnvelope × N
│
▼
<A2UILayoutEditor registry={registry} />信封结构(CardStudioEnvelope):
{
name: string;
description: string;
template: {
id: string;
component: string; // 唯一键,同名 register 会覆盖
category: string; // 资源库分组,如 'card'
icon?: string;
defaultLayout?: Record<string, unknown>;
};
surface: {
components: [...];
data: Record<string, unknown>;
};
meta?: { savedAt?: string };
}静态单卡(联调 / Test 页):
import { A2UILayoutEditor, type CardStudioEnvelope } from 'a2ui-auto-layout';
const smokeCard: CardStudioEnvelope = { /* 同上 */ };
export function LayoutStudioPage() {
return (
<A2UILayoutEditor
cardEnvelopes={[smokeCard]}
className="h-full min-h-0"
mode={{ workMode: 'edit', layoutMode: 'flow' }}
/>
);
}生产:从 API 加载卡片列表:
import { useEffect, useState } from 'react';
import { A2UILayoutEditor, type CardStudioEnvelope } from 'a2ui-auto-layout';
export function LayoutStudioPage() {
const [envelopes, setEnvelopes] = useState<CardStudioEnvelope[]>([]);
useEffect(() => {
fetch('/api/cards').then((r) => r.json()).then(setEnvelopes);
}, []);
const handleCardSave = (envelope: CardStudioEnvelope) => {
setEnvelopes((prev) => {
const component = envelope.template.component;
const idx = prev.findIndex((e) => e.template.component === component);
if (idx >= 0) {
const next = [...prev];
next[idx] = envelope;
return next;
}
return [...prev, envelope];
});
// 同时 POST 到你的 API
};
return (
<A2UILayoutEditor
cardEnvelopes={envelopes}
className="h-full min-h-0"
resourceLibraryEmptyMessage="暂无卡片,请先在卡片编辑器创建"
/>
);
}卡片编辑器保存时与布局编辑器共用同一份 envelopes 状态;handleCardSave 传给 A2UICardEditor 的 onSave 即可,保存后资源库立即可拖。
运行时增量注册(不重建 registry 时):
registry.register(registerDeclarativeCardFromEnvelope(envelope));| 项 | 说明 |
|----|------|
| createA2UIRegistry() 无参数 | 资源库为空(仅 PageRoot),侧栏显示「没有匹配的组件」 |
| useBuiltinComponents: true | 合并内置 Dashboard 组件(与业务卡片并存,非默认推荐) |
| templateGroups | 可选;控制资源库分组标题与顺序,由 template.category 匹配 |
| API 返回纯 surface | 用 normalizeCardStudioEnvelope(json) 规范化后再注册 |
cardEnvelopes(0.2.3+)是推荐接入方式;createA2UIRegistryFromEnvelopes与registerDeclarativeCardFromEnvelope供高级场景使用。
完整数据流、Umi 宿主与 Card Studio 联调见 宿主接入 §6 / §9。
工作模式
| 模式 | 典型场景 | 推荐 API |
|------|----------|----------|
| 编辑 | 配置台、Card Studio | A2UILayoutEditor / A2UICardEditor(默认) |
| 预览 | 编辑器内交互验收 | ref.setWorkMode('preview') 或 Card setPreviewMode(true) |
| 纯回显 | 业务详情页只读展示 | 布局:A2UILayoutEditor + showChrome={false} + preview;卡片:AdaptiveDeclarativeSurface |
预览 ≠ 纯回显:预览仍在编辑器壳内。布局若需零编辑 UI,见 宿主接入 §7.7 / §10。
纯回显示例
布局(编辑器壳内,快速接入):
import { A2UILayoutEditor, createA2UIRegistry, type A2UIDocument } from 'a2ui-auto-layout';
import 'a2ui-auto-layout/setup.css';
const registry = createA2UIRegistry();
export function LayoutDisplayPage({ document }: { document: A2UIDocument }) {
return (
<A2UILayoutEditor
showChrome={false}
className="h-full min-h-0"
mode={{ workMode: 'preview', layoutMode: 'flow' }}
document={document}
registry={registry}
onEvent={(payload) => console.log(payload)}
/>
);
}卡片 Surface(推荐生产路径):
import { AdaptiveDeclarativeSurface, ModalHostProvider } from 'a2ui-auto-layout';
import type { DeclarativeCardDocument } from 'a2ui-auto-layout';
import 'a2ui-auto-layout/setup.css';
export function CardDisplayPage({ surface }: { surface: DeclarativeCardDocument }) {
return (
<ModalHostProvider defaultModalMount="canvas">
<AdaptiveDeclarativeSurface
backend="hybrid"
composerParity
workMode="preview"
root={surface.root}
components={surface.components}
data={surface.data}
onSurfaceEvent={(name, payload) => console.log(name, payload)}
/>
</ModalHostProvider>
);
}样式说明
| 项 | 说明 |
|----|------|
| 入口 | import 'a2ui-auto-layout/setup.css' — 唯一推荐的全局样式入口 |
| 内容 | 编辑器主题、chrome、声明式 Surface、Composer parity、A2UI catalog 层 |
| Tailwind | 工具类限定在 .a2ui-auto-layout-root 与 .a2ui-declarative-surface 内,不会作为全局 .flex / .bg-primary 泄漏到宿主 |
| 宿主 reset | 编辑器壳内原生 button / input 有轻量 reset,减轻 Ant Design 等全局样式覆盖 |
| 构建 | 源码 *.module.less → dist/setup.css;JS bundle 不含 CSS side-effect |
Umi + MFSU 宿主建议在 .umirc.ts 中排除预构建:
mfsu: { exclude: ['a2ui-auto-layout', '@a2ui/react', '@a2ui/web_core'] },升级本包版本后清缓存:
pnpm exec rimraf src\.umi node_modules\.cache\mfsu不要在宿主页面依赖 setup.css 泄漏的全局 utility;宿主自己的 .bg-primary / .hidden 与包内 utility 已隔离。setup.css 可放在宿主 global.less 末尾(推荐)或任意顺序(scope 后编辑器内部均生效)。
样式仍异常时,确认版本 ≥ 0.2.2、入口已 import setup.css、MFSU 已 exclude;更多排查见 宿主接入 §4。
容器尺寸约定
编辑器根节点使用 height: 100% + min-height: 0 填满父级。推荐宿主结构:
<div className="flex h-screen flex-col">
{/* 可选:宿主顶栏 */}
<div className="min-h-0 flex-1">
<A2UILayoutEditor className="h-full min-h-0" /* ... */ />
</div>
</div>避免在 body 与编辑器之间产生双重滚动条。
主要导出
| 类别 | 符号 |
|------|------|
| 编辑器 | A2UILayoutEditor, A2UICardEditor |
| Registry | cardEnvelopes, createA2UIRegistryFromEnvelopes, buildTemplateGroupsFromEnvelopes |
| 渲染 | AdaptiveDeclarativeSurface, HybridDeclarativeSurface, OfficialDeclarativeSurface, ModalHostProvider |
| 类型 | A2UIDocument, CardStudioEnvelope, A2UIEventPayload, EditorTheme |
| 样式 | a2ui-auto-layout/setup.css(package export) |
完整 API 与 ref 方法见 DEVELOPMENT.md 附录 F。
本仓库开发
pnpm install
pnpm dev # build dist + 启动 demo → http://localhost:5173
pnpm build # dist/index.js + dist/index.d.ts + dist/setup.css
pnpm test
pnpm lint
pnpm typecheck
pnpm prepublish:verify # 发布前全量验证(lint / build / demo / smoke / leftovers)demo/ 与 npm 宿主行为一致:只消费 dist/,不 alias 到 src/。修改 src/ 后需重新 pnpm build(或另开终端 pnpm build:watch)。
| 命令 | 用途 |
|------|------|
| pnpm check:demo | demo 生产构建 |
| pnpm check:smoke | pack → 临时目录 npm install → Vite 构建(模拟 npm 消费者) |
| pnpm check:publish | 产物体积、exports、CSS 抽检 |
发布(维护者)
npm login
pnpm prepublish:verify
pnpm release # verify + npm publish --access public文档索引
| 文档 | 说明 | |------|------| | 宿主接入双编辑器 | 安装、样式、Registry、三种模式、纯回显、Umi FAQ | | 发布到 npm | 版本、验证门禁、smoke 测试 | | 开发文档 | 架构、本地调试、API 速查 | | AI 撰写 Surface | 声明式 JSON 编写指南(含 画布组件 JSON 约束规范) | | 声明式模式 | Row / Card / Chart 等布局模式 |
License
MIT
