Semi Design SideSheet 滑动侧边栏完全指南:从基础用法到源码级原理

发布时间:2026/9/25 5:14:23
Semi Design SideSheet 滑动侧边栏完全指南:从基础用法到源码级原理 前端UI组件设计系统【免费下载链接】semi-designA modern, comprehensive, flexible design system and React UI library, AI-friendly built-in.Provide 3000 Design Tokens, easy to build your design system. Make Semi Design to Any Design.‍ Design to Code in one click项目地址https://gitcode.com/gh_mirrors/se/semi-design点击查看免费下载SideSheet 是 Semi Design 提供的滑动侧边栏组件可从屏幕右、左、上、下四个边沿滑出浮层面板通常用于承载二级操作页面如详情预览、表单编辑、资源包创建等避免离开当前页面上下文。本指南将以 content/show/sidesheet/index.md 为骨架完整覆盖从引入、基本用法、位置/尺寸控制、外部区域交互到指定容器渲染的全部实战场景并结合 semi-ui/sideSheet 与 semi-foundation/sideSheet 的源码实现深入解析 body 滚动锁定、Esc 键盘关闭、动画调度与 ARIA 语义等底层机制读完即可在项目中直接落地可复用的 SideSheet 方案。如何引入SideSheet 与 React 生态中的其它 Semi 组件一样直接从douyinfe/semi-ui按需导入即可无需额外安装插件或配置import { SideSheet } from douyinfe/semi-ui;从仓库的组件实现看SideSheet 是一个 class 组件packages/semi-ui/sideSheet/index.tsx它通过Portal将弹层渲染到 body 或指定容器中其底层逻辑由SideSheetFoundation驱动packages/semi-foundation/sideSheet/sideSheetFoundation.ts这种「React 组件 Foundation」的分层结构也是 Semi 系组件的通用组织方式。基本用法受控显隐与遮罩关闭SideSheet 的显隐完全受控通过visible属性控制面板是否展示通过onCancel接收关闭请求。默认从屏幕右侧滑出并展示半透明遮罩点击遮罩区域即可关闭maskClosable默认为true。import React, { useState } from react; import { SideSheet, Button } from douyinfe/semi-ui; () { const [visible, setVisible] useState(false); const change () { setVisible(!visible); }; return ( Button onClick{change}Open SideSheet/Button SideSheet title滑动侧边栏 visible{visible} onCancel{change} pThis is the content of a basic sidesheet./p pHere is more content.../p /SideSheet / ); };值得注意的是SideSheet 并不自动维护自身的可见状态——关闭按钮、点击遮罩、按 Esc 键这些操作都只是触发onCancel回调最终是否关闭完全由你在父组件中更新visible决定。在源码中handleCancel经由 Foundation 的notifyCancel直接调用props.onCancelsideSheetFoundation.ts而点击遮罩时SideSheetContent.onMaskClick还会做一次e.target e.currentTarget的精确判定确保只有点击遮罩本身而非面板内容才会触发关闭SideSheetContent.tsx。自定义滑出位置placement通过placement属性可以控制侧边栏从哪个边沿滑出支持top、bottom、left、right四个方向默认值为rightimport React, { useState } from react; import { SideSheet, RadioGroup, Radio, Button } from douyinfe/semi-ui; () { const [visible, setVisible] useState(false); const change () { setVisible(!visible); }; const [placement, setPlacement] useState(right); const changePlacement e { setPlacement(e.target.value); }; return ( RadioGroup onChange{changePlacement} value{placement} Radio value{right}right/Radio Radio value{left}left/Radio Radio value{top}top/Radio Radio value{bottom}bottom/Radio /RadioGroup br / br / Button onClick{change}Open SideSheet/Button SideSheet title滑动侧边栏 visible{visible} onCancel{change} placement{placement} pThis is the content of a basic sidesheet./p pHere is more content.../p /SideSheet / ); };从源码看合法取值定义在 constants.ts 的PLACEMENT: [top, right, bottom, left]中组件会据此生成semi-sidesheet-right、semi-sidesheet-top等定位类名index.tsx对应的 CSS 定位规则左右方向占满高度、上下方向占满宽度位于 sideSheet.scss。自定义尺寸size / width / heightsize属性提供三档预设尺寸small448px、medium684px、large920px默认small。该属性仅在placement为left或right时生效即面板作为纵向侧边栏时控制宽度。如果预设尺寸不满足需求还可以通过width属性直接指定宽度例如width{900}或width{800px}import React, { useState } from react; import { SideSheet, RadioGroup, Radio, Button } from douyinfe/semi-ui; () { const [visible, setVisible] useState(false); const change () { setVisible(!visible); }; const [size, setSize] useState(small); const changeSize e { setSize(e.target.value); }; return ( RadioGroup onChange{changeSize} value{size} Radio value{small}small/Radio Radio value{medium}medium/Radio Radio value{large}large/Radio /RadioGroup br / br / Button onClick{change}Open SideSheet/Button SideSheet title滑动侧边栏 visible{visible} onCancel{change} size{size} pThis is the content of a basic sidesheet./p pHere is more content.../p /SideSheet / ); };尺寸取值的底层映射同样在 constants.ts 中WIDTH: { small: 448, medium: 684, large: 920 }SCSS 中则分别生成semi-sidesheet-size-small / -medium / -large三个宽度类sideSheet.scss。组件在计算最终宽高时有一套明确的优先级逻辑index.tsx当placement为left/right纵向时使用width属性若未设置则回退到size预设高度固定为100%。当placement为top/bottom横向时宽度固定为100%高度使用height属性默认值为 400若未设置则使用 constants 中的HEIGHT: 448该默认值定义见 constants.ts。测试用例对这套逻辑做了完整验证在left/right下设置width: 413px时 inner 宽度为 413px、高度为 100%在top/bottom下设置height: 413px时 inner 高度为 413px、宽度为 100%sideSheet.test.js。允许操作外部区域mask 与 disableScroll默认情况下 SideSheet 带有遮罩mask默认true此时外部区域被遮挡且不可操作。将mask设为false即可去掉遮罩允许用户继续操作页面上被露出的外部区域import React, { useState } from react; import { SideSheet, TextArea, Button } from douyinfe/semi-ui; () { const [visible, setVisible] useState(false); const [value, setValue] useState(); return ( Button onClick{() setVisible(true)}Open SideSheet/Button TextArea placeholderPlease enter something onChange{value setValue(value)} style{{ marginTop: 12 }}/ SideSheet title可操作外部的侧边栏 visible{visible} onCancel{() setVisible(false)} mask{false} disableScroll{false} p这里是输入的内容/p p{value}/p /SideSheet / ); };当 SideSheet 是默认渲染在 body 中时即不传入 getPopupContainer 参数会在打开时自动给 body 添加 overflow: hidden 来禁止滚动。如果你希望外部区域依然可滚动可以将 disableScroll 设为 false。上面的示例中SideSheet 打开后你仍然可以操作底部的 TextArea 输入框并且由于同时设置了disableScroll{false}外部页面也保持可滚动状态。从源码看body 滚动锁定由组件 adapter 中的disabledBodyScroll/enabledBodyScroll实现index.tsx打开时若未传getPopupContainer且当前overflow不是hidden则给document.body设置overflow: hidden同时用width: calc(100% - {scrollBarWidth}px)补偿滚动条消失带来的页面抖动关闭时恢复原始overflow与宽度。disableScroll的开关判断则在 Foundation 的beforeShow/afterHide中完成sideSheetFoundation.ts。测试 disableScroll 用例 验证了默认打开时 body 为overflow: hidden、关闭后恢复以及disableScroll{false}时不锁定滚动的行为。另外需要注意当mask{false}时面板本身的阴影会变为var(--semi-shadow-elevated)以在无遮罩情况下保持层次感对应样式见 sideSheet.scss。渲染在指定容器getPopupContainer默认情况下 SideSheet 通过 Portal 渲染到document.body。通过getPopupContainer可以指定父级 DOM弹层将渲染至该 DOM 内部适用于需要在特定区域内嵌展示而非全屏浮层的场景import React, { useState } from react; import { SideSheet, Button } from douyinfe/semi-ui; () { const [visible, setVisible] useState(false); const [value, setValue] useState(); const getContainer () { return document.querySelector(.sidesheet-container); }; return ( div style{{ height: 320, overflow: hidden, position: relative, border: 1px solid var(--semi-color-border), borderRadius: 2, padding: 24, textAlign: center, background: var(--semi-color-fill-0), }} classNamesidesheet-container spanRender in this/span br / br / Button onClick{() setVisible(true)}Open SideSheet/Button SideSheet title渲染在指定容器内部 visible{visible} onCancel{() setVisible(false)} width{220} getPopupContainer{getContainer} pThis is the content of a basic sidesheet./p pHere is more content.../p /SideSheet /div ); };容器需要手动设置样式 overflow: hidden否则会导致动画溢出。源码层面的处理细节如下传入getPopupContainer后组件 wrapper 的定位方式由position: static替代index.tsx并额外生成semi-sidesheet-popup类SCSS 中将弹层定位改为position: absolutesideSheet.scss。API 文档中也强调自定义容器需设置position: relative这会改变浮层在 DOM 树中的位置但不会改变视图渲染位置。渲染到指定容器后组件不再自动锁定 body 滚动disabledBodyScroll/enabledBodyScroll均以!getPopupContainer为前提因此外部页面依然可以自由滚动。测试 getPopupContainer 用例 同时验证了「渲染进容器」与「不传时容器内无弹层、body 被锁定」两种行为。由于浮层被限制在容器内容器必须自行设置overflow: hidden否则面板滑出动画会溢出容器边界。自定义内容区域title / footer / closeIcon 与样式SideSheet 支持完全自定义头部、底部与内容区。title可以是任意 ReactNode不限于字符串footer用于定制底部操作区closeIcon可替换右上角关闭图标传null则隐藏关闭按钮headerStyle/bodyStyle分别控制头部与内容区样式。以下示例组合了 Form 表单、Banner 提示与自定义底部按钮模拟「创建资源包」的二级操作页场景import React, { useState } from react; import { SideSheet, Form, Button, Typography, Banner } from douyinfe/semi-ui; () { const [visible, setVisible] useState(false); const show () { setVisible(true); }; const handleCancel (e) { setVisible(false); }; const { DatePicker, Select, Radio, RadioGroup, } Form; const footer ( div style{{ display: flex, justifyContent: flex-end }} Button style{{ marginRight: 8 }}重置/Button Button themesolid提交/Button /div ); return ( Button onClick{show}More Information/Button SideSheet title{Typography.Title heading{4}创建资源包/Typography.Title} headerStyle{{ borderBottom: 1px solid var(--semi-color-border) }} bodyStyle{{ borderBottom: 1px solid var(--semi-color-border) }} visible{visible} footer{footer} closeIcon{null} onCancel{handleCancel} Form DatePicker fielddate typedateTime initValue{new Date()} style{{ width: 272 }} label{{ text: 创建时间, required: true }} / RadioGroup fieldtype label目标操作系统 directionhorizontal initValue{all} Radio valueall全平台/Radio Radio valueiosiOS/Radio Radio valueandroidAndroid/Radio Radio valuewebWeb/Radio /RadioGroup RadioGroup fieldorigin label资源包来源 directionhorizontal initValue{scm} Radio valuescm从SCM上传/Radio Radio valuemanual手动上传/Radio /RadioGroup Banner fullMode{false} icon{null} typewarning bordered description{ Typography.Text strong当前部署环境线上部署/Typography.Text br / Typography.Text 请选择正确的SCM构建产物防止出现不符合预期的发布操作。 /Typography.Text / } / br / Select fieldusers label{{ text: 创建用户, required: true }} style{{ width: 560 }} multiple initValue{[1, 2, 3, 4]} Select.Option value1曲晨一/Select.Option Select.Option value2夏可曼/Select.Option Select.Option value3曲晨三/Select.Option Select.Option value4蔡妍/Select.Option /Select /Form /SideSheet / ); };渲染结构上面板内部遵循标准的「header / body / footer」三段式布局title渲染进.semi-sidesheet-title关闭按钮默认IconClose位于 header 右侧footer有独立容器body 区域设置了flex: 1与overflow: auto以便内容过长时内部滚动SideSheetContent.tsx。头部底部边框、body 与 footer 的内边距等视觉细节均由 variables.scss 中的 spacing 变量控制方便通过主题定制。API 参考下表完整列出 SideSheet 的全部 API属性说明类型默认值afterVisibleChange面板展示/隐藏时动画结束触发的回调(isVisible: boolean) void-bodyStyle面板内容的样式CSSProperties-className类名string-closable是否允许通过右上角的关闭按钮关闭booleantruecloseIcon关闭按钮的 iconReactNodeIconClose /closeOnEsc允许通过键盘事件 Esc 触发关闭booleanfalsedisableScroll默认渲染在 document.body 层时是否禁止 body 的滚动即给 body 添加overflow: hiddenbooleantruefooter侧边栏底部ReactNodenullgetPopupContainer指定父级 DOM弹层将会渲染至该 DOM 中自定义需要设置position: relative这会改变浮层 DOM 树位置但不会改变视图渲染位置() HTMLElement-headerStyle面板头部的样式CSSProperties-height高度位置为top或bottom时生效number | string400keepDOM关闭 SideSheet 时是否保留内部组件不销毁booleanfalsemask是否显示遮罩当mask{false}时允许对外部区域进行操作booleantruemaskClosable是否允许通过点击遮罩来关闭面板booleantruemaskStyle遮罩的样式CSSProperties-motion是否允许动画booleantrueplacement侧边栏滑出位置支持top,bottom,left,rightstringrightsize尺寸支持small(448px)medium(684px),large(920px)仅在left或right时生效stringsmallstyle可用于设置样式CSSProperties-title面板的标题ReactNode-visible面板是否可见booleanfalsewidth宽度位置为left或right时生效number | string448zIndex弹层 z-index 值number1000onCancel取消面板时的回调函数(e: MouseEvent) void-各属性的默认值在 index.tsx 的defaultProps中与文档一一对应其中afterVisibleChange默认为noop并且通过getDefaultPropsFromGlobalConfig支持从全局配置覆盖默认值类型约束如placement仅允许四种取值、size仅允许三档取值由propTypes校验index.tsx。源码级机制动画、Esc 关闭与 keepDOM动画调度SideSheet 的滑入滑出动画通过两层CSSAnimation编排index.tsx外层控制遮罩的淡入淡出内层控制面板本体按placement方向位移。SCSS 中为四个方向分别定义了slideShow_*/slideHide_*位移 keyframes以及opacityShow/opacityHide淡入淡出 keyframessideSheet.scss动画时长、缓动函数均可在 variables.scss 中通过主题变量调优。关闭动画结束后组件才真正从 DOM 卸载借助displayNone状态控制这也是afterVisibleChange在动画结束时才被回调的原因将motion{false}可完全禁用动画此时组件同步挂载/卸载。Esc 键盘关闭当closeOnEsc为true时组件在显示期间向window注册keydown监听setOnKeyDownListener隐藏时移除。Foundation 的handleKeyDown检测到KeyCode.ESC后stopPropagation并触发handleCancelsideSheetFoundation.ts即与点击关闭按钮、点击遮罩走同一条onCancel回调链路。keepDOM设置keepDOM后面板关闭时内部组件不会销毁仅通过semi-sidesheet-hiddendisplay: none类隐藏index.tsx 与 sideSheet.scss。这在需要保留面板内表单输入状态、避免每次打开都重新挂载昂贵子组件时非常有用。相应的卸载判断逻辑在shouldRender计算中index.tsx相关行为在测试用例中也有覆盖sideSheet.test.js。AccessibilityARIASideSheet 具有dialogrole 来表示它是一个弹窗组件SideSheetContent.tsx并设置了tabIndex{-1}便于焦点管理内部 header 具有headingrole 表明是 header且aria-level{1}标明标题层级SideSheetContent.tsx遮罩层标记了aria-hidden{true}SideSheetContent.tsx避免辅助技术重复朗读无意义的遮罩内容组件还透传aria-label等>赞分享前端UI组件设计系统【免费下载链接】semi-designA modern, comprehensive, flexible design system and React UI library, AI-friendly built-in.Provide 3000 Design Tokens, easy to build your design system. Make Semi Design to Any Design.‍ Design to Code in one click项目地址https://gitcode.com/gh_mirrors/se/semi-design点击查看免费下载相关推荐Semi Design Slider 滑动选择器完全指南从基础用法到源码级原理Semi Design Slider 滑动选择器完全指南从基础用法到源码级原理 Semi Design 的 Slider滑动选择器是输入类Input组前端UI组件设计系统Semi Design Card 组件完全指南从基础用法到源码级原理剖析Semi Design Card 组件完全指南从基础用法到源码级原理剖析 Card卡片是 Semi Design 展示类组件中的核心容器组件用于承载标题前端UI组件设计系统Semi Design TreeSelect 树选择器完全指南从基础用法到源码级原理Semi Design TreeSelect 树选择器完全指南从基础用法到源码级原理 导读 本文基于 Semi Designdouyinfe/semi u前端UI组件设计系统上一篇攻克MediaPipe面部关键点漂移难题从抖动到稳定的全流程优化方案下一篇突破M1/M2瓶颈MediaPipe Apple Silicon Mac姿势检测完美解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考