跳到主要内容

自定义 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" />;

要点:

  1. Tab 建议用 SectionTab 包一层,保证 hover / active / 深浅主题与内置 Tab 一致
  2. Panel 要用 observer 包裹(否则 store 变化时不会重渲染)
  3. 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 里的 InpaintSectionIdphotoSection 就是这样工作的:它们 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 样式。


下一步