使用场景
侧边信息栏主要用于在 AI 场景下,用于信息展示,功能配置。包括 MCP 配置、参考来源、代码预览、富文本预览及编辑等功能
代码演示
如何引入
import { Annotation, MCPConfigure, Sidebar } from '@fibrous/semi-ai-solid';
const { Container, CodeContent, CodeItem, FileContent, FileItem } = Sidebar;
const { AnnotationContent } = Annotation;
基础容器
侧边信息栏的基础容器,基础容器是MCP 配置、参考来源、以及侧边消息栏的基础容器。
visible配合onCancel使用控制显示和隐藏title设置标题,可传任意内容;需要自定义整个头部时使用renderHeadermotion设置是否有展开动画resizable设置宽度是否可伸缩,为true时候可通过minWidth,maxWidth设置最小,最大宽度
代码
import { type JSX, createSignal } from 'solid-js';
import { Sidebar } from '@fibrous/semi-ai-solid';
import { Button, Switch } from '@fibrous/semi-ui-solid';
const { Container } = Sidebar;
const containerStyle: JSX.CSSProperties = {
display: 'flex',
height: '500px',
border: '1px solid var(--semi-color-border)',
'border-radius': '8px',
overflow: 'hidden',
'box-sizing': 'border-box',
};
export default function SidebarDemo() {
const [visible, setVisible] = createSignal(true);
const [motion, setMotion] = createSignal(true);
const [resizable, setResizable] = createSignal(true);
const toggleVisible = () => setVisible((current) => !current);
return (
<div style={containerStyle}>
<div style={{ 'flex-grow': 1, 'flex-shrink': 1, padding: '20px' }}>
<div>
<Button aria-pressed={visible()} onClick={toggleVisible}>
显示容器
</Button>
<div style={{ display: 'flex', 'align-items': 'center', 'margin-top': '10px' }}>
<span id="sidebar-container-motion">是否有动画</span> <Switch aria-labelledby="sidebar-container-motion" checked={motion()} onChange={setMotion} />
</div>
<div style={{ display: 'flex', 'align-items': 'center', 'margin-top': '10px' }}>
<span id="sidebar-container-resizable">是否可伸缩</span> <Switch aria-labelledby="sidebar-container-resizable" checked={resizable()} onChange={setResizable} />
</div>
</div>
</div>
<Container motion={motion()} resizable={resizable()} minWidth={250} maxWidth="60%" visible={visible()} title="基础容器示例" style={resizable() ? {} : { width: '200px' }} onCancel={toggleVisible} />
</div>
);
}MCP 配置
使用 MCPConfigure 进行 MCP 工具的展示、启用/关闭、配置及搜索
- 通过
options和customOptions设置内置的 MCP 工具和自定义 MCP 工具 - 通过
visible设置组件的显示和隐藏, 使用onCancel监听用户的关闭行为 - 使用
onStatusChange处理 MCP 工具的启用/关闭,组件不会自行修改传入的列表,需把回调参数写回options或customOptions - 使用
onAddClick处理自定义 MCP 页的点击添加按钮后的操作 - 可通过
onConfigureClick监听内置 MCP 工具的配置,通过onEditClick监听自定义 MCP 工具的配置 - 工具项的图标和描述使用
renderIcon、renderDesc渲染,整行用renderItem替换;参数为{ option, custom }
其他 MCPConfigure 的 API 见 MCPConfigure
传入 MCP 工具项的类型如下
interface MCPConfigureOption {
icon?: string; // 图片地址,其他内容使用 renderIcon
label?: string;
value: string; // 必填,标识工具项,需唯一
desc?: string; // 纯文本,其他内容使用 renderDesc
active?: boolean;
disabled?: boolean;
configure?: boolean; // 是否显示配置按钮
}
用例如下:
代码
import { type Component, type JSX, Show, createSignal } from 'solid-js';
import { Dynamic } from 'solid-js/web';
import { MCPConfigure, type MCPConfigureOption as MCPOption } from '@fibrous/semi-ai-solid';
import { IconFigma, IconSemiLogo } from '@fibrous/semi-icons-solid';
import { Button, Form, FormInput, FormTextArea, Modal, type FormHandle } from '@fibrous/semi-ui-solid';
interface McpFormValues {
name: string;
src: string;
desc: string;
}
// icon only takes an image URL, so the built-in options get their icons from renderIcon
const builtinIcons: Record<string, Component> = {
'Semi mcp': IconSemiLogo,
figma: IconFigma,
};
const imageStyle: JSX.CSSProperties = { width: '100%', height: '100%' };
const defaultOptions: MCPOption[] = [
{
value: 'Semi mcp',
label: 'Semi',
configure: true,
desc: '支持 Semi 的文档、源码搜索,辅助开发',
},
{
value: 'figma',
label: 'Figma',
desc: 'Figma MCP Server 连接Figma与AI开发工具的功能。它通过标准化的模型上下文协MCP),将组件、变量等设计数据和上下文暴露给AI,从而实现从设计稿到代码的智能生成,显著提升开发效率。',
},
];
let index = 1;
const containerStyle: JSX.CSSProperties = {
display: 'flex',
height: '500px',
border: '1px solid var(--semi-color-border)',
'border-radius': '8px',
overflow: 'hidden',
'box-sizing': 'border-box',
};
export default function SidebarDemo() {
const [visible, setVisible] = createSignal(true);
const [options, setOptions] = createSignal(defaultOptions);
const [mVisible, setMVisible] = createSignal(false);
const [customOptions, setCustomOptions] = createSignal<MCPOption[]>([]);
let form: FormHandle<McpFormValues> | undefined;
const toggleVisible = () => setVisible((current) => !current);
const handleOk = () => {
form?.formApi
.validate()
.then((values) => {
setCustomOptions((current) => [
...current,
{
label: values.name,
icon: values.src,
value: `mcp-${index++}`,
desc: values.desc,
},
]);
setMVisible(false);
})
.catch((errors) => {
console.log('errors', errors);
});
};
const onStatusChange = (changed: MCPOption[], custom: boolean) => {
if (custom) {
setCustomOptions(changed);
} else {
setOptions(changed);
}
};
return (
<div style={containerStyle}>
<div style={{ 'flex-grow': 1, 'flex-shrink': 1, padding: '20px' }}>
<Button aria-pressed={visible()} onClick={toggleVisible}>
MCP 配置
</Button>
</div>
<MCPConfigure
style={{ width: '500px' }}
resizable={false}
visible={visible()}
onCancel={toggleVisible}
options={options()}
customOptions={customOptions()}
onStatusChange={onStatusChange}
onAddClick={() => setMVisible(true)}
onConfigureClick={(_e, option) => console.log('configure click', option)}
onEditClick={(_e, option) => console.log('edit click', option)}
renderIcon={({ option }) => (
<Show when={option.icon} fallback={<Dynamic component={builtinIcons[option.value]} />}>
{(src) => <img alt="" src={src()} style={imageStyle} />}
</Show>
)}
/>
<Modal title="自定义 MCP" visible={mVisible()} onOk={handleOk} onCancel={() => setMVisible(false)} closeOnEsc>
<Form ref={(node) => (form = node)} layout="vertical" onValueChange={(values) => console.log(values)}>
<FormInput rules={[{ required: true, message: '请输入 MCP 名称' }]} field="name" label="MCP 名称" style={{ width: '100%' }} />
<FormInput
initValue="/assets/placeholder/avatar-a.svg"
rules={[
{ required: true, message: '请输入 MCP 图标 URL' },
{
validator: (_rule: unknown, value: string) => {
const urlRegex = /^(?:https?:\/\/)?(?:www\.)?[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+(?::\d+)?(?:\/[^\s]*)?$/i;
return urlRegex.test(value);
},
message: '请输入有效的 MCP 图标 URL',
},
]}
field="src"
label="MCP 图标 URL"
style={{ width: '100%' }}
/>
<FormTextArea rules={[{ required: true, message: '请输入 MCP 介绍' }]} field="desc" label="MCP 介绍" style={{ width: '100%' }} />
</Form>
</Modal>
</div>
);
}参考来源
使用 Annotation 组件可以管理参考来源的展示
activeKey配合onChange管理当前展开的项info设置参考来源的具体内容onClick在点击某条参考来源时触发,参数为事件和该条目- 分组标题使用
renderInfoHeader渲染,条目使用renderItem替换;renderHeader是整个容器头部
代码
import { type JSX, createSignal } from 'solid-js';
import { Annotation, type AnnotationInfo, type AnnotationItem } from '@fibrous/semi-ai-solid';
import { Button } from '@fibrous/semi-ui-solid';
const containerStyle: JSX.CSSProperties = {
display: 'flex',
height: '500px',
border: '1px solid var(--semi-color-border)',
'border-radius': '8px',
overflow: 'hidden',
'box-sizing': 'border-box',
};
const defaultInfoList: AnnotationInfo[] = [
{
header: 'Semi design introduction',
key: '1',
annotations: [
{
order: 1,
type: 'video',
duration: 4432,
title: 'Semi Design is a design system designed',
url: 'https://semi.design/',
logo: '/assets/placeholder/avatar-a.svg',
siteName: 'Semi Design',
detail: ' As a comprehensive, easy-to-use, and high-quality modern enterprise-level application UI solution',
img: '/assets/root-web-sites/abstract.jpg',
},
{
order: 2,
title: 'Quick start',
type: 'video',
duration: 56,
url: 'https://semi.design/',
detail: ' As a comprehensive, easy-to-use, and high-quality modern enterprise-level application UI solution',
logo: '/assets/placeholder/avatar-a.svg',
siteName: 'Semi Design',
img: '/assets/root-web-sites/abstract.jpg',
},
{
order: 3,
title: 'Use components in a modular way',
url: 'https://semi.design/',
detail: `Semi provides esm format dist, and the css of the component is only imported by the corresponding js.
When used in Webpack, Rspack, create-react-app or Vite projects, there is no need to configure any compilation items.`,
logo: '/assets/placeholder/avatar-a.svg',
siteName: 'Semi Design',
img: '/assets/root-web-sites/abstract.jpg',
},
],
},
{
header: 'Design resource',
key: '2',
annotations: [
{
order: 2,
title: 'Semi Design resource',
url: 'https://semi.design/',
detail: 'Semi Design provides a wealth of design resources to help designers and developers collaborate efficiently. Whether you are a community user or a ByteDance internal designer, you can find UI Kit resource and Figma plug-ins that suit you here.',
logo: '/assets/placeholder/avatar-a.svg',
siteName: 'Semi Design resource',
img: '/assets/root-web-sites/abstract.jpg',
},
],
},
];
export default function SidebarDemo() {
const [visible, setVisible] = createSignal(true);
const [activeKey, setActiveKey] = createSignal<string | string[]>('1');
const toggleVisible = () => setVisible((current) => !current);
const onChange = (key: string | string[]) => {
console.log('onChange', key);
setActiveKey(key);
};
const onClick = (e: MouseEvent, item: AnnotationItem) => {
console.log('onClick', e, item);
};
return (
<div style={containerStyle}>
<div style={{ 'flex-grow': 1, 'flex-shrink': 1, padding: '20px' }}>
<Button aria-pressed={visible()} onClick={toggleVisible}>
参考来源
</Button>
</div>
<Annotation defaultSize={{ width: 420 }} visible={visible()} onCancel={toggleVisible} activeKey={activeKey()} info={defaultInfoList} onChange={onChange} onClick={onClick} />
</div>
);
}代码展示
可通过 Sidebar 中的 CodeItem 展示代码,CodeItem 基于 JsonViewer 以及 CodeHighlight 实现。
content: 内容字符串isJson: 是否为 jsonlanguage: 编程语言名称,同CodeHighlight的 language,需先通过CodeHighlight.register注册jsonViewerProps: 配置其他 JsonViewer 参数codeHighlightProps: 配置其他 CodeHighlight 参数
jsonViewerProps 与 codeHighlightProps 是 Solid 版 JsonViewer 与 CodeHighlight 的属性,晚于内置默认值展开,因此其中的 value、width、height 等会覆盖默认值,也可以带 class、style。
代码
import { createSignal } from 'solid-js';
import { Sidebar } from '@fibrous/semi-ai-solid';
import { Radio, RadioGroup } from '@fibrous/semi-ui-solid';
const { CodeItem } = Sidebar;
const code = `.semi-animation-react-demo-auto {
button {
height: 50px;
border: 0;
cursor: pointer !important;
background: #777;
color: white;
outline: none;
-webkit-appearance: none;
}
button:hover {
background: #878787;
}
.auto-main {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto 1fr;
background: #575757;
}
.content {
grid-column: span 3;
}
.item {
background: indianred;
width: 100%;
overflow: hidden;
color: white;
}
.item p {
margin: 0;
padding: 10px;
}
}
`;
const json = `{
"axisX": {
"title": {
"visible": false,
"position": "center"
},
"label": {
"visible": true,
"style": {
"fontSize": 12,
"fontWeight": 400,
"lineHeight": 16,
"fontFamily": [
"Inter"
],
"fill": "rgba(0, 0, 0, 0.47843137254901963)"
},
"space": 12
}
}
}`;
const codeProps = {
content: code,
language: 'css',
};
const jsonProps = {
isJson: true,
content: json,
};
export default function SidebarDemo() {
const [type, setType] = createSignal('json');
return (
<>
<RadioGroup type="button" buttonSize="middle" value={type()} onChange={(e) => setType(e.target.value)} style={{ 'margin-bottom': '10px' }}>
<Radio value="json">JSON</Radio>
<Radio value="css">CSS</Radio>
</RadioGroup>
<div style={{ height: '200px', overflow: 'auto' }}>
<CodeItem {...(type() === 'json' ? jsonProps : codeProps)} />
</div>
</>
);
}代码列表
用户可通过 Sidebar 中的 CodeContent 组件展示代码列表信息。
activeKey配合onChange管理当前展开的面板onExpand监听面板头部的展开按钮,设置后才显示展开按钮
代码
import { createSignal } from 'solid-js';
import { Sidebar } from '@fibrous/semi-ai-solid';
const { CodeContent } = Sidebar;
const defaultCodes = [
{
name: 'Component.js',
key: 'code1',
language: 'javascript',
content: `import React from 'react';
import { AutoComplete } from '@fibrous/semi-ui-react';
import { IconSearch } from '@fibrous/semi-icons-react';
() => {
const [stringData, setStringData] = useState([]);
const [value, setValue] = useState('');
const handleStringSearch = (value) => {
let result;
if (value) {
result = ['gmail.com', '163.com', 'qq.com'].map(domain => value+domain);
} else {
result = [];
}
setStringData(result);
};
const handleChange = (value) => {
console.log('onChange', value);
setValue(value);
};
return (
<AutoComplete
data={stringData}
value={value}
showClear
prefix={<IconSearch />}
placeholder="搜索... "
onSearch={handleStringSearch}
onChange={handleChange}
style={{ width: 200 }}
/>
);
};
`,
},
{
name: 'Style.css',
key: 'code2',
language: 'css',
content: `.semi-animation-react-demo-auto {
button {
height: 50px;
border: 0;
cursor: pointer !important;
background: #777;
color: white;
outline: none;
-webkit-appearance: none;
}
button:hover {
background: #878787;
}
.auto-main {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto 1fr;
background: #575757;
}
.content {
grid-column: span 3;
}
.item {
background: indianred;
width: 100%;
overflow: hidden;
color: white;
}
.item p {
margin: 0;
padding: 10px;
}
}
`,
},
{
name: 'Chart.json',
key: 'code3',
isJson: true,
language: 'html',
content: `{
"axisX": {
"title": {
"visible": false,
"position": "center"
},
"label": {
"visible": true,
"style": {
"fontSize": 12,
"fontWeight": 400,
"lineHeight": 16,
"fontFamily": [
"Inter"
],
"fill": "rgba(0, 0, 0, 0.47843137254901963)"
},
"space": 12
},
"domainLine": {
"visible": true,
"style": {
"lineWidth": 1,
"stroke": "rgba(0, 0, 0, 0.12156862745098039)",
"lineDash": []
}
},
"tick": {
"visible": false,
"style": {
"lineWidth": 1,
"stroke": "rgba(255, 255, 255, 0)"
}
},
"subTick": {
"visible": false
},
"grid": {
"visible": false
},
"subGrid": {
"visible": false
}
}
}`,
},
];
export default function SidebarDemo() {
const [activeKey, setActiveKey] = createSignal<string | string[]>('code1');
return (
<div class="semi-sidebar-main">
<CodeContent style={{ width: '600px' }} activeKey={activeKey()} codes={defaultCodes} onChange={setActiveKey} />
</div>
);
}富文本编辑器
可通过 Sidebar 中的 FileItem 查看、编辑富文本内容, FileItem 基于 tiptap 实现。
content:富文本 HTML。挂载后变化会同步到编辑器,与编辑器当前内容相同时不会重置,不会打断输入也不会丢失光标,同步不触发onContentChangeeditable: 设置是否可编辑,挂载后变化会同步到编辑器imgUploadProps: 设置图片上传路径以及图片上传后 src 的设置,通过action设置上传地址,通过getUploadImageSrc(response)函数根据上传响应返回图片上传后地址(未提供时,响应是字符串则直接作为地址,否则取response.src),用于富文本中 img 节点的 src;Upload 的on开头回调(如onSuccess)也可在这里传入。只有设置了action或customRequest,工具栏才显示图片按钮extensions与imgUploadProps只在创建编辑器时读取,要换用新值时重新挂载组件,例如用<Show when={版本} keyed>包裹onContentChange在编辑后触发,参数为 HTML 字符串
代码
import { createSignal } from 'solid-js';
import { Sidebar, type ImageUploadNodeOptions } from '@fibrous/semi-ai-solid';
import { Button } from '@fibrous/semi-ui-solid';
const { FileItem } = Sidebar;
const defaultFileContent = `<h2>
Semi Design 介绍
</h2>
<p>
Semi Design 是由 <strong>抖音前端团队</strong>和MED产品设计团队设计、开发并维护的<em>设计系统</em>。作为一个全面、易用、优质的现代应用UI解决方案,Semi Design从字节跳动各业务线的复杂场景中提炼而来,目前已经支撑了近千个平台产品,服务了内外部超过10万用户,详情见https://semi.design/zh-CN/start/introduction。Semi Design的特点包括:
</p>
<ul>
<li>
设计简洁、现代化。
</li>
<li>
提供主题方案,可深度样式定制。
</li>
</ul>
<p>
国际化,覆盖了简/繁体中文、英语、日语、韩语、葡萄牙语等20+种语言,日期时间组件提供全球时区支持,全部组件可自动适配阿拉伯文RTL布局。
</p>
<pre><code class="language-css">body {
display: none;
}</code></pre>
<p>
采用 Foundation 和 Adapter 跨框架技术方案,方便扩展。
</p>
<blockquote>
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统
<br />
— Semi Design
</blockquote>`;
const imgUploadProps: ImageUploadNodeOptions = {
action: 'https://api.semi.design/upload',
getUploadImageSrc: () => {
// response is the action's result; real code reads the uploaded image URL from it
return '/assets/placeholder/avatar-a.svg';
},
};
export default function SidebarDemo() {
const [editable, setEditable] = createSignal(true);
return (
<>
<Button aria-pressed={editable()} onClick={() => setEditable((current) => !current)}>
可编辑
</Button>
<br />
<br />
<FileItem name="Semi Design 介绍" content={defaultFileContent} editable={editable()} style={{ border: '1px solid var(--semi-color-border)', padding: '12px' }} imgUploadProps={imgUploadProps} />
</>
);
}富文本列表
用户可通过 Sidebar 中的 FileContent 组件展示富文本列表信息。
activeKey配合onChange管理当前展开的面板onExpand监听面板头部的展开按钮,设置后才显示展开按钮
代码
import { createSignal } from 'solid-js';
import { Sidebar } from '@fibrous/semi-ai-solid';
const { FileContent } = Sidebar;
const defaultFiles = [
{
key: 'file1',
name: 'Semi Design 介绍',
content: `
<h2>
Semi Design 介绍
</h2>
<p>
Semi Design 是由 <strong>抖音前端团队</strong>和MED产品设计团队设计、开发并维护的<em>设计系统</em>。作为一个全面、易用、优质的现代应用UI解决方案,Semi Design从字节跳动各业务线的复杂场景中提炼而来,目前已经支撑了近千个平台产品,服务了内外部超过10万用户,详情见https://semi.design/zh-CN/start/introduction。Semi Design的特点包括:
</p>
<ul>
<li>
设计简洁、现代化。
</li>
<li>
提供主题方案,可深度样式定制。
</li>
</ul>
<p>
国际化,覆盖了简/繁体中文、英语、日语、韩语、葡萄牙语等20+种语言,日期时间组件提供全球时区支持,全部组件可自动适配阿拉伯文RTL布局。
</p>
<pre><code class="language-css">body {
display: none;
}</code></pre>
<p>
采用 Foundation 和 Adapter 跨框架技术方案,方便扩展。
</p>
<blockquote>
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统
<br />
— Semi Design
</blockquote>
<p>
Semi Design 是由 <strong>抖音前端团队</strong>和MED产品设计团队设计、开发并维护的<em>设计系统</em>。作为一个全面、易用、优质的现代应用UI解决方案,Semi Design从字节跳动各业务线的复杂场景中提炼而来,目前已经支撑了近千个平台产品,服务了内外部超过10万用户,详情见https://semi.design/zh-CN/start/introduction。Semi Design的特点包括:
</p>
<ul>
<li>
设计简洁、现代化。
</li>
<li>
提供主题方案,可深度样式定制。
</li>
</ul>
<p>
国际化,覆盖了简/繁体中文、英语、日语、韩语、葡萄牙语等20+种语言,日期时间组件提供全球时区支持,全部组件可自动适配阿拉伯文RTL布局。
</p>
<pre><code class="language-css">body {
display: none;
}</code></pre>
<p>
采用 Foundation 和 Adapter 跨框架技术方案,方便扩展。
</p>
<blockquote>
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统
<br />
— Semi Design
</blockquote>
<p>
Semi Design 是由 <strong>抖音前端团队</strong>和MED产品设计团队设计、开发并维护的<em>设计系统</em>。作为一个全面、易用、优质的现代应用UI解决方案,Semi Design从字节跳动各业务线的复杂场景中提炼而来,目前已经支撑了近千个平台产品,服务了内外部超过10万用户,详情见https://semi.design/zh-CN/start/introduction。Semi Design的特点包括:
</p>
<ul>
<li>
设计简洁、现代化。
</li>
<li>
提供主题方案,可深度样式定制。
</li>
</ul>
<p>
国际化,覆盖了简/繁体中文、英语、日语、韩语、葡萄牙语等20+种语言,日期时间组件提供全球时区支持,全部组件可自动适配阿拉伯文RTL布局。
</p>
<pre><code class="language-css">body {
display: none;
}</code></pre>
<p>
采用 Foundation 和 Adapter 跨框架技术方案,方便扩展。
</p>
<blockquote>
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统
<br />
— Semi Design
</blockquote>
`,
},
{
key: 'file2',
name: 'Semi Design DSM',
content: `<h2>
Semi Design DSM
</h2>
<p>Semi DSM is a design system management tool provided by Semi Design. It supports global and component-level style customization and keeps synchronization between Figma and front-end code. Suitable for teams of all sizes. Whether you need to simplify workflow, improve team collaboration, or increase productivity, we have features suitable for you.</p>
`,
},
{
key: 'file3',
name: 'React v19 Adaptation',
content: `<h2>
React v19 Adaptation
</h2>
<p>Since the release of React v19, React has introduced numerous underlying mechanism and API changes, including upgrades and adjustments to the render mechanism, ref, context, TypeScript types, and related deprecated APIs. To ensure that the Semi Design component library is smoothly compatible with both React v19 and lower versions, we provide the original component package @fibrous/semi-ui-react for React versions below v19, as well as a new package @fibrous/semi-ui-19 specifically adapted for React v19, so users can choose as needed. This guide will help you understand how to install, use, and the precautions to take.
Installation & Usage
If your project is using React v19, please use @fibrous/semi-ui-19. For React versions below v19, continue using @fibrous/semi-ui-react as before.</p>
`,
},
];
export default function SidebarDemo() {
const [activeKey, setActiveKey] = createSignal<string | string[]>('file1');
return (
<div class="semi-sidebar-main">
<FileContent style={{ width: '600px' }} activeKey={activeKey()} files={defaultFiles} onChange={setActiveKey} />
</div>
);
}侧边信息栏
侧边信息栏 Sidebar 有主视图(mode 为 main)和详情视图(mode 为 code、file 及其他)。
主视图可以通过 title 配置标题,通过 showClose 决定是否展示关闭按钮,通过 options 设置顶部的按钮组(每项为 { name, key }),activeKey 配合 onActiveOptionChange 管理当前选中的项。按钮图标使用 renderOptionIcon 渲染,需要替换整个按钮时使用 renderOptionItem,其第二个参数 onChange 用于选中该项。
对于主视图的内容渲染,可通过 renderMainContent 传入渲染函数,参数为当前选中的 activeKey。可使用 Annotation 中的 AnnotationContent 渲染参考来源,使用 Sidebar 中内置的 FileContent 渲染富文本, CodeContent 渲染代码。渲染函数会随其中读取的信号重新执行,面板状态等信号建议只在传给子组件的属性中读取,避免内容整体重建。
对于详情视图,如果是富文本或者代码,分别设置 mode 为 code、file,通过 detailContent 设置显示内容,即可通过内置的代码显示组件, 富文本编辑器进行显示;detailContent 的 key 变化时会重新挂载。如果想要自定义详情视图的展示,则通过 renderDetailContent 自行渲染,详情头部通过 renderDetailHeader 自定义,两者返回内容为空时回退到内置内容。点击详情头部的返回按钮触发 onBackWard。
代码
import { type Component, type JSX, Match, Switch, createSignal } from 'solid-js';
import { Dynamic } from 'solid-js/web';
import { Annotation, Sidebar, type AnnotationInfo, type ImageUploadNodeOptions, type SideBarDetailContent, type SideBarOption } from '@fibrous/semi-ai-solid';
import { IconBriefStroked, IconCodeStroked, IconModalStroked, IconSearch } from '@fibrous/semi-icons-solid';
const { FileContent, CodeContent } = Sidebar;
const { AnnotationContent } = Annotation;
const containerStyle: JSX.CSSProperties = {
display: 'flex',
height: '800px',
border: '1px solid var(--semi-color-border)',
'border-radius': '8px',
overflow: 'hidden',
'box-sizing': 'border-box',
};
const optionList: SideBarOption[] = [
{ name: '查看搜索', key: 'searchResult' },
{ name: '文件预览', key: 'filePreview' },
{ name: '代码预览', key: 'codePreview' },
{ name: '浏览器', key: 'network' },
];
const optionIcons: Record<string, Component> = {
searchResult: IconSearch,
filePreview: IconBriefStroked,
codePreview: IconCodeStroked,
network: IconModalStroked,
};
const defaultCodes = [
{
name: 'Component.js',
key: 'code1',
language: 'javascript',
content: `import React from 'react';
import { AutoComplete } from '@fibrous/semi-ui-react';
import { IconSearch } from '@fibrous/semi-icons-react';
() => {
const [stringData, setStringData] = useState([]);
const [value, setValue] = useState('');
const handleStringSearch = (value) => {
let result;
if (value) {
result = ['gmail.com', '163.com', 'qq.com'].map(domain => value+domain);
} else {
result = [];
}
setStringData(result);
};
const handleChange = (value) => {
console.log('onChange', value);
setValue(value);
};
return (
<AutoComplete
data={stringData}
value={value}
showClear
prefix={<IconSearch />}
placeholder="搜索... "
onSearch={handleStringSearch}
onChange={handleChange}
style={{ width: 200 }}
/>
);
};
`,
},
{
name: 'Style.css',
key: 'code2',
language: 'css',
content: `.semi-animation-react-demo-auto {
button {
height: 50px;
border: 0;
cursor: pointer !important;
background: #777;
color: white;
outline: none;
-webkit-appearance: none;
}
button:hover {
background: #878787;
}
.auto-main {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto 1fr;
background: #575757;
}
.content {
grid-column: span 3;
}
.item {
background: indianred;
width: 100%;
overflow: hidden;
color: white;
}
.item p {
margin: 0;
padding: 10px;
}
}
`,
},
{
name: 'Chart.json',
key: 'code3',
isJson: true,
language: 'html',
content: `{
"axisX": {
"title": {
"visible": false,
"position": "center"
},
"label": {
"visible": true,
"style": {
"fontSize": 12,
"fontWeight": 400,
"lineHeight": 16,
"fontFamily": [
"Inter"
],
"fill": "rgba(0, 0, 0, 0.47843137254901963)"
},
"space": 12
},
"domainLine": {
"visible": true,
"style": {
"lineWidth": 1,
"stroke": "rgba(0, 0, 0, 0.12156862745098039)",
"lineDash": []
}
},
"tick": {
"visible": false,
"style": {
"lineWidth": 1,
"stroke": "rgba(255, 255, 255, 0)"
}
},
"subTick": {
"visible": false
},
"grid": {
"visible": false
},
"subGrid": {
"visible": false
}
}
}`,
},
];
const defaultFiles = [
{
key: 'file1',
name: 'Semi Design 介绍',
content: `
<h2>
Semi Design 介绍
</h2>
<p>
Semi Design 是由 <strong>抖音前端团队</strong>和MED产品设计团队设计、开发并维护的<em>设计系统</em>。作为一个全面、易用、优质的现代应用UI解决方案,Semi Design从字节跳动各业务线的复杂场景中提炼而来,目前已经支撑了近千个平台产品,服务了内外部超过10万用户,详情见https://semi.design/zh-CN/start/introduction。Semi Design的特点包括:
</p>
<ul>
<li>
设计简洁、现代化。
</li>
<li>
提供主题方案,可深度样式定制。
</li>
</ul>
<p>
国际化,覆盖了简/繁体中文、英语、日语、韩语、葡萄牙语等20+种语言,日期时间组件提供全球时区支持,全部组件可自动适配阿拉伯文RTL布局。
</p>
<pre><code class="language-css">body {
display: none;
}</code></pre>
<p>
采用 Foundation 和 Adapter 跨框架技术方案,方便扩展。
</p>
<blockquote>
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统
<br />
— Semi Design
</blockquote>
<p>
Semi Design 是由 <strong>抖音前端团队</strong>和MED产品设计团队设计、开发并维护的<em>设计系统</em>。作为一个全面、易用、优质的现代应用UI解决方案,Semi Design从字节跳动各业务线的复杂场景中提炼而来,目前已经支撑了近千个平台产品,服务了内外部超过10万用户,详情见https://semi.design/zh-CN/start/introduction。Semi Design的特点包括:
</p>
<ul>
<li>
设计简洁、现代化。
</li>
<li>
提供主题方案,可深度样式定制。
</li>
</ul>
<p>
国际化,覆盖了简/繁体中文、英语、日语、韩语、葡萄牙语等20+种语言,日期时间组件提供全球时区支持,全部组件可自动适配阿拉伯文RTL布局。
</p>
<pre><code class="language-css">body {
display: none;
}</code></pre>
<p>
采用 Foundation 和 Adapter 跨框架技术方案,方便扩展。
</p>
<blockquote>
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统
<br />
— Semi Design
</blockquote>
<p>
Semi Design 是由 <strong>抖音前端团队</strong>和MED产品设计团队设计、开发并维护的<em>设计系统</em>。作为一个全面、易用、优质的现代应用UI解决方案,Semi Design从字节跳动各业务线的复杂场景中提炼而来,目前已经支撑了近千个平台产品,服务了内外部超过10万用户,详情见https://semi.design/zh-CN/start/introduction。Semi Design的特点包括:
</p>
<ul>
<li>
设计简洁、现代化。
</li>
<li>
提供主题方案,可深度样式定制。
</li>
</ul>
<p>
国际化,覆盖了简/繁体中文、英语、日语、韩语、葡萄牙语等20+种语言,日期时间组件提供全球时区支持,全部组件可自动适配阿拉伯文RTL布局。
</p>
<pre><code class="language-css">body {
display: none;
}</code></pre>
<p>
采用 Foundation 和 Adapter 跨框架技术方案,方便扩展。
</p>
<blockquote>
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统
<br />
— Semi Design
</blockquote>
`,
},
{
key: 'file2',
name: 'Semi Design DSM',
content: `<h2>
Semi Design DSM
</h2>
<p>Semi DSM is a design system management tool provided by Semi Design. It supports global and component-level style customization and keeps synchronization between Figma and front-end code. Suitable for teams of all sizes. Whether you need to simplify workflow, improve team collaboration, or increase productivity, we have features suitable for you.</p>
`,
},
{
key: 'file3',
name: 'React v19 Adaptation',
content: `<h2>
React v19 Adaptation
</h2>
<p>Since the release of React v19, React has introduced numerous underlying mechanism and API changes, including upgrades and adjustments to the render mechanism, ref, context, TypeScript types, and related deprecated APIs. To ensure that the Semi Design component library is smoothly compatible with both React v19 and lower versions, we provide the original component package @fibrous/semi-ui-react for React versions below v19, as well as a new package @fibrous/semi-ui-19 specifically adapted for React v19, so users can choose as needed. This guide will help you understand how to install, use, and the precautions to take.
Installation & Usage
If your project is using React v19, please use @fibrous/semi-ui-19. For React versions below v19, continue using @fibrous/semi-ui-react as before.</p>
`,
},
];
const defaultInfoList: AnnotationInfo[] = [
{
header: 'Semi design introduction',
key: '1',
annotations: [
{
order: 1,
type: 'video',
duration: 4432,
title: 'Semi Design is a design system designed',
url: 'https://semi.design/',
logo: '/assets/placeholder/avatar-a.svg',
siteName: 'Semi Design',
detail: ' As a comprehensive, easy-to-use, and high-quality modern enterprise-level application UI solution',
img: '/assets/root-web-sites/abstract.jpg',
},
{
order: 2,
title: 'Quick start',
type: 'video',
duration: 56,
url: 'https://semi.design/',
detail: ' As a comprehensive, easy-to-use, and high-quality modern enterprise-level application UI solution',
logo: '/assets/placeholder/avatar-a.svg',
siteName: 'Semi Design',
img: '/assets/root-web-sites/abstract.jpg',
},
{
order: 3,
title: 'Use components in a modular way',
url: 'https://semi.design/',
detail: `Semi provides esm format dist, and the css of the component is only imported by the corresponding js.
When used in Webpack, Rspack, create-react-app or Vite projects, there is no need to configure any compilation items.`,
logo: '/assets/placeholder/avatar-a.svg',
siteName: 'Semi Design',
img: '/assets/root-web-sites/abstract.jpg',
},
],
},
{
header: 'Design resource',
key: '2',
annotations: [
{
order: 2,
title: 'Semi Design resource',
url: 'https://semi.design/',
detail: 'Semi Design provides a wealth of design resources to help designers and developers collaborate efficiently. Whether you are a community user or a ByteDance internal designer, you can find UI Kit resource and Figma plug-ins that suit you here.',
logo: '/assets/placeholder/avatar-a.svg',
siteName: 'Semi Design resource',
img: '/assets/root-web-sites/abstract.jpg',
},
],
},
];
const imgUploadProps: ImageUploadNodeOptions = {
action: 'https://api.semi.design/upload',
getUploadImageSrc: () => {
// response is the action's result; real code reads the uploaded image URL from it
return '/assets/placeholder/avatar-a.svg';
},
};
export default function SidebarDemo() {
const [mode, setMode] = createSignal('main');
const [activeKey, setActiveKey] = createSignal('codePreview');
const [activeCodeKey, setActiveCodeKey] = createSignal<string | string[]>('code1');
const [activeFileKey, setActiveFileKey] = createSignal<string | string[]>('file1');
const [activeReferKey, setActiveReferKey] = createSignal<string | string[]>('1');
const [currentDetail, setCurrentDetail] = createSignal<SideBarDetailContent>();
const onExpand = (_e: MouseEvent, content: SideBarDetailContent, mode: string) => {
setMode(mode);
setCurrentDetail(content);
};
return (
<div style={containerStyle}>
<div style={{ 'flex-grow': 1, 'flex-shrink': 1, padding: '20px' }}>工作区占位</div>
<Sidebar
showClose={false}
visible
motion={false}
title="Agent 的工作空间"
mode={mode()}
defaultSize={{ width: '60%' }}
onActiveOptionChange={(_e, key) => setActiveKey(key)}
activeKey={activeKey()}
options={optionList}
renderOptionIcon={(option) => <Dynamic component={optionIcons[option.key]} />}
onBackWard={(_e, next) => {
setMode(next);
}}
renderMainContent={(key) => (
<Switch>
<Match when={key === 'searchResult'}>
<AnnotationContent activeKey={activeReferKey()} info={defaultInfoList} onChange={setActiveReferKey} />
</Match>
<Match when={key === 'filePreview'}>
<FileContent activeKey={activeFileKey()} files={defaultFiles} onExpand={onExpand} onChange={setActiveFileKey} />
</Match>
<Match when={key === 'codePreview'}>
<CodeContent activeKey={activeCodeKey()} codes={defaultCodes} onExpand={onExpand} onChange={setActiveCodeKey} />
</Match>
<Match when={key === 'network'}>
<img alt="network" src="/assets/root-web-sites/test.jpg" style={{ width: '100%' }} />
</Match>
</Switch>
)}
detailContent={currentDetail()}
imgUploadProps={imgUploadProps}
/>
</div>
);
}API 参考
Sidebar、Annotation、MCPConfigure 与 Container 的 class、style、ref 和其他未声明的属性(如 aria-label、id、data-*)落在 .semi-sidebar-container 元素上;根元素被命名(aria-label 或 aria-labelledby)且没有指定 role 时,role 为 group。CodeContent、FileContent、AnnotationContent 的这些属性落在各自的根元素(折叠面板)上,CodeItem 与 FileItem 的落在各自的根元素 div 上。
Container
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| afterVisibleChange | 可见状态变化完成后的回调,参数为当前是否可见;有动画时在动画结束后触发。 | Type(isVisible: boolean) => void | - |
| children | 容器的内容。 | JSX.Element | - |
| closeOnEsc | 按下 Esc 键时是否触发 onCancel。 | boolean | - |
| containerRef | 接收 .semi-sidebar-container 元素,作用同 ref。 | TypeRef<HTMLDivElement> | - |
| defaultSize | 初始尺寸(宽度与高度),仅在 resizable 为 true 时生效。 | { width?: number | string; height?: number | string } | - |
| maxWidth | 可调整的最大宽度,仅在 resizable 为 true 时生效。 | string | number | - |
| minWidth | 可调整的最小宽度,仅在 resizable 为 true 时生效。 | string | number | 150 |
| motion | 显示与隐藏时是否使用展开动画。 | boolean | true |
| onCancel | 点击关闭按钮,或在 closeOnEsc 为 true 时按下 Esc 触发。 | Type(e: MouseEvent | KeyboardEvent) => void | - |
| renderHeader | 自定义整个头部;返回内容为空时使用内置头部。 | Type() => JSX.Element | - |
| resizable | 宽度是否可拖拽调整;为 true 时 minWidth、maxWidth 与 defaultSize 生效。 | boolean | true |
| showClose | 是否显示头部的关闭按钮。 | boolean | true |
| style | 容器元素的内联样式,可传样式对象或样式字符串。 | JSX.CSSProperties | string | - |
| title | 头部标题,可传任意内容。 | JSX.Element | - |
| visible | 是否显示。 | boolean | - |
MCPConfigure
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| afterVisibleChange | 可见状态变化完成后的回调,参数为当前是否可见;有动画时在动画结束后触发。 | Type(isVisible: boolean) => void | - |
| children | 不生效,内容由 MCP 工具列表渲染。 | JSX.Element | - |
| closeOnEsc | 按下 Esc 键时是否触发 onCancel。 | boolean | - |
| containerRef | 接收 .semi-sidebar-container 元素,作用同 ref。 | TypeRef<HTMLDivElement> | - |
| customOptions | 自定义 MCP 工具列表,在自定义页签中展示。 | MCPSolidOption[] | - |
| defaultSize | 初始尺寸(宽度与高度),仅在 resizable 为 true 时生效。 | { width?: number | string; height?: number | string } | - |
| filter | 自定义搜索过滤,参数为转为小写的输入值和选项;默认按 label 与 desc 匹配。 | Type(inputValue: string, option: MCPSolidOption) => boolean | - |
| maxWidth | 可调整的最大宽度,仅在 resizable 为 true 时生效。 | string | number | - |
| minWidth | 可调整的最小宽度,仅在 resizable 为 true 时生效,默认 150。 | string | number | - |
| motion | 显示与隐藏时是否使用展开动画,默认 true。 | boolean | - |
| onAddClick | 点击自定义页签中的新增按钮时触发。 | Type(e: MouseEvent) => void | - |
| onCancel | 点击关闭按钮,或在 closeOnEsc 为 true 时按下 Esc 触发。 | Type(e: MouseEvent | KeyboardEvent) => void | - |
| onConfigureClick | 点击内置 MCP 工具的配置按钮时触发,参数为事件和该选项。 | Type(e: MouseEvent, option: MCPSolidOption) => void | - |
| onEditClick | 点击自定义 MCP 工具的编辑按钮时触发,参数为事件和该选项。 | Type(e: MouseEvent, option: MCPSolidOption) => void | - |
| onSearch | 搜索时触发,参数为输入值和是否在自定义列表中搜索。 | Type(inputValue: string, custom: boolean) => void | - |
| onStatusChange | 切换启用或关闭时触发,参数为切换后的完整选项列表和是否为自定义列表;需自行把新列表写回 options 或 customOptions。 | Type(options: MCPSolidOption[], custom: boolean) => void | - |
| options | 内置 MCP 工具列表;每项的 value 必填且唯一。 | MCPSolidOption[] | - |
| placeholder | 搜索框的占位文字,默认使用语言包文案。 | string | - |
| renderDesc | 渲染工具项的描述,参数为 { option, custom };不传时显示选项的 desc 文本。 | Type(props: MCPRenderItemProps) => JSX.Element | - |
| renderHeader | 自定义整个头部;返回内容为空时使用内置头部。 | Type() => JSX.Element | - |
| renderIcon | 渲染工具项的图标,参数为 { option, custom };不传时选项的 icon 作为图片地址渲染。 | Type(props: MCPRenderItemProps) => JSX.Element | - |
| renderItem | 自定义整行工具项,参数为 { option, custom }。 | Type(props: MCPRenderItemProps) => JSX.Element | - |
| resizable | 宽度是否可拖拽调整,默认 true;为 true 时 minWidth、maxWidth 与 defaultSize 生效。 | boolean | - |
| showClose | 是否显示头部的关闭按钮,默认 true。 | boolean | - |
| style | 容器元素的内联样式,可传样式对象或样式字符串。 | JSX.CSSProperties | string | - |
| title | 头部标题,默认使用语言包中的 MCP 配置。 | JSX.Element | - |
| visible | 是否显示。 | boolean | - |
MCPConfigureOption
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| active | 是否处于激活状态 | boolean | false |
| configure | 是否显示配置按钮 | boolean | false |
| desc | 描述文本,内置过滤会匹配它;其他内容使用 renderDesc | string | - |
| disabled | 是否禁用,为 true 时用户无法更改激活状态 | boolean | false |
| icon | 图标的图片地址;其他内容使用 renderIcon | string | - |
| label | 标签文本 | string | - |
| value | 对应的值,必填。在 onStatusChange 中标识选项,也作为列表项的 key,同一列表内需唯一 | string | - |
Annotation
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| activeKey | 当前展开的分组 key,支持单个或多个。 | string | string[] | - |
| afterVisibleChange | 可见状态变化完成后的回调,参数为当前是否可见;有动画时在动画结束后触发。 | Type(isVisible: boolean) => void | - |
| children | 不生效,内容由参考来源列表渲染。 | JSX.Element | - |
| closeOnEsc | 按下 Esc 键时是否触发 onCancel。 | boolean | - |
| containerRef | 接收 .semi-sidebar-container 元素,作用同 ref。 | TypeRef<HTMLDivElement> | - |
| defaultSize | 初始尺寸(宽度与高度),仅在 resizable 为 true 时生效。 | { width?: number | string; height?: number | string } | - |
| info | 参考来源分组列表,每组包含 header、key 与 annotations。 | AnnotationInfo[] | - |
| maxWidth | 可调整的最大宽度,仅在 resizable 为 true 时生效。 | string | number | - |
| minWidth | 可调整的最小宽度,仅在 resizable 为 true 时生效,默认 150。 | string | number | - |
| motion | 显示与隐藏时是否使用展开动画,默认 true。 | boolean | - |
| onCancel | 点击关闭按钮,或在 closeOnEsc 为 true 时按下 Esc 触发。 | Type(e: MouseEvent | KeyboardEvent) => void | - |
| onChange | 展开的分组变化时触发,参数为当前展开的 key。 | Type(key: string | string[]) => void | - |
| onClick | 点击某条参考来源时触发,参数为事件和该条目。 | Type(e: MouseEvent, item: AnnotationItem) => void | - |
| renderHeader | 自定义整个头部;返回内容为空时使用内置头部。 | Type() => JSX.Element | - |
| renderInfoHeader | 渲染分组标题图标后的内容,参数为该分组;不传时显示分组的 header 文本。即 AnnotationContent 的 renderHeader,与容器的 renderHeader 区分。 | ContentProps['renderHeader'] | - |
| renderItem | 自定义参考来源条目的渲染,参数为该条目。 | Type(annotation: AnnotationItem) => JSX.Element | - |
| resizable | 宽度是否可拖拽调整,默认 true;为 true 时 minWidth、maxWidth 与 defaultSize 生效。 | boolean | - |
| showClose | 是否显示头部的关闭按钮,默认 true。 | boolean | - |
| style | 容器元素的内联样式,可传样式对象或样式字符串。 | JSX.CSSProperties | string | - |
| title | 头部标题,默认使用语言包中的参考来源。 | JSX.Element | - |
| visible | 是否显示。 | boolean | - |
AnnotationContent
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| activeKey | 当前展开的分组 key,支持单个或多个。 | string | string[] | - |
| info | 参考来源分组列表,每组包含 header、key 与 annotations。 | AnnotationInfo[] | - |
| onChange | 展开的分组变化时触发,参数为当前展开的 key。 | Type(key: string | string[]) => void | - |
| onClick | 点击某条参考来源时触发,参数为事件和该条目。 | Type(e: MouseEvent, item: AnnotationItem) => void | - |
| renderHeader | 渲染分组标题图标后的内容,参数为该分组;不传时显示分组的 header 文本。 | Type(info: AnnotationInfo) => JSX.Element | - |
| renderItem | 自定义参考来源条目的渲染,参数为该条目。 | Type(annotation: AnnotationItem) => JSX.Element | - |
AnnotationInfo
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| annotations | 该分组下的参考来源条目 | AnnotationItem[] | - |
| header | 分组标题的纯文本,也可用 renderInfoHeader(Annotation)或 renderHeader(AnnotationContent)自定义 | string | - |
| key | 分组的唯一标识 | string | - |
AnnotationItem
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| detail | 内容详情/补充说明(如视频简介、文本备注等) | string | - |
| duration | 时长(通常为视频时长,单位:秒) | number | - |
| img | 图片地址(如视频封面图、文本配图地址) | string | - |
| logo | 站点/内容所属平台的 logo 图片地址 | string | - |
| onClick | 点击该条目时触发,参数为事件和该条目(不含 onClick),优先于 Annotation 的 onClick | (event: MouseEvent, item: AnnotationItem) => void | - |
| order | 引用序号(用于内容排序/标注序号展示) | number | - |
| siteName | 内容所属的站点/平台名称 | string | - |
| title | 内容标题 | string | - |
| type | 内容类型 | 'video' | 'text' | - |
| url | 资源链接,在新标签页打开;没有 url 的条目渲染为无 href 的链接,只能用指针点击 | string | - |
Sidebar
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| activeKey | 主视图中当前选中的选项 key。 | string | - |
| afterVisibleChange | 可见状态变化完成后的回调,参数为当前是否可见;有动画时在动画结束后触发。 | Type(isVisible: boolean) => void | - |
| children | 不生效,内容由主视图或详情视图渲染。 | JSX.Element | - |
| closeOnEsc | 按下 Esc 键时是否触发 onCancel。 | boolean | - |
| containerRef | 不生效,需要容器元素时使用 ref。 | TypeRef<HTMLDivElement> | - |
| defaultSize | 初始尺寸(宽度与高度),仅在 resizable 为 true 时生效。 | { width?: number | string; height?: number | string } | - |
| detailContent | code 与 file 模式显示的条目,为 CodeItem 或 FileItem 的属性加 key;key 变化时重新挂载该条目。file 模式下其中的 editable、imgUploadProps 与 onContentChange 不生效,由 fileEditable、imgUploadProps 与 onFileContentChange 决定。 | DetailContent | - |
| fileEditable | file 模式的富文本是否可编辑。 | boolean | true |
| imgUploadProps | file 模式的图片上传配置,编辑器创建时读取,之后变化不生效。 | ImageUploadNodeOptions | - |
| maxWidth | 可调整的最大宽度,仅在 resizable 为 true 时生效。 | string | number | - |
| minWidth | 可调整的最小宽度,仅在 resizable 为 true 时生效,默认 150。 | string | number | - |
| mode | 展示模式:main 为主视图,code 与 file 使用内置详情,其他字符串通过 renderDetailContent 自行渲染。 | string | "main" |
| motion | 显示与隐藏时是否使用展开动画,默认 true。 | boolean | - |
| onActiveOptionChange | 点击选项按钮时触发,参数为事件和该选项的 key;需自行把 key 写回 activeKey。 | Type(e: MouseEvent, activeKey: string) => void | - |
| onBackWard | 点击详情头部的返回按钮时触发,参数为事件和 main;需自行把 mode 改回 main,返回值被忽略。 | Type(e: MouseEvent, mode: string) => void | Promise<unknown> | - |
| onCancel | 点击关闭按钮,或在 closeOnEsc 为 true 时按下 Esc 触发。 | Type(e: MouseEvent | KeyboardEvent) => void | - |
| onDetailContentCopy | 点击详情头部的复制按钮后触发,在写入剪贴板完成后异步调用,参数为事件、复制的内容和结果(写入被拒绝时为 false)。 | Type(e: MouseEvent, content: string, res: boolean) => void | - |
| onFileContentChange | file 模式下编辑富文本后触发,参数为 HTML 字符串。 | Type(content: string) => void | - |
| options | 主视图顶部的选项按钮,每项包含 name 与 key;图标由 renderOptionIcon 渲染。 | Option[] | - |
| renderDetailContent | 自定义详情内容,参数为当前模式;返回内容为空时,code 与 file 模式回退到内置详情。 | Type(mode: string) => JSX.Element | - |
| renderDetailHeader | 自定义详情头部,参数为当前模式和 detailContent;返回内容为空时使用内置头部。 | Type(mode: string, detailContent?: DetailContent) => JSX.Element | - |
| renderHeader | 不生效,主视图使用 title,详情视图使用 renderDetailHeader。 | Type() => JSX.Element | - |
| renderMainContent | 渲染主视图内容,参数为当前选中的 activeKey。 | Type(activeKey: string) => JSX.Element | - |
| renderOptionIcon | 渲染内置选项按钮的图标,参数为该选项。 | Type(option: Option) => JSX.Element | - |
| renderOptionItem | 自定义选项按钮,第二个参数 onChange 用于选中该选项。 | Type(option: Option, onChange: (e: MouseEvent, activeKey: string) => void) => JSX.Element | - |
| resizable | 宽度是否可拖拽调整,默认 true;为 true 时 minWidth、maxWidth 与 defaultSize 生效。 | boolean | - |
| showClose | 是否显示头部的关闭按钮,默认 true。 | boolean | - |
| style | 容器元素的内联样式,可传样式对象或样式字符串。 | JSX.CSSProperties | string | - |
| title | 主视图的标题;详情视图使用内置的详情头部。 | JSX.Element | - |
| visible | 是否显示。 | boolean | - |
SideBarOption
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| key | 选项的唯一标识 | string | - |
| name | 选项名称,图标使用 renderOptionIcon | string | - |
CodeContent
codes 的每一项为 CodeItem 的属性加用于标识面板的 key。
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| activeKey | 当前展开的代码面板 key,支持单个或多个。 | string | string[] | - |
| codes | 代码条目列表,每项为 CodeItem 的属性加用于标识面板的 key。 | CodeItemProps[] | - |
| onChange | 展开的面板变化时触发,参数为当前展开的 key。 | Type(activeKey: string | string[]) => void | - |
| onExpand | 点击面板头部的展开按钮时触发,参数为事件、该代码条目和 code;设置后才显示展开按钮。 | Type(e: MouseEvent, code: CodeItemProps, mode: string) => void | - |
CodeItem
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| codeHighlightProps | CodeHighlight 的属性,在 code 与 language 之后展开。 | TypePartial<CodeHighlightProps> | - |
| content | 代码/文本内容。 | string | - |
| isJson | 是否为 JSON 格式内容,为 true 时使用 JsonViewer 展示。 | boolean | - |
| jsonViewerProps | JsonViewer 的属性,在 value、width、height 等默认值之后展开。 | TypePartial<JsonViewerProps> | - |
| key | 面板的唯一标识,用于 CodeContent 的 activeKey,也决定 Sidebar 详情的重新挂载。 | string | - |
| language | 代码语言类型(如 js、ts、css 等),同 CodeHighlight 的 language。 | string | - |
| name | 名称,显示在面板头部与 Sidebar 详情头部。 | string | - |
class、style 与原生属性落在根元素 div 上。
FileContent
files 的每一项为 FileItem 的属性加用于标识面板的 key,以只读方式渲染,extensions 与 imgUploadProps 会传给其预览。
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| activeKey | 当前展开的文件面板 key,支持单个或多个。 | string | string[] | - |
| files | 文件条目列表,以只读方式渲染,每项为 FileItem 的属性加用于标识面板的 key。 | FileItemProps[] | - |
| onChange | 展开的面板变化时触发,参数为当前展开的 key。 | Type(activeKey: string | string[]) => void | - |
| onExpand | 点击面板头部的展开按钮时触发,参数为事件、该文件条目和 file;设置后才显示展开按钮。 | Type(e: MouseEvent, file: FileItemProps, mode: string) => void | - |
FileItem
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| content | 富文本 HTML,挂载后变化会同步到编辑器,不触发 onContentChange。 | string | - |
| editable | 是否可编辑,挂载后变化会同步到编辑器。 | boolean | true |
| extensions | 追加在内置扩展之后的 tiptap 扩展,编辑器创建时读取,之后变化不生效。 | Extensions | - |
| imgUploadProps | 图片上传配置,编辑器创建时读取,之后变化不生效。 | ImageUploadNodeOptions | - |
| key | 面板的唯一标识,用于 FileContent 的 activeKey,也决定 Sidebar 详情的重新挂载。 | string | - |
| name | 名称,显示在面板头部、Sidebar 详情头部,并作为编辑区的 aria-label。 | string | - |
| onContentChange | 编辑后触发,参数为 HTML 字符串。 | Type(content: string) => void | - |
class、style 与原生属性落在根元素 div 上。
