自定义 Section
Section 是 <SidePanel /> 的基本单元。一个 Section 描述了"侧边上的 Tab 图标是什么"以及"点开 Tab 之后右边展示的内容是什么"。
本页介绍 3 种场景:新增 / 替换 / 按需展开。
Section 类型定义
export type Section = {
/** 唯一标识 */
name: string;
/** 侧边竖列里的 Tab 按钮组件 */
Tab: React.ComponentType<{
onClick: () => void;
active: boolean;
}>;
/** 点击 Tab 后右侧展开的内容组件 */
Panel: React.ComponentType<{
store: StoreType;
}>;
/**
* 是否出现在常驻 Tab 列表里
* false = 不显示 Tab,但可以通过 store.openSidePanel(name) 按需打开
* 默认 true
*/
visibleInList?: boolean;
};
场景 1:新增一个 Section
最小示例:一个"自定义"面板。
import { observer } from 'mobx-react-lite';
import { Sparkles } from 'lucide-react';
import {
SidePanel,
SectionTab,
DEFAULT_SECTIONS,
} from '@ydesign/react-editor/side-panel';
import type { Section } from '@ydesign/react-editor/side-panel';
const AISection: Section = {
name: 'ai',
// 侧边 Tab:图标 + 名字(SectionTab 是内置的 Tab 容器,建议复用)
Tab: observer(props => (
<SectionTab name="AI 助手" {...props}>
<Sparkles size={20} />
</SectionTab>
)),
// 点开之后的内容;observer 让 store 变化自动触发重渲染
Panel: observer(({ store }) => {
return (
<div style={{ padding: 12 }}>
<h3>用 AI 生成图片</h3>
<button
onClick={async () => {
const url = await callYourAIService('一只可爱的猫咪');
// 通过 store 往画布加图片
store.addElement({
type: 'image',
src: url,
left: 100,
top: 100,
width: 500,
height: 500,
});
}}
>
生成一张
</button>
<p style={{ marginTop: 16 }}>
当前画布:{store.width} × {store.height},共 {store.objects.length} 个元素
</p>
</div>
);
}),
};
// 与默认 Section 组合:插到最后
const sections = [...DEFAULT_SECTIONS, AISection];
// 初次打开时停留在 AI 面板
<SidePanel store={store} sections={sections} defaultSection="ai" />;
要点:
Tab建议用SectionTab包一层,保证 hover / active / 深浅主题与内置 Tab 一致Panel要用observer包裹(否则store变化时不会重渲染)store是 MST 实例,直接调store.addElement(...)/store.set(...)就能向画布下发
场景 2:替换内置 Section
想把内置的 PhotosSection 换成你自己的图库?只要造一个同名 Section(name: 'photos'),放进 sections 数组即可 —— 同名后者会生效。
import {
SidePanel,
SectionTab,
DEFAULT_SECTIONS,
} from '@ydesign/react-editor/side-panel';
import type { Section } from '@ydesign/react-editor/side-panel';
import { observer } from 'mobx-react-lite';
import { Image } from 'lucide-react';
import { useEffect, useState } from 'react';
const MyPhotosSection: Section = {
name: 'photos', // 👈 和内置同名
Tab: observer(props => (
<SectionTab name="我的图库" {...props}>
<Image size={20} />
</SectionTab>
)),
Panel: observer(({ store }) => {
const [list, setList] = useState<any[]>([]);
useEffect(() => {
fetch('/my-api/photos').then(r => r.json()).then(setList);
}, []);
return (
<div>
{list.map(item => (
<img
key={item.id}
src={item.thumbnail}
style={{ width: '100%', cursor: 'pointer', marginBottom: 8 }}
onClick={() =>
store.addElement({
type: 'image',
src: item.url,
left: 50,
top: 50,
width: item.width,
height: item.height,
})
}
/>
))}
</div>
);
}),
};
// 把默认 photos 换成我们的
const sections = DEFAULT_SECTIONS.map(s =>
s.name === 'photos' ? MyPhotosSection : s
);
<SidePanel store={store} sections={sections} />;
💡 想保留内置面板的大部分能力、只改 UI 里一两处?推荐直接复用
<ImagesGrid />组件,大部分素材面板的布局代码都能省掉。
场景 3:移除某个 Section
传一个名字数组、省掉你不想要的名字即可:
<SidePanel
store={store}
sections={['templates', 'text', 'photos', 'background', 'layers']}
// upload / shapes / size / idphoto / inpaint 都不会出现
/>
或者基于 DEFAULT_SECTIONS 过滤:
import { DEFAULT_SECTIONS } from '@ydesign/react-editor/side-panel';
const sections = DEFAULT_SECTIONS.filter(s => s.name !== 'upload');
<SidePanel store={store} sections={sections} />;
visibleInList:按需展开的面板
有些面板不应该常驻在 Tab 列表,只有进入特定工作流时才弹出来。比如:
- AI 消除笔 —— 只有选中一张图片、点工具栏按钮进入"消除笔模式"时才显示
- 证件照排版 —— 只有用户点"生成证件照"按钮后才进入
- 元素属性编辑器 —— 只有选中某个自定义元素时才在侧边弹出
这类 Section 设置 visibleInList: false 即可:
import { autorun } from 'mobx';
const TextQuickEditSection: Section = {
name: 'text-quick-edit',
visibleInList: false, // 👈 不在 Tab 列表里显示
Panel: observer(({ store }) => {
const el = store.selectedElements[0];
if (el?.type !== 'textbox') return null;
return (
<div>
<textarea
value={el.text}
onChange={e => store.set({ text: e.target.value }, el)}
/>
</div>
);
}),
};
// 在 sections 里注册(仍然要注册,只是不显示 Tab)
const sections = [...DEFAULT_SECTIONS, TextQuickEditSection];
// 通过 MobX autorun 根据选中态自动打开
autorun(() => {
const el = store.selectedElements[0];
if (el?.type === 'textbox') {
store.openSidePanel('text-quick-edit');
}
});
<SidePanel store={store} sections={sections} />;
📌
DEFAULT_SECTIONS里的InpaintSection和IdphotoSection就是这样工作的:它们visibleInList: false,平时隐藏,由业务代码在适当时机调store.openSidePanel('inpaint')/store.openSidePanel('idphoto')打开。
手动控制开关
// 打开
store.openSidePanel('ai');
// 关闭(传空字符串)
store.openSidePanel('');
// 当前打开的面板
store.openedSidePanel;
SectionTab 说明
SectionTab 是所有内置 Section 的 Tab 共用组件。它接受 Tab 特有的 props(onClick / active)加上自己的:
<SectionTab
name="面板名字" // Tab 按钮下方的文字
{...props} // 由 <SidePanel /> 传进来的 onClick / active
>
{/* 这里放图标,推荐 lucide-react */}
<YourIcon size={20} />
</SectionTab>
用了 SectionTab,你的自定义 Tab 就会自动:
- 跟随 hover / active 态变色
- 响应深 / 浅主题切换
- 保持与内置 Tab 一致的尺寸和边距
如果你想做完全自定义的 Tab 按钮,也可以不使用 SectionTab,直接渲染一个自己的 <div onClick={props.onClick}>,只是需要自己处理 active 样式。
下一步
- 👉 内置 10 个 Section 详解
- 👉 ImagesGrid 组件 —— 素材网格快捷组件
- 👉 定制化 · SidePanel