资讯详情

资讯详情

TanStack Table Alpine 列排序(Column Ordering)实战指南:状态管理、拖拽重排与源码级实现解析

TanStack Table Alpine 列排序Column Ordering实战指南状态管理、拖拽重排与源码级实现解析【免费下载链接】table Headless UI for building powerful tables datagrids for TS/JS - React-Table, Vue-Table, Solid-Table, Svelte-Table项目地址: https://gitcode.com/gh_mirrors/ta/table本篇指南聚焦tanstack/alpine-tableTanStack Table v9 的 Alpine.js 适配层中的**列排序Column Ordering**能力。在 Alpine 项目中列排序允许用户或代码动态调整表格列的显示顺序并与其他特性列固定、分组协同工作。读完本篇你将掌握如何启用columnOrderingFeature、通过initialState/ 外部 Atom / v8 风格受控状态三种方式管理列顺序、用table.setColumnOrder接入拖拽排序以及从 table-core 源码 理解列排序在底层是如何生效的。快速上手启用列排序并接入示例Alpine 版本的官方示例位于 examples/alpine/column-ordering包含完整的 Vite Alpine 工程index.html、src/main.ts、src/makeData.ts、src/index.css可先运行该示例再对照阅读下文。列排序设置Column Ordering Setup启用列排序特性的方式与启用其他特性一致将columnOrderingFeature传入tableFeatures再交给createTable。添加该特性后相关的状态切片与 APIcolumnOrder、setColumnOrder、resetColumnOrder、getColumnIndexes等即被挂载到表格实例上。import { createTable, tableFeatures, columnOrderingFeature, } from tanstack/alpine-table const features tableFeatures({ columnOrderingFeature }) const table createTable({ features, columns, get data() { return local.data }, })注意上面data通过 getter 返回——在创建表格时像data这样的响应式输入例如用Alpine.reactive支撑的数据应通过 getter 读取这样表格才能感知更新。这也是 Alpine 适配层的通用约定参考 examples/alpine/column-ordering/src/main.ts 中的用法。从源码看特性启用后内部做了三件事见 columnOrderingFeature.tsgetInitialState为表格注入columnOrder默认状态空数组表示保持定义顺序getDefaultTableOptions注册默认的onColumnOrderChange状态更新器assignColumnPrototype/constructTableAPIs将列的getIndex、getIsFirstColumn、getIsLastColumn与表格的getColumnIndexes、setColumnOrder、resetColumnOrder、getOrderColumnsFn挂到原型与实例上。影响列顺序的要素与执行顺序默认情况下列按照columns数组中定义的顺序排列。但你也可以手动通过columnOrder状态指定列顺序。此外列固定Column Pinning与分组Grouping也会改变列的最终呈现顺序。三者的作用顺序固定如下列固定如果启用了固定列会被拆分为 start左侧固定、center未固定与 end右侧固定三个区域参见 Column Pinning 指南手动列排序手动指定的columnOrder在这一步应用分组如果启用了分组、存在分组状态且tableOptions.groupedColumnMode为reorder | remove则被分组的列会被移到列流的最前面reorder模式或直接从列流中移除remove模式参见 Grouping 指南。[!NOTE] 与列固定一起使用时columnOrder状态只会影响未固定unpinned的列。即固定的 start/end 区域列不受手动排序影响。这一顺序可以在 table-core 的列排序工具函数 中得到印证table_getOrderColumnsFn先应用columnOrder无排序时直接返回原始列随后调用orderColumns处理分组规则——groupedColumnMode: remove时过滤掉分组列reorder时把分组列按分组状态顺序前置。底层排序算法的细节从 table_getOrderColumnsFn 实现 可以观察到三个值得注意的行为未出现在columnOrder中的列会按原始定义顺序追加在已排序列之后不会丢失重复或未知 id算法用Map按 id 取出列后即删除天然处理了columnOrder中重复 id 与不存在 id 的情况叶子列粒度排序针对叶子列leaf columns进行setColumnOrder期望传入叶子列的 id 数组。管理列排序状态Column Order State如果不提供columnOrder状态TanStack Table 直接沿用columns数组的顺序。要自定义顺序只需把「列 id 字符串数组」交给columnOrder状态即可类型为ColumnOrderState Arraystring见 columnOrderingFeature.types.ts。方式一initialState 指定初始顺序如果只是想在首次渲染时指定列顺序在initialState表选项中设置columnOrder即可const features tableFeatures({ columnOrderingFeature }) const table createTable({ features, //... initialState: { columnOrder: [columnId1, columnId2, columnId3], }, //... })[!NOTE] 如果你同时用state表选项指定了columnOrder那么initialState将不再生效。请仅在initialState与state两者中选择其一管理某一状态切片不要同时使用。示例工程中也预留了这条注释掉的用法examples/alpine/column-ordering/src/main.ts。从源码看table_resetColumnOrder(table)无参调用时会克隆table.initialState.columnOrder不存在则重置为[]这正是「初始顺序」语义的落点见 columnOrderingFeature.utils.ts。方式二v9 推荐外部 Atom 托管如果需要动态改变列顺序、或在表格初始化之后再设置顺序可以像管理其他表格状态一样管理columnOrder。在 v9 中推荐用外部 Atom托管状态切片将 Atom 传入表格的atoms选项。外部 Atom 能提供应用中任何位置的细粒度订阅其他代码可以不经过拥有表格的组件直接读写列顺序。tanstack/store已经是tanstack/alpine-table的依赖所以可以直接使用createAtom。import { createAtom } from tanstack/store import { createTable, tableFeatures, columnOrderingFeature, } from tanstack/alpine-table import type { ColumnOrderState } from tanstack/alpine-table const features tableFeatures({ columnOrderingFeature }) const columnOrderAtom createAtomColumnOrderState([ columnId1, columnId2, columnId3, ]) // 在需要的地方订阅 columnOrderAtom.subscribe(() { // 响应列顺序变化 }) const table createTable({ features, //... atoms: { columnOrder: columnOrderAtom, }, //... })该方式同样在示例中预留了使用位置examples/alpine/column-ordering/src/main.ts。从源码看table_setColumnOrder最终通过setStateSlice(table, columnOrder, updater)更新状态见 columnOrderingFeature.utils.ts无论状态由表格内部管理、由state 回调托管还是由外部 Atom 托管入口完全一致。方式三v8 风格受控状态state onColumnOrderChangestate.columnOrder配合onColumnOrderChange的 v8 风格模式在 Alpine 中依然受支持把状态切片放进Alpine.reactive即可。对于简单集成或迁移 v8 代码来说很方便但粒度不如外部 Atom 细。更深入的对比参见 Table State 指南。const features tableFeatures({ columnOrderingFeature }) const local Alpine.reactive({ columnOrder: [columnId1, columnId2, columnId3] as ColumnOrderState, }) //... const table createTable({ features, //... state: { get columnOrder() { return local.columnOrder // 将响应式切片连接回表格 }, //... }, onColumnOrderChange: (updater) { local.columnOrder typeof updater function ? updater(local.columnOrder) : updater }, //... })这里的onColumnOrderChange对应源码中的TableOptions_ColumnOrdering.onColumnOrderChange见 columnOrderingFeature.types.ts类型为OnChangeFnColumnOrderState而特性注册时默认用makeStateUpdater(columnOrder, table)生成了默认回调这正是「表格内部托管状态」时自动工作的机制。重排列把拖拽事件接入 setColumnOrder如果表格 UI 允许用户拖拽重排列只需把拖拽方案的 drop 事件接到table.setColumnOrder上。下面以原生浏览器拖拽事件作用于表头单元格为例拖拽中的状态用Alpine.reactive保存以便模板响应式渲染const local Alpine.reactive({ movingColumnId: null as string | null }) // 把被拖拽的列移动到目标列之前 function handleDrop(targetColumnId: string) { const fromId local.movingColumnId if (!fromId || fromId targetColumnId) return table.setColumnOrder((prevColumnOrder) { const newColumnOrder [...prevColumnOrder] newColumnOrder.splice( newColumnOrder.indexOf(targetColumnId), 0, newColumnOrder.splice(newColumnOrder.indexOf(fromId), 1)[0]!, ) return newColumnOrder }) local.movingColumnId null }table.setColumnOrder的入参是一个UpdaterColumnOrderState——既可以是「下一个 id 数组」也可以是「接收旧数组、返回新数组的函数」见 columnOrderingFeature.types.ts。无论表格由内部管理columnOrder状态、由stateonColumnOrderChange控制还是由外部 Atom 托管调用方式完全相同。官方 Column Ordering 示例 传入的是一整组叶子列 id 的数组例如randomizeColumns() { table.setColumnOrder( faker.helpers.shuffle(table.getAllLeafColumns().map((d) d.id)), ) }示例中还演示了reset()setColumnOrder([])恢复定义顺序与stressTest()换入 1000 行数据完整逻辑见 examples/alpine/column-ordering/src/main.ts。列排序 API 一览表格级 APItable.setColumnOrder([lastName, firstName, age]) // 直接更新列顺序 table.resetColumnOrder() // 重置为 initialState.columnOrder table.resetColumnOrder(true) // 忽略初始状态清空顺序恢复定义顺序resetColumnOrder的语义与源码完全一致无参时克隆initialState.columnOrder传true时重置为[]见 columnOrderingFeature.utils.ts。此外表格还暴露了table.getColumnIndexes()它一次性构建所有可见固定区域内「列 id → 索引」的映射all/center/start/end四个区域是列级getIndex的数据来源多数应用无需直接调用它。列级 API在列固定、手动排序、分组都应用之后列对象暴露以下辅助方法读取其当前渲染位置column.getIndex() // 在全部可见叶子列中的索引 column.getIndex(start) // 在 start 固定区域中的索引 column.getIndex(center) // 在 center 区域中的索引 column.getIndex(end) // 在 end 固定区域中的索引 column.getIsFirstColumn() // 是否为第一个可见列 column.getIsLastColumn() // 是否为最后一个可见列这些辅助方法适合用于样式化列边界或构建需要感知当前渲染顺序的拖拽目标。从实现层面看getIndex依赖的索引映射是**记忆化memoized**的其 memo 依赖包括columns、columnOrder、columnPinning、columnVisibility、grouping与groupedColumnMode见 columnOrderingFeature.ts四个区域在一次遍历中构建完成避免每个列各自扫描见 columnOrderingFeature.utils.tsgetIsFirstColumn/getIsLastColumn则直接比较区域首/尾列的 id见同文件 L115-L147。position参数缺省时按all完整可见列序计算未找到时getIndex返回-1。Alpine 下的拖拽方案建议TanStack Table 对拖拽方案没有强绑定以下为官方给出的三条建议原生浏览器拖拽事件用dragstart、dragenter、dragend配合自己的Alpine.reactive状态零依赖、非常轻量代价是需要自行处理移动端触摸touch支持。社区中 Material React Table 的列排序就是这种「无 DnD 依赖」实现由于它本质只是用 DOM 事件喂给table.setColumnOrder因此这套思路可以直接迁移到 Alpine选用框架无关的库如果需要现成库可考察 Atlassian 的 Pragmatic drag and drop 这类框架无关方案。选定前请确认其维护状况、包体积以及对语义化table标记的兼容程度不要使用 React 专属 DnD 库包括 DnD Kit 的dnd-kit/*包。它们依赖 React 的组件模型无法在 Alpine 中工作。小结列排序是 TanStack Table 多特性协作的典型例子columnOrderingFeature提供状态与 APIcolumnOrder决定叶子列顺序列固定把列拆成三区、分组把分组列前置或移除三者按固定顺序作用于最终渲染。在 Alpine 中推荐用外部 Atom 托管columnOrder获得细粒度订阅简单场景可用initialState迁移场景可用 v8 风格受控状态。将拖拽 drop 事件接到table.setColumnOrder即可获得完整的用户可重排体验而getIndex系列列级 API 则为边界样式与拖拽目标提供了可靠的渲染位置依据。【免费下载链接】table Headless UI for building powerful tables datagrids for TS/JS - React-Table, Vue-Table, Solid-Table, Svelte-Table项目地址: https://gitcode.com/gh_mirrors/ta/table创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →