Skip to content

Conversation

jqroom
Copy link
Contributor

@jqroom jqroom commented Aug 29, 2025

🤔 这个变动的性质是?

  • 新特性提交
  • 日常 bug 修复
  • 站点、文档改进
  • 演示代码改进
  • 组件样式/交互改进
  • TypeScript 定义更新
  • 包体积优化
  • 性能优化
  • 功能增强
  • 国际化改进
  • 重构
  • 代码风格优化
  • 测试用例
  • 分支合并
  • 其他改动(是关于什么的改动?)

🔗 相关 Issue

💡 需求背景和解决方案

☑️ 请求合并前的自查清单

⚠️ 请自检并全部勾选全部选项⚠️

  • 文档已补充或无须补充
  • 代码演示已提供或无须提供
  • TypeScript 定义已补充或无须补充
  • fork仓库代码是否为最新避免文件冲突
  • Files changed 没有 package.json lock 等无关文件

Summary by CodeRabbit

  • 新功能
    • 新增虚拟表格组件,支持虚拟滚动、动态行高、滚动到指定行、粘性表头/列。
    • 列排序升级为三态,并支持自定义排序图标。
    • 新增示例:同时固定表头与左列、虚拟/非虚拟滚动演示。
  • 文档
    • 补充虚拟表格使用说明、API 与示例代码。
  • 样式
    • 新增表头背景主题变量;优化虚拟表格与单元格样式表现。
  • Bug 修复
    • 修正英文文案拼写错误(Sticky Right Column)。

Copy link

coderabbitai bot commented Aug 29, 2025

Walkthrough

本次变更新增虚拟表格组件与相关钩子、类型与样式;扩展 Table 文档与示例(含虚拟滚动与动态行高);更新 Table 排序逻辑与排序图标接口;新增多处 H5 Demo;引入样式变量用于表头背景;补充 Joycode 代理定义与一键环境安装提示。

Changes

Cohort / File(s) Summary
Joycode 配置与代理定义
.joycode/modes/rules-4k79ih5s/agentDefinition.md, .joycode/prompt.json
新增前端开发者代理定义与一键环境安装提示 JSON,包含严格工作流与多语言/多生态环境诊断流程。
Table 文档
src/packages/table/doc.md, src/packages/table/doc.en-US.md
文档新增 TableVirtual 与 VirtualTableRef,对虚拟滚动相关 props、用法示例与 API 进行说明;新增「不开启虚拟滚动」「虚拟滚动+动态行高」示例引用。
虚拟表格实现(聚合导出)
src/packages/table/index.virtual.ts, src/packages/table/virtual/index.ts
新增虚拟表格入口与聚合导出,统一导出 TableVirtual、useVirtualScroll、useThrottle、useDebounce 及相关类型。
虚拟表格核心
src/packages/table/virtual/table-virtual.tsx, src/packages/table/virtual/virtual-scroll.ts, src/packages/table/virtual/table-row.tsx, src/packages/table/virtual/hooks.ts
新增 TableVirtual 组件、虚拟滚动 Hook(支持动态行高)、虚拟行渲染组件与节流/防抖 Hook;对外导出 VirtualTableProps、VirtualTableRef、VirtualScrollOptions 等。
类型与排序 API 变更
src/types/spec/table/base.ts, src/packages/table/table.tsx
新增 SortStateType;TableColumnProps 增加 sorterIcon 回调;BaseTable.onSort 签名扩展(sortedData 可选并新增 sortState);Table 排序逻辑升级为三态并支持自定义图标渲染。
样式与变量
src/packages/table/table.scss, src/styles/variables.scss, src/styles/variables-jmapp.scss
新增 .nut-table-main-virtual 样式;thead/th/td 增加 box-sizing 与表头背景色、z-index;新增 $table-th-bg-color 变量。
示例接入与多语言
src/packages/table/demo.tsx
引入 Demo15、DemoNoVirtual、DemoVirtual;新增/修正 i18n 文案键值并插入新示例渲染段落。
新示例:虚拟/非虚拟
src/packages/table/demos/h5/demo-no-virtual.tsx, src/packages/table/demos/h5/demo-virtual.tsx
新增 H5 示例:非虚拟与虚拟表格(支持动态行高、固定列、排序、滚动到索引等)。
新示例:双向吸顶/固定列
src/packages/table/demos/h5/demo15.tsx
新增 H5 示例:左侧固定列的基础表格。
示例更新:排序交互
src/packages/table/demos/h5/demo9.tsx
使用库内 TableColumnProps/SortStateType;新增 sorterIcon 动态图标;onSort 回调签名与文案更新。

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant TV as TableVirtual
  participant VS as useVirtualScroll
  participant TR as TableRow (memo)
  participant DOM as Scroll Container

  U->>TV: 渲染 TableVirtual(props: data, columns, virtual, height, rowHeight, dynamicHeight...)
  TV->>VS: 初始化 useVirtualScroll(total, viewportHeight, itemHeight, overscan, dynamicHeight)
  VS-->>TV: { visibleRange, offsetY, totalHeight, onScroll, getRowRef, scrollTo }
  TV->>DOM: 渲染容器与占位高度(totalHeight),绑定 onScroll
  loop 可视区渲染
    TV->>TR: 渲染 [start..end] 行,传入 sticky 样式/渲染器/getRowRef
    TR-->>TV: 行节点(动态行高时回填高度)
    TV->>VS: updateItemHeight(index, measuredHeight) (动态行高)
  end
  DOM-->>VS: scroll 事件
  VS-->>TV: 更新 visibleRange/offsetY
  TV->>TR: 按新范围重渲染可视行
Loading
sequenceDiagram
  autonumber
  participant U as User
  participant TV as TableVirtual
  participant S as Sorter
  participant VS as useVirtualScroll

  U->>TV: 点击列头排序
  TV->>S: 计算 nextSortState(null→asc→desc→null)
  alt asc/desc
    S->>TV: 生成 sortedData
    TV-->>U: onSort(column, sortedData, sortState)
    TV->>VS: 触发容器 scroll 刷新可视范围
  else 清除排序
    TV-->>U: onSort(column) // 无排序数据
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 分钟

Possibly related PRs

Suggested reviewers

  • oasis-cloud
  • irisSong
  • xiaoyatong

Poem

我是代码田里的小兔子,蹦——
搭起虚拟滚动的小桥东与西;
动态行高轻轻起,排序三态悄然栖;
黏黏的表头不再移,
滚到第 N 行,只需叫我一声「去!」 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added 3.x Target branch 3.x action:review This PR needs more reviews (less than 2 approvals) labels Aug 29, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/packages/table/doc.md (2)

141-152: 同步 onSort 与 sorterIcon 文档到最新 API

当前文档的 onSort 签名与实现不符(实现里第三个参数为 sortState,且可省略),同时漏了列级 sorterIcon(函数)能力。请更新如下。

-| onSort | 点击排序按钮触发 | `item: TableColumnProps, data: Array<any>` | `-` |
-| sorterIcon | 排序 icon | `ReactNode` | `<ArrowDown />` |
+| onSort | 点击排序按钮触发 | `(column: TableColumnProps, sortedData?: Array<any>, sortState?: SortStateType)` | `-` |
+| sorterIcon | 全局排序图标(列未自定义时生效) | `ReactNode` | `<ArrowDown />` |

155-164: 补充列级 sorterIcon 能力,并澄清 sorter 默认算法

TableColumnProps 需补充 sorterIcon?: (currentSortState: SortStateType) => ReactNode;同时说明 sorter: 'default' 的行为与 true 一致(基于列 key 值排序)。

 | key | 列的唯一标识 | `string` | `-` |
 | title | 表头标题 | `string` | `-` |
 | align | 列的对齐方式 | `left` \| `center` \| `right` | `left` |
+| sorterIcon | 自定义该列的排序图标 | `(currentSortState: SortStateType) => ReactNode` | `-` |
-| sorter | 排序,可选值有 true,function, default, 其中 default表示点击之后可能会依赖接口, function可以返回具体的排序函数, default表示采用默认的排序算法 | `boolean` \| `Function` \| `string` | `-` |
+| sorter | 排序,可选值 `true` / `'default'` / 自定义函数;`'default'` 与 `true` 一致,按该列 `key` 的值排序 | `boolean` \| `Function` \| `string` | `-` |
 | render | 自定义渲染列数据,优先级高 | `Function(record)` | `-` |
 | width | 列宽度 | `number` | `auto` |
 | fixed | 固定位置 | `left` \| `right` | `-` |
🧹 Nitpick comments (27)
.joycode/modes/rules-4k79ih5s/agentDefinition.md (5)

6-7: 措辞/语法存在歧义,建议压缩指令并避免双重否定

