
前端UI组件【免费下载链接】rsuite A suite of React components .项目地址https://gitcode.com/gh_mirrors/rs/rsuite点击查看免费下载导读List是 rsuite 中用于展示列表型数据、并原生支持拖拽排序的容器组件。本文将从基础渲染、尺寸与边框样式到基于collection的分组排序、固定项排序等高级用法逐一展开并结合仓库源码剖析其拖拽排序的底层实现helper 元素、auto-scroll、事件回调最后给出完整的 Props 参考表。读完本文你将能够熟练使用List/List.Item实现消息流、商品分类、待办清单等各类列表交互场景并理解排序回调中Payload的含义与数据更新策略。组件定位与核心特性根据官方文档docs/pages/components/list/en-US/index.md的定义List 组件用于展示一组数据适合呈现列表型内容并支持拖拽排序。它的核心特性包括标准语义化结构List渲染为rolelistList.Item渲染为rolelistitem见 List.tsx 与 ListItem.tsx对无障碍与 SEO 友好尺寸体系lg / md / sm / xs四档默认md视觉样式边框bordered、悬停动效hover、条目分隔线divider拖拽排序sortable开启后支持鼠标与触摸两种交互并配套完整的生命周期回调分组排序通过collection将条目划分到独立分组组内索引唯一即可实现“组内排序、跨组不动”。导入方式import { List } from rsuite;与 rsuite 其他组件一样List从包根入口统一导出其静态子组件通过List.Item访问Subcomponents声明见 List.tsx。推荐按需引入对应样式如rsuite/dist/rsuite.min.css或样式变量体系并在使用前确认项目已配置好 rsuite 的样式加载方式。基础用法最基础的场景是直接渲染一组数据。下面的示例来自官方文档示例片段 default.md展示了一个简单的聊天消息列表import { List } from rsuite; const messages [ { id: 1, sender: Alice, content: Hey, are we still meeting tomorrow?, time: 2024-12-05 10:15 }, { id: 2, sender: Bob, content: Yes, let’s meet at 3 PM., time: 2024-12-05 10:18 }, { id: 3, sender: Charlie, content: Can you send me the report?, time: 2024-12-05 11:00 }, { id: 4, sender: David, content: I will, no worries., time: 2024-12-05 11:05 } ]; const App () ( List {messages.map(message ( List.Item key{message.id} strong{message.sender}/strong: {message.content} small({message.time})/small /List.Item ))} /List );List.Item本身是通用容器继承自内部Box条目内容可以完全自定义任意嵌套文本、图标、组件。默认渲染为纵向列表容器自带overflow-y: auto见 styles/index.scss内容超出时可滚动。尺寸控制List与List.Item均支持size属性取值lg | md | sm | xs默认md。List.Item上的size会覆盖来自父级List的默认值源码中const size sizeProp || parentSize;见 ListItem.tsx。官方示例 size.md 使用SegmentedControl动态切换尺寸import { List, SegmentedControl } from rsuite; const data [Roses are red, Violets are blue, Sugar is sweet, And so are you]; const App () { const [size, setSize] React.useState(sm); return ( SegmentedControl data{[ { value: xs, label: Extra Small }, { value: sm, label: Small }, { value: md, label: Medium }, { value: lg, label: Large } ]} value{size} onChange{setSize} / hr / List size{size} {data.map((item, index) ( List.Item key{index} index{index} {item} /List.Item ))} /List / ); };各尺寸对应的上下内边距定义在样式表中styles/index.scsssizepadding-top / padding-bottomlg20pxmd13pxsm8pxxs2px边框与悬停样式边框为List添加bordered属性即可为整个列表绘制圆角边框同时每个条目获得水平方向的内边距padding-inline-start/end: 20pxList bordered List.ItemRoses are red/List.Item List.ItemViolets are blue/List.Item List.ItemSugar is sweet/List.Item List.ItemAnd so are you/List.Item /List悬停动效添加hover属性后条目在鼠标悬停时会显示背景高亮--rs-list-hover-bgList hover {/* ... */} /List样式实现上List通过data-bordered、data-hover、data-sortable、data-divider、data-sorting等 data 属性驱动样式List.tsx对应 CSS 使用属性选择器匹配例如.rs-list[data-hovertrue] .rs-list-item:hover { background-color: var(--rs-list-hover-bg); }条目分隔线 dividerdivider属性用于控制条目之间是否显示分隔线默认值为true源码默认值见 List.tsx。该属性自5.75.0版本引入见 List.tsx 的version 5.75.0注释。无分隔线的紧凑示例 no-divider.md 中使用divider{false}与sizexs组合出联系人信息行图标 文本import { List, HStack, Text } from rsuite; import PeoplesIcon from rsuite/icons/Peoples; import LocationIcon from rsuite/icons/Location; import EmailIcon from rsuite/icons/Email; import GlobalIcon from rsuite/icons/Global; const data [ { id: 1, icon: PeoplesIcon /, value: rsuite/team }, { id: 2, icon: LocationIcon /, value: Shanghai, China }, { id: 3, icon: EmailIcon /, value: a hrefmailto:john.doersuitejs.comjohn.doersuitejs.com/a }, { id: 4, icon: GlobalIcon /, value: a hrefhttp://rsuitejs.comrsuitejs.com/a } ]; const App () ( List divider{false} sizexs {data.map(item ( List.Item key{item.id} HStack {item.icon} Text{item.value}/Text /HStack /List.Item ))} /List );分隔线样式通过相邻兄弟选择器实现.rs-list[data-dividertrue] .rs-list-item .rs-list-item { border-top: var(--rs-list-border-width) solid var(--rs-list-border); }拖拽排序核心场景开启sortable后即可拖拽重排。官方示例 sortable.md 是一个待办清单import { List } from rsuite; const defaultData [ { text: Finish the project report }, { text: Attend team meeting at 3 PM }, { text: Buy groceries for the week }, { text: Call mom to check in } ]; const App () { const [data, setData] React.useState(defaultData); const handleSortEnd ({ oldIndex, newIndex }) setData(prvData { const moveData prvData.splice(oldIndex, 1); const newData [...prvData]; newData.splice(newIndex, 0, moveData[0]); return newData; }); return ( List sortable bordered onSort{handleSortEnd} {data.map(({ text }, index) ( List.Item key{index} index{index} {text} /List.Item ))} /List ); };要点说明index必填官方文档明确强调「indexofList.Itemis required」排序引擎依据每个条目的index计算新位置且索引必须在所属 collection 内唯一数据同步由开发者负责List只负责视觉上的拖拽动画不会自动修改你的 state。需要在onSort或onSortEnd回调中拿到{ oldIndex, newIndex }手动对数据数组执行splice移动回调时机onSortStart按下并经过pressDelay后开始、onSortMove拖拽移动过程中持续触发、onSortEnd动画结束后触发而onSort与onSortEnd在同一点触发见 useSortHelper.ts可用onSort做最终的数据提交。生命周期回调与排序状态List在sortable模式下会挂载鼠标/触摸事件List.tsxonMouseDown{sortable ? handleStart : undefined} onMouseUp{sortable ? handleEnd : undefined} onTouchStart{sortable ? handleTouchStart : undefined} onTouchEnd{sortable ? handleTouchEnd : undefined}handleStartuseSortHelper.ts会做一系列前置校验只有满足全部条件才启动排序不是鼠标右键button ! 2命中的是已注册的 list item条目未设置disabled当前不在排序状态拖拽起点不是输入框、按钮等交互元素isContainInteractiveElement排除保证列表内的按钮仍可正常点击。校验通过后pressDelay毫秒后触发handlePress真正开始拖拽。测试用例 List.spec.tsx 使用vi.useFakeTimers()验证了pressDelay{0}时mousedown后onSortStart会被调用。排序动画的底层实现从源码结构看拖拽排序由 useSortHelper.ts 驱动其核心机制包括helper 元素拖拽开始时克隆被拖拽节点追加rs-list-item-helper类并position: fixed跟随指针移动useSortHelper.ts。样式表为其提供了阴影、圆角与z-index--rs-zindex-list-helper且在 Modal / Drawer 打开时 z-index 会相应叠加styles/index.scss确保浮层内拖拽仍可见holder 占位原节点添加rs-list-item-holder类visibility: hidden隐藏本体同时以虚线边框勾勒出目标占位styles/index.scss兄弟节点位移遍历同 collection 内按index排序的条目根据指针与目标节点的边缘偏移edgeOffset计算每个兄弟节点的translate3d位移并设置transitionDuration动画时长useSortHelper.ts自动滚动autoScroll默认开启。拖拽到容器边缘时AutoScrollerAutoScroller.ts会根据最大/最小位移边界自动滚动滚动容器并把滚动增量叠加到 helper 的位移上useSortHelper.ts触屏支持触摸拖拽时通过touchmove事件上的preventDefault()禁止页面滚动并临时将document.body.style.overflow设为hidden结束时恢复useSortHelper.ts。排序结束后清理 helper 节点、复位所有条目的 transform / transition并触发onSortEnd与onSortuseSortHelper.ts。索引与集合管理条目注册与查询由 useManager.ts 维护ListItem挂载时通过register将{ node, edgeOffset, info: { collection, disabled, index } }推入对应 collection 的数组卸载时unregisterListItem.tsx。getOrderedItems按index升序排序供排序引擎按视觉顺序遍历useManager.ts。这也是文档要求index在 collection 内唯一的原因——它直接决定了排序算法的基准顺序。分组排序 Collection Sort通过collection属性可以把条目划分到多个独立分组。官方文档说明每个collection相互独立List.Item的index必填且在该 collection 内唯一。示例 collection.md 用商品分类演示了分组拖拽——同一分组内可自由排序不同分组的条目互不干扰import { List } from rsuite; const defaultData [ { text: Apple iPhone 15, collection: 0, price: 999, status: In Stock }, { text: Samsung Galaxy S23, collection: 0, price: 849, status: Out of Stock }, { text: Google Pixel 8, collection: 0, price: 799, status: In Stock }, { text: Sony WH-1000XM5 Headphones, collection: 1, price: 350, status: In Stock }, { text: Bose QuietComfort 45, collection: 1, price: 329, status: In Stock }, { text: Beats Studio 3 Wireless Headphones, collection: 1, price: 299, status: Out of Stock }, { text: Dell XPS 13 Laptop, collection: 2, price: 1200, status: In Stock }, { text: MacBook Pro 16-inch, collection: 2, price: 2400, status: Out of Stock }, { text: HP Spectre x360, collection: 2, price: 1500, status: In Stock }, { text: Oculus Quest 2, collection: 3, price: 299, status: In Stock, disabled: true }, { text: PlayStation VR, collection: 3, price: 399, status: Out of Stock } ]; const colors [ #f0f8ff, // Light Blue for Phones #f5f5dc, // Light Beige for Headphones #fff0f5, // Lavender for Laptops #ffebcd // Blanched Almond for VR ]; const App () { const [data, setData] React.useState(defaultData); const handleSortEnd ({ oldIndex, newIndex }) setData(prvData { const moveData prvData.splice(oldIndex, 1); const newData [...prvData]; newData.splice(newIndex, 0, moveData[0]); return newData; }); const getCollectionStyle collection { return { backgroundColor: colors[collection], color: black }; }; return ( List sortable bordered onSort{handleSortEnd} {data.map(({ text, collection, disabled, status, price }, index) ( List.Item key{text} index{index} disabled{disabled} collection{collection} style{getCollectionStyle(collection)} {text} - ${price} - Status: {status} /List.Item ))} /List ); };注意示例中还演示了disabled的用法Oculus Quest 2设置了disabled: true该条目在拖拽时会被排除useSortHelper中!curManagedItem.info.disabled校验对应样式为cursor: not-allowedstyles/index.scss。collection的类型为string | number默认0。需要留意的是index只需在单个 collection 内部唯一不同 collection 可以复用相同的 index 值如每个分组都从 0 开始排序引擎按collection隔离处理useManager.ts 按 collection 取有序列表。固定项排序 Fixed Item Sort基于Collection Sort可以进一步实现“排序过程中部分条目固定不动”的效果。示例 sort-fixed.md 中浏览器列表的左侧序号项被固定只有右侧浏览器名称项参与排序import { List } from rsuite; const BrowserList [Chrome, Edge, FireFox, Safari]; const App () { const [data, setData] React.useState(BrowserList); const handleSortEnd ({ oldIndex, newIndex }) setData(prvData { const moveData prvData.splice((oldIndex - 1) / 2, 1); const newData [...prvData]; newData.splice((newIndex - 1) / 2, 0, moveData[0]); return newData; }); return ( List sortable bordered onSort{handleSortEnd} {data.flatMap((browser, index) [ List.Item key{index} index{index * 2} collectionorder disabled {index 1} /List.Item, List.Item key{browser} index{index * 2 1} {browser} /List.Item ])} /List ); };实现思路拆解将列表视为“序号项 内容项”交替出现的结构序号项与内容项共享同一个collectionorder但序号项设置disabled从而固定位置序号项使用偶数索引index * 2内容项使用奇数索引index * 2 1保证组内索引唯一且顺序正确排序回调中通过(oldIndex - 1) / 2和(newIndex - 1) / 2把“视觉索引”换算回内容数组的真实下标再执行数据移动。这种模式适用于需要固定序号徽标、行号或按钮列同时允许内容行重排的场景。自定义条目内容List.Item的内容完全开放可以自由组合 rsuite 的布局与视觉组件。官方示例 custom.md 使用HStack、Avatar、Text组装出带头像、发件人、时间与消息摘要的复杂条目import { List, HStack, Text, Avatar } from rsuite; const messages [ { id: 1, sender: Alice, content: Hey, are we still meeting tomorrow?, time: 2024-12-05 10:15, avatar: https://i.pravatar.cc/150?u1 }, { id: 2, sender: Bob, content: Yes, let’s meet at 3 PM., time: 2024-12-05 10:18, avatar: https://i.pravatar.cc/150?u2 }, { id: 3, sender: Charlie, content: Can you send me the report?, time: 2024-12-05 11:00, avatar: https://i.pravatar.cc/150?u3 }, { id: 4, sender: David, content: I will, no worries., time: 2024-12-05 11:05, avatar: https://i.pravatar.cc/150?u4 } ]; const App () ( List {messages.map(message ( List.Item key{message.id} HStack spacing{15} alignItemscenter Avatar src{message.avatar} alt{message.sender} circle / HStack.Item flex{1} HStack justifyContentspace-between Text strong{message.sender}/Text Text muted sizesm {message.time} /Text /HStack Text{message.content}/Text /HStack.Item /HStack /List.Item ))} /List );Props 完整参考List属性类型(默认值)说明版本autoScrollboolean(true)列表溢出时启用自动滚动borderedboolean显示列表项周围边框dividerboolean在列表项之间显示分隔线5.75.0hoverboolean启用列表项悬停动画onSort(payload: Payload ) void排序结束时触发的回调onSortEnd(payload: Payload ) void排序操作结束后触发的回调onSortMove(payload: Payload ) void列表项在列表中移动时触发的回调onSortStart(payload: Payload ) void排序开始时触发的回调pressDelaynumber(0)按下后触发排序前的延迟毫秒sizelg | md | sm | xs(md)定义列表项的尺寸sortableboolean启用列表项排序功能transitionDurationnumber(300)排序动画时长毫秒List.Item属性类型(默认值)说明collectionnumber | string(0)列表项的集合标识符disabledboolean禁用该条目防止其被移动index *number条目在其集合内的唯一索引排序时必填sizelg | md | sm | xs(md)定义单个列表项的尺寸覆盖父级 List 的 sizePayload 类型排序回调统一携带以下数据结构定义见 useSortHelper.tsinterface Payload { collection: number | string; node: HTMLElement; newIndex: number; oldIndex: number; }字段含义collection被移动条目所属的集合标识node被移动条目的 DOM 节点oldIndex移动前的索引newIndex移动后的目标索引。在onSort/onSortEnd中拿到oldIndex与newIndex后即可对数据数组执行标准的“移除 插入”操作完成状态同步见上文handleSortEnd示例。测试与可靠性验证仓库为 List 提供了较完整的测试覆盖src/List/test/List.spec.tsx验证标准 Props 透传、rolelist语义、data-bordered/data-divider/data-hover/data-sortable属性、五种尺寸渲染以及onSortStart/onSortMove/onSortEnd/onSort回调在鼠标事件流mousedown → mousemove → mouseup中的触发顺序ListItem.spec.tsx验证List.Item的渲染、尺寸、disabled 行为ListStyle.spec.tsx 与 ListItemStyle.spec.tsx验证样式属性与 data 属性的对应关系。测试注释也明确指出受测试环境限制触摸事件无法完整自动化覆盖需要在真实设备上手动验证List.spec.tsx——这是移动端集成时需要特别留意的点。小结与最佳实践纯展示场景直接使用ListList.Item按需搭配size、bordered、hover、divider{false}可排序场景List sortable务必为每个List.Item提供collection 内唯一的index并在onSort/onSortEnd中基于oldIndex/newIndex手动更新数据源分组场景用collection隔离分组组间索引互不干扰用disabled固定不允许移动的条目交互细节条目内含按钮/输入框时排序引擎会自动排除交互元素上的拖拽pressDelay可防止误触transitionDuration控制动画节奏autoScroll保证长列表边缘拖拽的顺滑体验。适用前提本文所述行为基于当前仓库中 rsuite 的List实现divider属性自 5.75.0 起可用使用更低版本时需注意 API 差异。赞分享前端UI组件【免费下载链接】rsuite A suite of React components .项目地址https://gitcode.com/gh_mirrors/rs/rsuite点击查看免费下载相关推荐Kronos-Tokenizer-2k与传统金融预测工具对比为什么它能实现零样本跨市场任务突破Kronos Tokenizer 2k与传统金融预测工具对比为什么它能实现零样本跨市场任务突破 Kronos Tokenizer 2k作为金融市场语言的创新前端UI组件Semi Design List 列表组件完全指南从基础渲染到虚拟滚动、拖拽排序与键盘交互Semi Design List 列表组件完全指南从基础渲染到虚拟滚动、拖拽排序与键盘交互 列表List是 Semi Designsemi ui中最常前端UI组件设计系统APITable 组件库 TreeView 树组件实战指南基础渲染、异步加载与拖拽排序APITable 组件库 TreeView 树组件实战指南基础渲染、异步加载与拖拽排序 TreeView 是 APITable 开源项目组件库 apita低代码后端前端协同办公上一篇umi SharedArrayBuffer多线程内存共享实战指南下一篇突破物理仿真瓶颈MuJoCo与MJX的可视化与数据交互全攻略创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考