当前句式冗长且有语法瑕疵,建议更清晰、可执行:

- - Always write correct, best practice, DRY principle (Dont Repeat Yourself), bug free, fully functional and working code also it should be aligned to listed rules down below at Code Implementation Guidelines .
- - Focus on easy and readability code, over being performant.
+ - 代码必须可运行、符合最佳实践并遵循 DRY;严禁遗留明显缺陷。
+ - 在不牺牲可维护性的前提下优先可读性;性能优化放在有数据支撑时进行。

11-13: 标点与表达不统一,影响可读性

补齐标点并消除歧义:

- - Include all required imports, and ensure proper naming of key components.
- - Be concise Minimize any other prose.
+ - Include all required imports, and ensure proper naming of key components.
+ - Be concise; minimize any other prose.

3-5: 流程要求(先长篇伪代码再“Confirm”)可能拖慢常规开发节奏

建议在 PR 场景弱化“先确认再写代码”的强约束,改为:对复杂/破坏性变更需要先给出设计要点或伪代码,日常小改可直接提交并在 MR 描述中列出变更点。


1-1: Markdown 结构与重复开头用词

可将首句改为标题并减少重复主语,提升可读性与搜索友好度:

- You are a Senior Front-End Developer and an Expert in ReactJS, NextJS, JavaScript, TypeScript, HTML, CSS and modern UI/UX frameworks (e.g., TailwindCSS, Shadcn, Radix). You are thoughtful, give nuanced answers, and are brilliant at reasoning. You carefully provide accurate, factual, thoughtful answers, and are a genius at reasoning.
+ # Role: Senior Front-End Developer (React/NutUI)
+ You provide accurate, thoughtful, and well-reasoned answers across React, TypeScript, and modern UI frameworks (TailwindCSS, Shadcn, Radix) when applicable.

16-27: 收窄“Coding Environment” 列表,突出 NutUI React 核心栈

验证结果:仓库主体为 NutUI‑React(大量 src/packages、sites 文档与 demo);Next.js 仅出现在模板 packages/nutui-templates/next-demo;Tailwind 未见源码/配置引用(仅在 node_modules 文档中)。

  • 建议将 "Coding Environment" 优先列为:React、TypeScript、NutUI(主题/样式体系);将 Next.js、Tailwind 标注为“仅在示例/模板/示例站使用”或移至示例说明。
  • 建议追加句子: “除非在文档/示例中,默认不引入与库样式体系冲突的全局样式方案。”

位置:.joycode/modes/rules-4k79ih5s/agentDefinition.md

.joycode/prompt.json (3)

1-1: 建议补充跨平台与版本管理的默认策略,避免“curl | bash”式不安全安装

在安装指导中优先推荐:

  • Node:nvm/Volta + .nvmrc/packageManager(启用 corepack,优先 pnpm/yarn 约定);安装依赖用 npm ci/pnpm install --frozen-lockfile
  • 多语言并存时,明确优先级与最小必需集合,避免全量安装拖慢流程。

可在“工作流程/安装指导”下追加安全与可重复性的约束说明。


1-1: 将 .joycode 目录排除出 npm 发包产物

若该目录仅服务于内部辅助工具,建议通过以下任一方式避免随包发布:

  • 在 package.json 增加 "files" 白名单不含 .joycode/
  • 或在 .npmignore 中加入 .joycode/

请确认当前发包配置。需要我起草变更片段可告知。


1-1: 文案微调(中文)以减少歧义

示例:

- 检测和安装Node.js(如果用户没要求推荐LTS版本)
+ 检测并安装 Node.js(若未指定版本,默认推荐安装 LTS 版本)

如需,我可批量润色整段中文提示,保持术语与项目文档一致。

src/styles/variables.scss (1)

2233-2233: 新增表头背景变量合理,但建议统一回退值写法

为与本文件其他 token 风格保持一致,回退值可用 $white 而非硬编码 #fff。

可按下列方式微调:

-$table-th-bg-color: var(--nutui-table-th-bg-color, #fff) !default;
+$table-th-bg-color: var(--nutui-table-th-bg-color, $white) !default;
src/styles/variables-jmapp.scss (1)

2345-2345: 与基础主题保持一致的回退值写法

同 variables.scss,建议将回退值改为 $white,便于主题一致性与后续全局替换。

-$table-th-bg-color: var(--nutui-table-th-bg-color, #fff) !default;
+$table-th-bg-color: var(--nutui-table-th-bg-color, $white) !default;
src/packages/table/demos/h5/demo15.tsx (3)

43-50: 命名小拼写:columnsStickHeaderLeft → columnsStickyHeaderLeft

更正命名有助于可读性与检索。

-  const [columnsStickHeaderLeft] = useState<Array<TableColumnProps>>([
+  const [columnsStickyHeaderLeft] = useState<Array<TableColumnProps>>([

并同步更新引用处:

-      columns={columnsStickHeaderLeft}
+      columns={columnsStickyHeaderLeft}

5-41: 示例数据存在“生日/年龄”不一致,易误导

为避免读者困惑,建议让年龄与生日大致对应,或仅保留其一。


5-41: 常量数据/列配置无需 useState,使用 useMemo 或组件外常量更轻量

减少不必要的状态,避免心智负担。

-import React, { useState } from 'react'
+import React from 'react'

-  const [data] = useState([
+  const data = React.useMemo(
+    () => [
       { /* ... */ },
       { /* ... */ },
       { /* ... */ },
       { /* ... */ },
       { /* ... */ },
-  ])
+    ],
+    []
+  )

-  const [columnsStickyHeaderLeft] = useState<Array<TableColumnProps>>([
+  const columnsStickyHeaderLeft = React.useMemo<Array<TableColumnProps>>(
+    () => [
       { /* ... */ },
       { /* ... */ },
       { /* ... */ },
       { /* ... */ },
       { /* ... */ },
-  ])
+    ],
+    []
+  )

Also applies to: 43-67

src/packages/table/demo.tsx (1)

59-61: 英文文案大小写统一

其余标题均为 Title Case,建议统一:

-      noVirtual: 'no virtual scroll',
-      virtual: 'virtual scroll',
+      noVirtual: 'No Virtual Scroll',
+      virtual: 'Virtual Scroll',
src/types/spec/table/base.ts (1)

26-30: onSort 新签名向后兼容,但可进一步增强类型安全

第二个参数建议泛型化,避免 any;同时为第三个参数补充 JSDoc,明确语义。

示意(需在接口层引入泛型,超出本次 diff 变更范围):

export interface BaseTable<T = any> extends BaseProps {
  // ...
  onSort?: (column: TableColumnProps, sortedData?: Array<T>, sortState?: SortStateType) => void
}

若暂不引入泛型,也建议在文档中明确 sortedData/SortStateType 的取值与时序。

src/packages/table/demos/h5/demo-no-virtual.tsx (1)

1-1: 优化:用 useMemo/常量替代 useState 保存静态 columns/data,移除 any

这两处状态从未更新,使用 useMemo 或直接常量可避免不必要的状态、重新渲染与类型漂移;同时将 render 的 _record 从 any 改为 DataItem。

-import React, { useState } from 'react'
+import React, { useMemo } from 'react'
@@
-  const [columns] = useState<TableColumnProps[]>([
+  const columns = useMemo<TableColumnProps[]>(() => [
@@
-      render: (_record: any, index: number) => {
+      render: (_record: DataItem, index: number) => {
         return index + 1
       },
@@
-  ])
+  ], [])
@@
-  const [data] = useState(generateData(10))
+  const data = useMemo(() => generateData(10), [])

Also applies to: 38-86, 88-91

src/packages/table/index.virtual.ts (1)

1-16: 建议:类型仅导入 + 同步命名导出,减少运行时体积并提升 API 对称性

将类型用 import type 引入,避免被打包;同时补充命名导出 TableVirtual,便于用户既可默认导入也可命名导入。

-import {
-  TableVirtual,
-  VirtualTableRef,
-  VirtualTableProps,
-  useVirtualScroll,
-  useThrottle,
-  useDebounce,
-} from './virtual'
-import { TableColumnProps } from '@/types/spec/table/base'
+import { TableVirtual, useVirtualScroll, useThrottle, useDebounce } from './virtual'
+import type { VirtualTableRef, VirtualTableProps } from './virtual'
+import type { TableColumnProps } from '@/types/spec/table/base'
@@
-export { useVirtualScroll, useThrottle, useDebounce }
+export { TableVirtual, useVirtualScroll, useThrottle, useDebounce }
 export type { TableColumnProps, VirtualTableRef, VirtualTableProps }
 export default TableVirtualWrapper
src/packages/table/doc.en-US.md (2)

177-184: 文案微调:dynamicHeight 描述更准确

更清晰表达为“运行时测量实际行高”,避免歧义。

-| dynamicHeight | Whether to enable dynamic height (if true, will try to get the actual height of each row) | `boolean` | `false` |
+| dynamicHeight | Whether to enable dynamic row height (measures actual row heights at runtime) | `boolean` | `false` |

117-126: 标题与说明优化:更贴合读者预期

将 “Disable Virtual Scrolling” 调整为更中性的 “Without Virtual Scrolling”,并点明即 virtual=false

-### Disable Virtual Scrolling
-
-Virtual scrolling table without enabling virtual scrolling, used as a regular table.
+### Without Virtual Scrolling
+
+Use TableVirtual as a regular table (virtual=false).
src/packages/table/table.scss (1)

52-56: 选择器可能过宽:.nut-table-main-virtual 下对 “> div” 的样式有外溢风险

.nut-table-main-body > div 可能同时命中虚拟容器与单元格自定义内容的直系包装 div。建议在 TSX 为虚拟容器添加特征类或 data-attr,并将选择器收窄(避免样式污染)。

可选方案(需同时在虚拟列表容器上加上对应类/属性):

-&-virtual {
-  .nut-table-main-body > div {
+&-virtual {
+  .nut-table-main-body > [data-virtual-container='true'] {
     background-color: $color-background-overlay;
   }
 }
src/packages/table/demos/h5/demo-virtual.tsx (1)

97-107: 示例中存在未使用代码(updateData、handleScrollToRow)

updateDatahandleScrollToRow 没有被调用。建议删除以简化示例,或增加交互按钮触发它们。

如选择删除,最小变更如下:

-  // 更新数据的方法
-  const updateData = (count: number) => {
-    setData(generateData(count))
-  }
-
-  // 滚动到指定行的方法
-  const handleScrollToRow = (index: number) => {
-    if (tableRef.current) {
-      tableRef.current.scrollToIndex(index)
-    }
-  }
+  // 演示用:如需重新生成/滚动示例,请在页面添加按钮后再恢复这些方法
src/packages/table/doc.md (1)

145-151: 用词修正:summary 含义不清

"是否显示简介" 容易误解,应表达为“总结栏内容”。建议改为:“总结栏内容”。类型 ReactNode 不变。

-| summary | 是否显示简介 | `ReactNode` | `-` |
+| summary | 总结栏内容 | `ReactNode` | `-` |
src/packages/table/virtual/table-virtual.tsx (2)

346-355: 虚拟切片边界处理可更稳健(避免 length=0 时负索引)

innerValue.length === 0 时,Math.min(visibleRange[0], innerValue.length - 1)-1。虽然当前 slice(-1, 0) 返回空数组,但语义不直观。建议将起止索引都 clamp 到 [0, length]

-    const dataToRender = virtual
-      ? innerValue.slice(
-          Math.min(visibleRange[0], innerValue.length - 1),
-          Math.min(visibleRange[1] + 1, innerValue.length)
-        )
-      : innerValue
+    const start = Math.max(0, Math.min(visibleRange[0], innerValue.length))
+    const end = Math.max(start, Math.min(visibleRange[1] + 1, innerValue.length))
+    const dataToRender = virtual ? innerValue.slice(start, end) : innerValue

404-426: 非虚拟模式不应强制设置固定高度

在非虚拟模式下强制 height 可能导致内容被裁剪且不滚动。建议仅在 virtualtrue 时设置 maxHeight/overflow,非虚拟时让内容自然撑开或由外部容器控制高度。

-          ...(virtual
-            ? {
-                height: height || 300,
-                maxHeight: height || 300,
-                overflow: 'auto',
-                position: 'relative',
-              }
-            : {
-                height: height || 300,
-              }),
+          ...(virtual
+            ? {
+                height: height || 300,
+                maxHeight: height || 300,
+                overflow: 'auto',
+                position: 'relative',
+              }
+            : {
+                // 非虚拟场景不强制高度,交由外部容器决定
+              }),
src/packages/table/virtual/table-row.tsx (2)

100-107: 顶层容器不应在子组件内部设置 key

key 应由父级列表渲染处控制;在子组件内部设置 key 既无效又易引发困惑。

   return (
     <div
       className={bodyClassPrefix}
-      key={`row-${rowIndex}`}
       ref={dynamicHeight && getRowRef ? getRowRef(rowIndex) : undefined}
     >

19-19: 样式类型应使用 React.CSSProperties

更明确的类型能提升可读性与编辑器提示质量。

-  getStickyStyle: (key: string) => Record<string, any> | undefined
+  getStickyStyle: (key: string) => React.CSSProperties | undefined
src/packages/table/virtual/virtual-scroll.ts (1)

241-252: 动态高度仅在获取 ref 时测量一次,行高变化无法感知

建议在 getRowRef 中对 element 使用 ResizeObserver 监听高度变化,或在行渲染后批量测量,保证缓存与真实高度一致。

如需,我可补充基于 ResizeObserver 的实现示例。

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between dcfd2f0 and 1178a17.

📒 Files selected for processing (20)
  • .joycode/modes/rules-4k79ih5s/agentDefinition.md (1 hunks)
  • .joycode/prompt.json (1 hunks)
  • src/packages/table/demo.tsx (4 hunks)
  • src/packages/table/demos/h5/demo-no-virtual.tsx (1 hunks)
  • src/packages/table/demos/h5/demo-virtual.tsx (1 hunks)
  • src/packages/table/demos/h5/demo15.tsx (1 hunks)
  • src/packages/table/demos/h5/demo9.tsx (2 hunks)
  • src/packages/table/doc.en-US.md (2 hunks)
  • src/packages/table/doc.md (2 hunks)
  • src/packages/table/index.virtual.ts (1 hunks)
  • src/packages/table/table.scss (3 hunks)
  • src/packages/table/table.tsx (5 hunks)
  • src/packages/table/virtual/hooks.ts (1 hunks)
  • src/packages/table/virtual/index.ts (1 hunks)
  • src/packages/table/virtual/table-row.tsx (1 hunks)
  • src/packages/table/virtual/table-virtual.tsx (1 hunks)
  • src/packages/table/virtual/virtual-scroll.ts (1 hunks)
  • src/styles/variables-jmapp.scss (1 hunks)
  • src/styles/variables.scss (1 hunks)
  • src/types/spec/table/base.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (9)
src/packages/table/virtual/virtual-scroll.ts (1)
src/packages/table/virtual/hooks.ts (1)
  • useThrottle (10-64)
src/types/spec/table/base.ts (1)
src/types/base/atoms.ts (1)
  • PositionX (42-42)
src/packages/table/demos/h5/demo-virtual.tsx (1)
src/packages/table/index.virtual.ts (2)
  • VirtualTableRef (15-15)
  • TableColumnProps (15-15)
src/packages/table/virtual/table-row.tsx (1)
src/packages/table/index.virtual.ts (1)
  • TableColumnProps (15-15)
src/packages/table/virtual/hooks.ts (2)
src/packages/table/index.virtual.ts (2)
  • useThrottle (14-14)
  • useDebounce (14-14)
src/packages/table/virtual/index.ts (2)
  • useThrottle (8-8)
  • useDebounce (8-8)
src/packages/table/demos/h5/demo9.tsx (1)
src/types/spec/table/base.ts (2)
  • TableColumnProps (5-14)
  • SortStateType (16-16)
src/packages/table/virtual/table-virtual.tsx (5)
src/utils/typings.ts (1)
  • ComponentDefaults (10-13)
src/types/spec/table/base.ts (1)
  • SortStateType (16-16)
src/hooks/use-props-value.ts (1)
  • usePropsValue (11-35)
src/packages/table/utils.ts (1)
  • useTableSticky (4-84)
src/packages/table/virtual/virtual-scroll.ts (1)
  • useVirtualScroll (41-264)
src/packages/table/index.virtual.ts (2)
src/packages/table/virtual/index.ts (1)
  • TableVirtual (1-1)
src/packages/table/virtual/table-virtual.tsx (1)
  • TableVirtual (489-491)
src/packages/table/table.tsx (1)
src/types/spec/table/base.ts (1)
  • SortStateType (16-16)
🪛 LanguageTool
.joycode/modes/rules-4k79ih5s/agentDefinition.md

[style] ~1-~1: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...nswers, and are brilliant at reasoning. You carefully provide accurate, factual, th...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[grammar] ~4-~4: There might be a mistake here.
Context: ...pseudocode, written out in great detail. - Confirm, then write code! - Always write...

(QB_NEW_EN)


[grammar] ~6-~6: There might be a mistake here.
Context: ... working code also it should be aligned to listed rules down below at Code Impleme...

(QB_NEW_EN)


[grammar] ~7-~7: There might be a mistake here.
Context: ...readability code, over being performant. - Fully implement all requested functional...

(QB_NEW_EN)


[grammar] ~9-~9: There might be a mistake here.
Context: ... todo’s, placeholders or missing pieces. - Ensure code is complete! Verify thorough...

(QB_NEW_EN)


[grammar] ~12-~12: There might be a mistake here.
Context: .... - Be concise Minimize any other prose. - If you think there might not be a correc...

(QB_NEW_EN)


[grammar] ~20-~20: There might be a mistake here.
Context: ...e following coding languages: - ReactJS - NextJS - JavaScript - TypeScript - Tailw...

(QB_NEW_EN)


[grammar] ~21-~21: There might be a mistake here.
Context: ...ng coding languages: - ReactJS - NextJS - JavaScript - TypeScript - TailwindCSS - ...

(QB_NEW_EN)


[grammar] ~22-~22: There might be a mistake here.
Context: ...guages: - ReactJS - NextJS - JavaScript - TypeScript - TailwindCSS - HTML - CSS #...

(QB_NEW_EN)


[grammar] ~23-~23: There might be a mistake here.
Context: ...actJS - NextJS - JavaScript - TypeScript - TailwindCSS - HTML - CSS ### Code Imple...

(QB_NEW_EN)


[grammar] ~24-~24: There might be a mistake here.
Context: ... - JavaScript - TypeScript - TailwindCSS - HTML - CSS ### Code Implementation Guid...

(QB_NEW_EN)


[grammar] ~25-~25: There might be a mistake here.
Context: ...Script - TypeScript - TailwindCSS - HTML - CSS ### Code Implementation Guidelines ...

(QB_NEW_EN)


[grammar] ~36-~36: There might be a mistake here.
Context: ... and on:keydown, and similar attributes. - Use consts instead of functions, for exa...

(QB_NEW_EN)

src/packages/table/doc.en-US.md

[grammar] ~177-~177: There might be a mistake here.
Context: ...y | Description | Type | Default Value | | --- | --- | --- | --- | | virtual | Wh...

(QB_NEW_EN)


[grammar] ~178-~178: There might be a mistake here.
Context: ...efault Value | | --- | --- | --- | --- | | virtual | Whether to enable virtual sc...

(QB_NEW_EN)


[grammar] ~179-~179: There might be a mistake here.
Context: ...irtual scrolling | boolean | false | | height | Table viewport height | `numb...

(QB_NEW_EN)


[grammar] ~180-~180: There might be a mistake here.
Context: ...ble viewport height | number | 300 | | rowHeight | Height of each row | `numb...

(QB_NEW_EN)


[grammar] ~181-~181: There might be a mistake here.
Context: ...| Height of each row | number | 40 | | overscan | Number of rows to preload |...

(QB_NEW_EN)


[grammar] ~182-~182: There might be a mistake here.
Context: ...er of rows to preload | number | 5 | | dynamicHeight | Whether to enable dyna...

(QB_NEW_EN)


[grammar] ~187-~187: There might be a mistake here.
Context: ...leRef | Property | Description | Type | | --- | --- | --- | | scrollToIndex | Me...

(QB_NEW_EN)


[grammar] ~188-~188: There might be a mistake here.
Context: ...Description | Type | | --- | --- | --- | | scrollToIndex | Method to scroll to a ...

(QB_NEW_EN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (10)
src/packages/table/demos/h5/demo15.tsx (1)

69-75: 请确认“同时固定表头和左列”的效果确实开启

当前仅设置了容器高度与左列 fixed。若表头吸顶不依赖显式 prop,此实现OK;若需要显式开关(例如 stickyHeader/scroll.y 等),请补充以确保示例名副其实。

src/packages/table/demo.tsx (1)

17-19: 新增示例接入位置合理

引入 Demo15/Virtual/NoVirtual 并在页面中分区展示,结构清晰。

Also applies to: 92-99

src/types/spec/table/base.ts (1)

16-16: 新增 SortStateType 定义清晰

三态类型简单明了,便于拓展。

src/packages/table/demos/h5/demo-no-virtual.tsx (1)

44-46: TableColumnProps.render 已定义第二个参数 index,TableVirtual 在 TableRow 中传入 rowIndex 正确,无需修改类型或实现

Likely an incorrect or invalid review comment.

src/packages/table/virtual/index.ts (1)

1-8: 导出聚合清晰,命名与类型拆分合理

当前导出结构简洁且易于 Tree-shaking,保持即可。

src/packages/table/table.scss (2)

73-81: 表头样式调整合理

为 th 增加 box-sizing、独立 background-color 与较高 z-index,与 sticky/fixed 列层级配合良好。


94-96: 为 td 增加 box-sizing: border-box 有助于宽度一致性

与指定 width 的列交互时更可预期,保留此变更。

src/packages/table/demos/h5/demo9.tsx (1)

48-61: 排序图标与回调签名更新合理

  • sorterIcon 基于 SortStateType 渲染图标,契合新 API。
  • onSort 回调签名与实现一致(含 sortedDatasortState)。

Also applies to: 64-70

src/packages/table/table.tsx (1)

135-171: 默认排序图标渲染逻辑清晰,可保留

列级 sorterIcon 优先,其次全局,再到默认图标,优先级符合预期。

Also applies to: 184-186

src/packages/table/virtual/hooks.ts (1)

10-15: 更新 useThrottle/useDebounce 签名为 void 返回类型
将泛型 T 约束为返回 void,且函数签名改为 (...args: Parameters) => void,以保证类型签名与实际实现一致,避免下游误用。已确认 virtual-scroll.ts 中唯一的调用(throttledScrollHandler)并未使用其返回值,变更安全。

Comment on lines +32 to +37
- Use early returns whenever possible to make the code more readable.
- Always use Tailwind classes for styling HTML elements; avoid using CSS or tags.
- Use “class:” instead of the tertiary operator in class tags whenever possible.
- Use descriptive variable and function/const names. Also, event functions should be named with a “handle” prefix, like “handleClick” for onClick and “handleKeyDown” for onKeyDown.
- Implement accessibility features on elements. For example, a tag should have a tabindex=“0”, aria-label, on:click, and on:keydown, and similar attributes.
- Use consts instead of functions, for example, “const toggle = () =>”. Also, define a type if possible.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

与本项目技术栈不符:Svelte 指令与 Tailwind-only 约束会误导 React/NutUI 贡献者

  • “class:”“on:click”“on:keydown”是 Svelte 语法,React 中应使用 className、onClick、onKeyDown,且 a11y 属性在 JSX 中为 tabIndex。
  • “始终使用 Tailwind,避免使用 CSS”与 NutUI React 现有样式体系(Less/主题变量、组件内样式约束)相冲突,易造成风格割裂、样式覆盖难以维护。

建议将规则改为 React/NutUI 友好版,并放宽 Tailwind-only 约束(可选用 clsx/twMerge 统一 className)。示例修订:

- - Always use Tailwind classes for styling HTML elements; avoid using CSS or tags.
- - Use “class:” instead of the tertiary operator in class tags whenever possible.
- - Implement accessibility features on elements. For example, a tag should have a tabindex=“0”, aria-label, on:click, and on:keydown, and similar attributes.
+ - 优先遵循本项目的样式体系(NutUI 变量/Less、组件约定)。在不冲突时可配合 Tailwind 使用,并通过 clsx/twMerge 组织 className。
+ - 在 React 中使用 className 与条件类工具(如 clsx/cn 或 twMerge),避免深层三元表达式。
+ - 实现无障碍:在 JSX 中使用 tabIndex、aria-* 属性,并使用 onClick/onKeyDown/onKeyUp 等事件处理器;必要时添加 role 与键盘交互逻辑。

如需,我可以提交一版与 NutUI 风格系统对齐的完整规则重写。

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Use early returns whenever possible to make the code more readable.
- Always use Tailwind classes for styling HTML elements; avoid using CSS or tags.
- Use “class:” instead of the tertiary operator in class tags whenever possible.
- Use descriptive variable and function/const names. Also, event functions should be named with a “handle” prefix, like “handleClick” for onClick and “handleKeyDown” for onKeyDown.
- Implement accessibility features on elements. For example, a tag should have a tabindex=“0”, aria-label, on:click, and on:keydown, and similar attributes.
- Use consts instead of functions, for example, “const toggle = () =>”. Also, define a type if possible.
- Use early returns whenever possible to make the code more readable.
- 优先遵循本项目的样式体系(NutUI 变量/Less、组件约定)。在不冲突时可配合 Tailwind 使用,并通过 clsx/twMerge 组织 className。
- 在 React 中使用 className 与条件类工具(如 clsx/cn 或 twMerge),避免深层三元表达式。
- 实现无障碍:在 JSX 中使用 tabIndex、aria-* 属性,并使用 onClick/onKeyDown/onKeyUp 等事件处理器;必要时添加 role 与键盘交互逻辑。
- Use descriptive variable and function/const names. Also, event functions should be named with a “handle” prefix, like “handleClick” for onClick and “handleKeyDown” for onKeyDown.
- Use consts instead of functions, for example, “const toggle = () =>”. Also, define a type if possible.
🧰 Tools
🪛 LanguageTool

[grammar] ~36-~36: There might be a mistake here.
Context: ... and on:keydown, and similar attributes. - Use consts instead of functions, for exa...

(QB_NEW_EN)

🤖 Prompt for AI Agents
.joycode/modes/rules-4k79ih5s/agentDefinition.md around lines 32 to 37: the
rules currently reference Svelte directives and enforce Tailwind-only styling
which conflicts with this repo’s React/NutUI stack; update the rule text to use
React/JSX equivalents (className instead of class:, onClick/onKeyDown instead of
on:click/on:keydown, tabIndex and aria-* casing for JSX), relax the
“Tailwind-only” constraint to allow NutUI’s Less/theme variables and component
styles (recommend using clsx or twMerge to compose className when Tailwind is
used), keep guidance on early returns, descriptive names and handler naming
(e.g., handleClick), prefer const arrow functions and adding types where
applicable, and note accessibility attributes should be applied in JSX form;
optionally offer to attach a full rewritten rule set aligned to NutUI styling
conventions.

@@ -0,0 +1 @@
[{"label":"一键安装环境","name":"Install","description":"专注于解决工作空间环境问题","prompt":"你是一位专门从事解决工作空间环境问题的全栈工程师和DevOps专家,你的主要任务是帮助用户诊断、修复和配置当前工作空间`/Users/jiangqi147/github/nutui-react`的开发环境。\n\n## 核心职责\n\n### 1. 环境检测与诊断\n- 自动扫描工作空间中的项目文件(package.json, requirements.txt, pom.xml, Gemfile, go.mod等)\n- 识别项目所需的运行环境和依赖\n- 检测当前系统已安装的环境版本\n- 分析环境配置冲突和兼容性问题\n\n### 2. 主流环境支持\n**Node.js生态系统:**\n- 检测和安装Node.js(如果用户没要求推荐LTS版本)\n- 配置npm/yarn/pnpm包管理器\n- 处理node_modules依赖问题\n- 解决版本冲突和权限问题\n\n**Python生态系统:**\n- 安装Python(2.x/3.x版本管理)\n- 配置pip包管理器和虚拟环境(venv/conda)\n- 处理requirements.txt依赖安装\n- 解决Python路径和模块导入问题\n\n**Java生态系统:**\n- 安装和配置JDK/JRE(版本选择和JAVA_HOME设置)\n- 配置Maven/Gradle构建工具\n- 处理依赖下载和仓库配置\n- 解决类路径和编译问题\n\n**其他主流环境:**\n- Go语言环境配置\n- Ruby和Rails环境\n- PHP和Composer\n- .NET Core环境\n- Docker容器化环境\n\n### 3. 项目启动与运行\n- 分析项目启动脚本和配置文件\n- 提供标准化的启动命令\n- 配置开发服务器和热重载\n- 设置环境变量和配置文件\n- 处理端口冲突和服务依赖\n\n### 4. 问题解决策略\n- 提供跨平台解决方案(Windows/macOS/Linux)\n- 给出详细的安装步骤和命令\n- 提供多种安装方式选择(官方安装器/包管理器/容器化)\n- 预防常见错误和最佳实践建议\n- 提供环境验证和测试方法\n\n### 5. 交互方式\n- 首先询问用户的操作系统和项目类型\n- 逐步引导用户完成环境配置\n- 提供可复制的命令和脚本\n- 在每个步骤后确认执行结果\n- 遇到问题时提供多种备选方案\n\n## 工作流程\n1. **环境扫描**:分析工作空间文件结构,识别项目类型\n2. **需求评估**:确定所需的运行环境和版本要求\n3. **现状检查**:检测当前已安装的环境和工具\n4. **差距分析**:对比需求与现状,列出缺失项\n5. **安装指导**:提供详细的安装和配置步骤\n6. **验证测试**:确保环境配置正确可用\n7. **项目启动**:协助用户成功启动项目\n\n请始终保持耐心和专业,用通俗易懂的语言解释技术概念,并在每个关键步骤提供清晰的指导。现在请开始分析当前工作空间的环境需求。\n"}] No newline at end of file
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

硬编码本地绝对路径不可移植,且可能泄露开发者用户名

/Users/jiangqi147/github/nutui-react 会在他人机器上失效,也会把个人用户名暴露到仓库历史。建议改为占位符或由运行环境注入工作区根路径。

- "…配置当前工作空间`/Users/jiangqi147/github/nutui-react`的开发环境。"
+ "…配置当前工作空间`${WORKSPACE_ROOT}`的开发环境。"

同时在执行时由宿主侧将 ${WORKSPACE_ROOT} 替换为实际路径,或在工具侧自动探测 repo 根目录。

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[{"label":"一键安装环境","name":"Install","description":"专注于解决工作空间环境问题","prompt":"你是一位专门从事解决工作空间环境问题的全栈工程师和DevOps专家,你的主要任务是帮助用户诊断、修复和配置当前工作空间`/Users/jiangqi147/github/nutui-react`的开发环境。\n\n## 核心职责\n\n### 1. 环境检测与诊断\n- 自动扫描工作空间中的项目文件(package.json, requirements.txt, pom.xml, Gemfile, go.mod等)\n- 识别项目所需的运行环境和依赖\n- 检测当前系统已安装的环境版本\n- 分析环境配置冲突和兼容性问题\n\n### 2. 主流环境支持\n**Node.js生态系统:**\n- 检测和安装Node.js(如果用户没要求推荐LTS版本)\n- 配置npm/yarn/pnpm包管理器\n- 处理node_modules依赖问题\n- 解决版本冲突和权限问题\n\n**Python生态系统:**\n- 安装Python(2.x/3.x版本管理)\n- 配置pip包管理器和虚拟环境(venv/conda)\n- 处理requirements.txt依赖安装\n- 解决Python路径和模块导入问题\n\n**Java生态系统:**\n- 安装和配置JDK/JRE(版本选择和JAVA_HOME设置)\n- 配置Maven/Gradle构建工具\n- 处理依赖下载和仓库配置\n- 解决类路径和编译问题\n\n**其他主流环境:**\n- Go语言环境配置\n- Ruby和Rails环境\n- PHP和Composer\n- .NET Core环境\n- Docker容器化环境\n\n### 3. 项目启动与运行\n- 分析项目启动脚本和配置文件\n- 提供标准化的启动命令\n- 配置开发服务器和热重载\n- 设置环境变量和配置文件\n- 处理端口冲突和服务依赖\n\n### 4. 问题解决策略\n- 提供跨平台解决方案(Windows/macOS/Linux)\n- 给出详细的安装步骤和命令\n- 提供多种安装方式选择(官方安装器/包管理器/容器化)\n- 预防常见错误和最佳实践建议\n- 提供环境验证和测试方法\n\n### 5. 交互方式\n- 首先询问用户的操作系统和项目类型\n- 逐步引导用户完成环境配置\n- 提供可复制的命令和脚本\n- 在每个步骤后确认执行结果\n- 遇到问题时提供多种备选方案\n\n## 工作流程\n1. **环境扫描**:分析工作空间文件结构,识别项目类型\n2. **需求评估**:确定所需的运行环境和版本要求\n3. **现状检查**:检测当前已安装的环境和工具\n4. **差距分析**:对比需求与现状,列出缺失项\n5. **安装指导**:提供详细的安装和配置步骤\n6. **验证测试**:确保环境配置正确可用\n7. **项目启动**:协助用户成功启动项目\n\n请始终保持耐心和专业,用通俗易懂的语言解释技术概念,并在每个关键步骤提供清晰的指导。现在请开始分析当前工作空间的环境需求。\n"}]
[{"label":"一键安装环境","name":"Install","description":"专注于解决工作空间环境问题","prompt":"你是一位专门从事解决工作空间环境问题的全栈工程师和DevOps专家,你的主要任务是帮助用户诊断、修复和配置当前工作空间`${WORKSPACE_ROOT}`的开发环境。\n\n## 核心职责\n\n### 1. 环境检测与诊断\n- 自动扫描工作空间中的项目文件(package.json, requirements.txt, pom.xml, Gemfile, go.mod等)\n- 识别项目所需的运行环境和依赖\n- 检测当前系统已安装的环境版本\n- 分析环境配置冲突和兼容性问题\n\n### 2. 主流环境支持\n**Node.js生态系统:**\n- 检测和安装Node.js(如果用户没要求推荐LTS版本)\n- 配置npm/yarn/pnpm包管理器\n- 处理node_modules依赖问题\n- 解决版本冲突和权限问题\n\n**Python生态系统:**\n- 安装Python(2.x/3.x版本管理)\n- 配置pip包管理器和虚拟环境(venv/conda)\n- 处理requirements.txt依赖安装\n- 解决Python路径和模块导入问题\n\n**Java生态系统:**\n- 安装和配置JDK/JRE(版本选择和JAVA_HOME设置)\n- 配置Maven/Gradle构建工具\n- 处理依赖下载和仓库配置\n- 解决类路径和编译问题\n\n**其他主流环境:**\n- Go语言环境配置\n- Ruby和Rails环境\n- PHP和Composer\n- .NET Core环境\n- Docker容器化环境\n\n### 3. 项目启动与运行\n- 分析项目启动脚本和配置文件\n- 提供标准化的启动命令\n- 配置开发服务器和热重载\n- 设置环境变量和配置文件\n- 处理端口冲突和服务依赖\n\n### 4. 问题解决策略\n- 提供跨平台解决方案(Windows/macOS/Linux)\n- 给出详细的安装步骤和命令\n- 提供多种安装方式选择(官方安装器/包管理器/容器化)\n- 预防常见错误和最佳实践建议\n- 提供环境验证和测试方法\n\n### 5. 交互方式\n- 首先询问用户的操作系统和项目类型\n- 逐步引导用户完成环境配置\n- 提供可复制的命令和脚本\n- 在每个步骤后确认执行结果\n- 遇到问题时提供多种备选方案\n\n## 工作流程\n1. **环境扫描**:分析工作空间文件结构,识别项目类型\n2. **需求评估**:确定所需的运行环境和版本要求\n3. **现状检查**:检测当前已安装的环境和工具\n4. **差距分析**:对比需求与现状,列出缺失项\n5. **安装指导**:提供详细的安装和配置步骤\n6. **验证测试**:确保环境配置正确可用\n7. **项目启动**:协助用户成功启动项目\n\n请始终保持耐心和专业,用通俗易懂的语言解释技术概念,并在每个关键步骤提供清晰的指导。现在请开始分析当前工作空间的环境需求。\n"}]
🤖 Prompt for AI Agents
.joycode/prompt.json lines 1-1: The prompt hardcodes a local absolute path
(/Users/jiangqi147/github/nutui-react), which is non-portable and exposes a
username; replace it with a placeholder or inject the workspace root at runtime.
Update the prompt text to use a variable like ${WORKSPACE_ROOT} or
<WORKSPACE_ROOT> (and document that the host will replace it), and add fallback
logic in tooling to auto-detect the repository root if the variable is not
provided so the prompt remains portable and safe for other contributors.

Comment on lines +15 to +18
const DemoVirtual = () => {
// 创建表格引用
const tableRef = useRef<VirtualTableRef>(null)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

ref 未绑定导致示例中 scrollToIndex 无法使用

已创建 tableRef,但未通过 ref 传给 <TableVirtual />;示例内的 handleScrollToRow 永远拿不到实例。请至少将 ref 绑定到组件。

应用以下 diff 修复:

-  const tableRef = useRef<VirtualTableRef>(null)
+  const tableRef = useRef<VirtualTableRef>(null)
@@
-    <TableVirtual
+    <TableVirtual
+      ref={tableRef}
       columns={columns}
       data={data}
       virtual
       height={400}
       rowHeight={40} // 默认行高,实际会根据内容动态调整
       overscan={10}
       bordered
       dynamicHeight // 启用动态高度
     />

Also applies to: 109-119

🤖 Prompt for AI Agents
In src/packages/table/demos/h5/demo-virtual.tsx around lines 15-18 (and also
apply same fix at lines 109-119): a useRef for VirtualTableRef is created but
never passed to the <TableVirtual /> component, so handlers like
handleScrollToRow cannot access the instance; bind the ref by passing
ref={tableRef} (or the correct forwardedRef prop name) into the <TableVirtual />
JSX where the table is rendered so the ref is attached and instance methods
(e.g., scrollToIndex) become available to the handlers.

Comment on lines +169 to +171
```tsx
import { TableVirtual } from '@nutui/nutui-react'
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

验证:包根是否已导出 TableVirtual(与文档导入路径一致)

文档使用 import { TableVirtual } from '@nutui/nutui-react',请确认根入口已 re-export。


🏁 Script executed:

#!/usr/bin/env bash
# 查找任何导出 TableVirtual 的根级入口
rg -nP -C2 '\bexport\s+{[^}]*\bTableVirtual\b' src || true
# 查找是否有从 table/index.virtual 再导出到包根
rg -nP -C2 'export\s+\*\s+from.+table\/index\.virtual' src || true

Length of output: 407


确保在包根入口文件导出 TableVirtual

文档中示例使用了

import { TableVirtual } from '@nutui/nutui-react'

但在包根入口(如 src/index.ts 或主导出文件)中未找到对 TableVirtual 的 re-export。请在入口加入:

export { TableVirtual } from './packages/table/virtual';
🤖 Prompt for AI Agents
In src/packages/table/doc.en-US.md around lines 169 to 171, the docs show
importing TableVirtual from '@nutui/nutui-react' but the package root does not
re-export TableVirtual; update the package root export file (e.g., src/index.ts
or your library's main export file) to add a re-export for the virtual table
component so consumers can import it from the library root, specifically
re-export the component from its source path (./packages/table/virtual) and
ensure the export is included in the library build/exports map.

Comment on lines +165 to +184
## TableVirtual

### 引入

```tsx
import { TableVirtual } from '@nutui/nutui-react'
```

### Props

继承 Table 组件的所有属性,并新增以下属性:

| 属性 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| virtual | 是否启用虚拟滚动 | `boolean` | `false` |
| height | 表格可视区域高度 | `number` | `300` |
| rowHeight | 每行高度 | `number` | `40` |
| overscan | 预加载的行数 | `number` | `5` |
| dynamicHeight | 是否启用动态高度(如果为true,将尝试获取每行的实际高度) | `boolean` | `false` |

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

确认 VirtualTable Props 未暴露 props 版的 scrollToIndex

实现中 ref 暴露 scrollToIndex,props 形态会引起误用(见组件实现)。文档当前未列出 props 版,保持如此是正确的;请确保后续不添加该 props,以免与 ref 冲突。

建议运行脚本核对导出与文档一致:


🏁 Script executed:

#!/bin/bash
# 校验是否存在 props 版 scrollToIndex
rg -nP "scrollToIndex\s*\?:\s*\(" src/packages/table -C2
# 校验对外导出的类型/组件
rg -nP "export\s+\{[^}]*TableVirtual|VirtualTableRef|TableColumnProps|SortStateType" -C2

Length of output: 39529


移除 VirtualTableProps 中的 scrollToIndex

该方法仅应通过 ref 暴露,props 接口不应包含 scrollToIndex,请在 src/packages/table/virtual/table-virtual.tsxVirtualTableProps 中删除该属性声明,并确保文档和导出类型保持一致。

🤖 Prompt for AI Agents
In src/packages/table/doc.md around lines 165 to 184 and in
src/packages/table/virtual/table-virtual.tsx (VirtualTableProps), remove the
scrollToIndex property from the VirtualTableProps type/interface so it is no
longer declared on props, implement scrollToIndex only via the component ref
API, update any exported types to reflect the removal, and update the doc.md to
not list scrollToIndex in the props table (ensuring typed exports and docs
remain consistent).

Comment on lines +203 to +258
const handleSorterClick = (item: TableColumnProps) => {
if (!item.sorter) return

// 获取当前排序状态,如果不存在则默认为 null(不排序)
const currentSortState = sortedMapping.current[item.key] || null

// 根据当前状态确定下一个状态:null -> asc -> desc -> null
let nextSortState: 'asc' | 'desc' | null
if (currentSortState === null) {
nextSortState = 'asc' // 默认不排序 -> 升序
} else if (currentSortState === 'asc') {
nextSortState = 'desc' // 升序 -> 降序
} else {
nextSortState = null // 降序 -> 不排序
}

// 更新排序状态
sortedMapping.current[item.key] = nextSortState

// 根据排序状态执行相应的排序操作
if (nextSortState === null) {
// 不排序,恢复原始数据
setValue(data)
onSort && onSort(item)
} else {
const copied = [...innerValue]
if (typeof item.sorter === 'function') {
// 使用自定义排序函数
if (nextSortState === 'asc') {
copied.sort(item.sorter as (a: any, b: any) => number)
} else {
// 降序:交换排序函数的参数顺序
copied.sort(
(a, b) => -(item.sorter as (a: any, b: any) => number)(a, b)
)
}
} else if (item.sorter === 'default') {
// 默认排序
if (nextSortState === 'asc') {
copied.sort()
} else {
copied.sort().reverse()
}
} else if (item.sorter === true) {
// 简单排序,根据列的 key 值进行排序
const key = item.key
if (nextSortState === 'asc') {
copied.sort((a, b) => (a[key] > b[key] ? 1 : -1))
} else {
copied.sort((a, b) => (a[key] > b[key] ? -1 : 1))
}
}
setValue(copied, true)
onSort && onSort(item, copied, nextSortState)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

排序:与 Table 保持一致的修复(单列高亮、'default' 行为与相等值返回 0)

这里与非虚拟表相同的问题,需要一并修复。

-    // 更新排序状态
-    sortedMapping.current[item.key] = nextSortState
+    // 更新排序状态:只保留当前列
+    sortedMapping.current = {}
+    sortedMapping.current[item.key] = nextSortState
@@
-      } else if (item.sorter === 'default') {
-        // 默认排序
-        if (nextSortState === 'asc') {
-          copied.sort()
-        } else {
-          copied.sort().reverse()
-        }
+      } else if (item.sorter === 'default') {
+        // 默认排序:等同于 true,按列值排序
+        const key = item.key
+        if (nextSortState === 'asc') {
+          copied.sort((a, b) => (a[key] === b[key] ? 0 : a[key] > b[key] ? 1 : -1))
+        } else {
+          copied.sort((a, b) => (a[key] === b[key] ? 0 : a[key] > b[key] ? -1 : 1))
+        }
@@
-          copied.sort((a, b) => (a[key] > b[key] ? 1 : -1))
+          copied.sort((a, b) => (a[key] === b[key] ? 0 : a[key] > b[key] ? 1 : -1))
@@
-          copied.sort((a, b) => (a[key] > b[key] ? -1 : 1))
+          copied.sort((a, b) => (a[key] === b[key] ? 0 : a[key] > b[key] ? -1 : 1))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleSorterClick = (item: TableColumnProps) => {
if (!item.sorter) return
// 获取当前排序状态,如果不存在则默认为 null(不排序)
const currentSortState = sortedMapping.current[item.key] || null
// 根据当前状态确定下一个状态:null -> asc -> desc -> null
let nextSortState: 'asc' | 'desc' | null
if (currentSortState === null) {
nextSortState = 'asc' // 默认不排序 -> 升序
} else if (currentSortState === 'asc') {
nextSortState = 'desc' // 升序 -> 降序
} else {
nextSortState = null // 降序 -> 不排序
}
// 更新排序状态
sortedMapping.current[item.key] = nextSortState
// 根据排序状态执行相应的排序操作
if (nextSortState === null) {
// 不排序,恢复原始数据
setValue(data)
onSort && onSort(item)
} else {
const copied = [...innerValue]
if (typeof item.sorter === 'function') {
// 使用自定义排序函数
if (nextSortState === 'asc') {
copied.sort(item.sorter as (a: any, b: any) => number)
} else {
// 降序:交换排序函数的参数顺序
copied.sort(
(a, b) => -(item.sorter as (a: any, b: any) => number)(a, b)
)
}
} else if (item.sorter === 'default') {
// 默认排序
if (nextSortState === 'asc') {
copied.sort()
} else {
copied.sort().reverse()
}
} else if (item.sorter === true) {
// 简单排序,根据列的 key 值进行排序
const key = item.key
if (nextSortState === 'asc') {
copied.sort((a, b) => (a[key] > b[key] ? 1 : -1))
} else {
copied.sort((a, b) => (a[key] > b[key] ? -1 : 1))
}
}
setValue(copied, true)
onSort && onSort(item, copied, nextSortState)
}
}
const handleSorterClick = (item: TableColumnProps) => {
if (!item.sorter) return
// 获取当前排序状态,如果不存在则默认为 null(不排序)
const currentSortState = sortedMapping.current[item.key] || null
// 根据当前状态确定下一个状态:null -> asc -> desc -> null
let nextSortState: 'asc' | 'desc' | null
if (currentSortState === null) {
nextSortState = 'asc' // 默认不排序 -> 升序
} else if (currentSortState === 'asc') {
nextSortState = 'desc' // 升序 -> 降序
} else {
nextSortState = null // 降序 -> 不排序
}
// 更新排序状态:只保留当前列
sortedMapping.current = {}
sortedMapping.current[item.key] = nextSortState
// 根据排序状态执行相应的排序操作
if (nextSortState === null) {
// 不排序,恢复原始数据
setValue(data)
onSort && onSort(item)
} else {
const copied = [...innerValue]
if (typeof item.sorter === 'function') {
// 使用自定义排序函数
if (nextSortState === 'asc') {
copied.sort(item.sorter as (a: any, b: any) => number)
} else {
// 降序:交换排序函数的参数顺序
copied.sort(
(a, b) => -(item.sorter as (a: any, b: any) => number)(a, b)
)
}
} else if (item.sorter === 'default') {
// 默认排序:等同于 true,按列值排序
const key = item.key
if (nextSortState === 'asc') {
copied.sort((a, b) => (a[key] === b[key] ? 0 : a[key] > b[key] ? 1 : -1))
} else {
copied.sort((a, b) => (a[key] === b[key] ? 0 : a[key] > b[key] ? -1 : 1))
}
} else if (item.sorter === true) {
// 简单排序,根据列的 key 值进行排序
const key = item.key
if (nextSortState === 'asc') {
copied.sort((a, b) => (a[key] === b[key] ? 0 : a[key] > b[key] ? 1 : -1))
} else {
copied.sort((a, b) => (a[key] === b[key] ? 0 : a[key] > b[key] ? -1 : 1))
}
}
setValue(copied, true)
onSort && onSort(item, copied, nextSortState)
}
}
🤖 Prompt for AI Agents
In src/packages/table/virtual/table-virtual.tsx around lines 203 to 258:
currently sorting toggles per-column but does not clear other columns' sort
state (so single-column highlight isn't enforced), uses negation of custom
sorter for desc (which can break comparators and 0 results), and uses
copied.sort()/reverse for 'default' which doesn't return 0 for equal values. Fix
by: when setting nextSortState for item.key, clear all other keys in
sortedMapping.current so only this column is highlighted; for custom sorter,
call the sorter with reversed arguments for desc (i.e. sorter(b,a)) instead of
negating the return value; for 'default' and key-based true sorter, use an
explicit comparator that returns 1/0/-1 (e.g. a[key] > b[key] ? 1 : a[key] <
b[key] ? -1 : 0) and apply that for asc and reverse the comparator for desc;
keep restoring original data when nextSortState is null and call setValue/onSort
as before.

Comment on lines +117 to +147
// 动态高度的计算
let currentHeight = 0
let startIndex = 0
let endIndex = 0

// 找到起始索引
for (let i = 0; i < total; i++) {
const rowHeight = heightCache[i] || itemHeight
if (currentHeight + rowHeight > currentScrollTop) {
startIndex = Math.max(0, i - overscan)
break
}
currentHeight += rowHeight
}

// 找到结束索引
currentHeight = 0
for (let i = 0; i < total; i++) {
const rowHeight = heightCache[i] || itemHeight
currentHeight += rowHeight
if (currentHeight > currentScrollTop + viewportHeight) {
endIndex = Math.min(total - 1, i + overscan)
break
}

// 如果到达最后一行,设置结束索引为最后一行
if (i === total - 1) {
endIndex = total - 1
}
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

动态高度可见区计算为 O(n)×2,长列表下性能风险较大

二次从 0 扫描 total。建议基于前缀和数组做二分查找,或从 startIndex 开始单向累加计算 endIndex,均可降为 O(log n) 或 O(k)。

可先做轻量修正:从 startIndex 开始计算 endIndex,避免第二次从 0 扫描。

-    // 找到结束索引
-    currentHeight = 0
-    for (let i = 0; i < total; i++) {
-      const rowHeight = heightCache[i] || itemHeight
-      currentHeight += rowHeight
-      if (currentHeight > currentScrollTop + viewportHeight) {
-        endIndex = Math.min(total - 1, i + overscan)
-        break
-      }
-      if (i === total - 1) {
-        endIndex = total - 1
-      }
-    }
+    // 从 startIndex 开始计算结束索引,避免 O(n) 级全表扫描
+    currentHeight = 0
+    for (let i = startIndex; i < total; i++) {
+      const rowHeight = heightCache[i] || itemHeight
+      currentHeight += rowHeight
+      if (currentHeight > viewportHeight) {
+        endIndex = Math.min(total - 1, i + overscan)
+        break
+      }
+      if (i === total - 1) {
+        endIndex = total - 1
+      }
+    }

如需进一步优化,我可提供前缀和 + 二分版本。

🤖 Prompt for AI Agents
In src/packages/table/virtual/virtual-scroll.ts around lines 117 to 147, the
code scans from 0 twice to compute startIndex and endIndex causing O(n)×2 work;
change the second scan to begin at startIndex and initialize currentHeight to
the cumulative height at startIndex so endIndex is found by a single forward
accumulation from startIndex until currentHeight exceeds currentScrollTop +
viewportHeight (adding overscan and clamping to bounds). Concretely: after
finding startIndex, set currentHeight to the sum of heights of rows
0..startIndex-1 (or maintain the already computed currentHeight from the first
loop), then loop i from startIndex to total-1 accumulating row heights and break
when the viewport bottom is reached, setting endIndex = Math.min(total - 1, i +
overscan). This avoids the second full scan and reduces work to O(k); for
further optimization replace with prefix-sum + binary search later.

Comment on lines +196 to +203
const onScroll = (event: React.UIEvent) => {
// 获取当前滚动位置
const scrollContainer = event.target as HTMLDivElement
const currentScrollTop = scrollContainer.scrollTop

// 使用节流函数处理滚动更新
throttledScrollHandler(currentScrollTop)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

onScroll 使用 event.target 取滚动容器不可靠,应使用 currentTarget

target 可能指向子元素,导致读取错误的 scrollTop。

-  const onScroll = (event: React.UIEvent) => {
+  const onScroll = (event: React.UIEvent<HTMLDivElement>) => {
     // 获取当前滚动位置
-    const scrollContainer = event.target as HTMLDivElement
+    const scrollContainer = event.currentTarget as HTMLDivElement
     const currentScrollTop = scrollContainer.scrollTop
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const onScroll = (event: React.UIEvent) => {
// 获取当前滚动位置
const scrollContainer = event.target as HTMLDivElement
const currentScrollTop = scrollContainer.scrollTop
// 使用节流函数处理滚动更新
throttledScrollHandler(currentScrollTop)
}
const onScroll = (event: React.UIEvent<HTMLDivElement>) => {
// 获取当前滚动位置
const scrollContainer = event.currentTarget as HTMLDivElement
const currentScrollTop = scrollContainer.scrollTop
// 使用节流函数处理滚动更新
throttledScrollHandler(currentScrollTop)
}
🤖 Prompt for AI Agents
In src/packages/table/virtual/virtual-scroll.ts around lines 196 to 203,
onScroll currently reads scroll position from event.target which can be a child
element; change it to use event.currentTarget (typed as HTMLDivElement or set
onScroll parameter to React.UIEvent<HTMLDivElement>) and read
currentTarget.scrollTop, then pass that value to throttledScrollHandler so the
scroll position is always taken from the actual scroll container.

Comment on lines +206 to +229
const scrollTo = (index: number) => {
if (containerRef.current) {
const targetIndex = Math.min(Math.max(0, index), totalRef.current - 1) // 确保不会滚动到超出范围的位置

let targetScrollTop = 0
if (!dynamicHeight) {
targetScrollTop = targetIndex * itemHeight
} else {
// 计算目标位置的滚动偏移
for (let i = 0; i < targetIndex; i++) {
targetScrollTop += heightCache[i] || itemHeight
}
}

// 设置DOM元素的滚动位置
containerRef.current.scrollTop = targetScrollTop

// 更新ref值
scrollTopRef.current = targetScrollTop

// 触发状态更新以重新渲染
setScrollTopState(targetScrollTop)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

scrollTo 在 total=0 时会计算出 -1 索引,可能写入负的 scrollTop

需要边界保护。

   const scrollTo = (index: number) => {
     if (containerRef.current) {
-      const targetIndex = Math.min(Math.max(0, index), totalRef.current - 1) // 确保不会滚动到超出范围的位置
+      if (totalRef.current <= 0) {
+        containerRef.current.scrollTop = 0
+        scrollTopRef.current = 0
+        setScrollTopState(0)
+        return
+      }
+      const targetIndex = Math.min(Math.max(0, index), totalRef.current - 1)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const scrollTo = (index: number) => {
if (containerRef.current) {
const targetIndex = Math.min(Math.max(0, index), totalRef.current - 1) // 确保不会滚动到超出范围的位置
let targetScrollTop = 0
if (!dynamicHeight) {
targetScrollTop = targetIndex * itemHeight
} else {
// 计算目标位置的滚动偏移
for (let i = 0; i < targetIndex; i++) {
targetScrollTop += heightCache[i] || itemHeight
}
}
// 设置DOM元素的滚动位置
containerRef.current.scrollTop = targetScrollTop
// 更新ref值
scrollTopRef.current = targetScrollTop
// 触发状态更新以重新渲染
setScrollTopState(targetScrollTop)
}
}
const scrollTo = (index: number) => {
if (containerRef.current) {
// Guard against empty list (total = 0) to avoid negative scrollTop
if (totalRef.current <= 0) {
containerRef.current.scrollTop = 0
scrollTopRef.current = 0
setScrollTopState(0)
return
}
const targetIndex = Math.min(
Math.max(0, index),
totalRef.current - 1
) // 确保不会滚动到超出范围的位置
let targetScrollTop = 0
if (!dynamicHeight) {
targetScrollTop = targetIndex * itemHeight
} else {
// 计算目标位置的滚动偏移
for (let i = 0; i < targetIndex; i++) {
targetScrollTop += heightCache[i] || itemHeight
}
}
// 设置DOM元素的滚动位置
containerRef.current.scrollTop = targetScrollTop
// 更新ref值
scrollTopRef.current = targetScrollTop
// 触发状态更新以重新渲染
setScrollTopState(targetScrollTop)
}
}
🤖 Prompt for AI Agents
In src/packages/table/virtual/virtual-scroll.ts around lines 206 to 229,
scrollTo can compute targetIndex as Math.min(Math.max(0, index),
totalRef.current - 1) which becomes -1 when totalRef.current is 0 and leads to a
negative scrollTop; fix by early-returning when totalRef.current <= 0 (or set
targetIndex = 0 if you prefer no-op), ensuring you never use totalRef.current -
1 when total is zero, and keep the rest of the logic unchanged (this prevents
the for-loop and scrollTop calculations from running with an invalid negative
targetIndex).

Comment on lines +9 to 10
sorterIcon?: (currentSortState: SortStateType) => ReactNode
sorter?: ((a: any, b: any) => number) | boolean | string
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

列级 sorterIcon 为函数,但表格级 sorterIcon 仍为 ReactNode,API 不一致

建议统一两处 API 形态,表格级也支持函数形式作为默认渲染器,避免歧义与重复命名造成的困惑。

针对列级命名,也可考虑改为 renderSorterIcon 以与节点型 props 区分(可选)。

-  sorterIcon?: (currentSortState: SortStateType) => ReactNode
+  sorterIcon?: (currentSortState: SortStateType) => ReactNode
+  // 可选:rename 为 renderSorterIcon 以避免与表格级 sorterIcon 混淆

同时,建议调整 BaseTable.sorterIcon 的类型(该行不在本次变更范围,示意如下):

-  sorterIcon?: ReactNode
+  sorterIcon?: ReactNode | ((currentSortState: SortStateType) => ReactNode)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
sorterIcon?: (currentSortState: SortStateType) => ReactNode
sorter?: ((a: any, b: any) => number) | boolean | string
sorterIcon?: (currentSortState: SortStateType) => ReactNode
// 可选:rename 为 renderSorterIcon 以避免与表格级 sorterIcon 混淆
sorter?: ((a: any, b: any) => number) | boolean | string
🤖 Prompt for AI Agents
In src/types/spec/table/base.ts around lines 9 to 10, the column-level
sorterIcon is typed as a function (currentSortState: SortStateType) => ReactNode
while the table-level sorterIcon remains a ReactNode, causing an inconsistent
API; update the table-level sorterIcon type to also accept a function signature
(currentSortState: SortStateType) => ReactNode as the default renderer so both
levels share the same shape, and optionally consider renaming the column prop to
renderSorterIcon to distinguish render-function props from node props (ensure
any references/types elsewhere are updated accordingly).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.x Target branch 3.x action:review This PR needs more reviews (less than 2 approvals) size/XXL
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant