Skip to content

Conversation

QdabuliuQ
Copy link

@QdabuliuQ QdabuliuQ commented Aug 20, 2025

🤔 This is a ...

  • 🆕 New feature

🔗 Related Issues

ant-design/ant-design#54204

💡 Background and Solution

tour would like to support configuring offset separately
Add the [number, number][] type to the offset in the useTarget hook

📝 Change Log

Language Changelog
🇺🇸 English gap.offset Added the type [number, number][]
🇨🇳 Chinese gap.offset 新增 [number, number][] 类型

Summary by CodeRabbit

  • 新功能

    • 支持 gap.offset 接受按步骤数组,可为每一步设置不同的遮罩偏移并随步骤切换生效。
    • 导出新的 TypeScript 类型 TourRef,可通过 ref 调用 next/prev/close/finish 等方法。
  • 文档

    • 新增 “Offset” 章节与示例,演示按步骤设置偏移的用法。
  • 测试

    • 新增用例覆盖 gap.offset 数组场景及 TourRef 导出。

Copy link

vercel bot commented Aug 20, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
tour Ready Ready Preview Comment Aug 21, 2025 8:49am

Copy link

coderabbitai bot commented Aug 20, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Warning

Rate limit exceeded

@QdabuliuQ has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 12 minutes and 46 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between a9bd27e and 8ff9c28.

📒 Files selected for processing (1)
  • tests/index.test.tsx (1 hunks)

Walkthrough

在文档中新增“Gap Offset 数组”示例;核心实现扩展 useTarget 支持按步骤的 gap.offset 数组并新增 DEFAULT_GAP_OFFSET 与 current 参数;Tour 将 mergedCurrent 传给 useTarget;导出类型新增 TourRef,interface 中 data-* 类型改为 Record;新增对应测试。

Changes

Cohort / File(s) Change Summary
Docs
docs/demo/gap.md
新增 “Offset” 小节并嵌入示例 gap-offset-array.tsx,仅文档。
Examples
docs/examples/gap-offset-array.tsx
新增示例文件:展示 gap={{ offset: [[10,10],[20,20],[30,30]] }} 的用法;导出默认 App。
Hook: useTarget 扩展
src/hooks/useTarget.ts
Gap.offset 扩展为 `number
Component: Tour 参数传递
src/Tour.tsx
将内部 mergedCurrent 传入 useTarget(useTarget(..., current=mergedCurrent));调用点更新以传递当前步骤索引。
Types & Exports
src/index.tsx, src/interface.ts
src/index.tsx 新增导出类型 TourRefsrc/interface.tsHTMLAriaDataAttributes 从索引签名改为 Record<\data-${string}`, unknown>`(等价的类型表示)。
Tests
tests/index.test.tsx
新增测试:验证 gap.offset 为数组时,随着步骤变化遮罩矩形尺寸;测试中导入并使用新导出类型 TourRef

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant App
  participant Tour
  participant useTarget
  participant TourStep

  App->>Tour: render(steps, gap.offset = array)
  Note over Tour: 管理 mergedCurrent(当前步骤索引)
  Tour->>useTarget: useTarget(targetRef, placeholderRef, current=mergedCurrent, gap)
  useTarget-->>Tour: 返回位置/遮罩(按 current 选取 offset)
  Tour->>TourStep: render 当前步骤

  TourStep->>Tour: onNext/onPrev/onClose/onFinish
  Tour->>Tour: handleNext/handlePrev/handleClose/handleFinish
  alt 步骤变更
    Tour->>useTarget: useTarget(..., current=新的 mergedCurrent)
    useTarget-->>Tour: 重新计算位置(offset 随步骤变化)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • afc163
  • zombieJ

Poem

小兔跳步寻偏移,三处间隙排成队,
钩子记住每一步,Tour 紧随合并意。
文档示例新登场,测试轻敲保无碍,
一跳一行乐开怀。 🐇✨

✨ 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 @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai 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:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai 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 @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @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.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @QdabuliuQ, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant enhancement to the Tour component, allowing developers to specify different gap offsets for each step of a tour. This is achieved by extending the gap.offset property to accept an array of [number, number] tuples. Additionally, the Tour component now exposes an imperative handle (TourRef) with methods to programmatically control the tour's navigation and state.

Highlights

  • Flexible Gap Offsets: The gap.offset property now supports an array of [number, number] tuples, enabling distinct horizontal and vertical offsets for the highlight area of each individual tour step.
  • Programmatic Tour Control: A new TourRef interface and useImperativeHandle implementation provide methods (next, prev, close, finish) to programmatically navigate and manage the tour's lifecycle.
  • New Demo and Documentation: A dedicated demo (gap-offset-array.tsx) has been added to illustrate the new array offset functionality, and the README.md has been updated to document the TourInstance (TourRef) methods.
  • Refactored Tour Logic: Internal Tour component logic has been refactored to integrate the imperative handle and correctly apply step-specific gap offsets.
  • Comprehensive Test Coverage: New test cases have been added to ensure the correct behavior of both the TourRef methods and the dynamic gap.offset application across tour steps.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces two main features: the ability to specify an array of offsets for different tour steps, and a ref to programmatically control the tour (next, prev, close, finish). The changes look good overall, with new types, logic, documentation, and tests for these features.

However, I've found a few issues:

  • There are some undocumented changes in the TourStepInfo interface, such as making title optional and adding contentRender, which could be a breaking change or a new feature that needs documentation.
  • There's a redundant property definition in the interfaces.
  • The new test for the ref functionality has a flaw in its assertions.
  • Some comments in the code are in Chinese, which should be translated to English for consistency.

I've left specific comments with suggestions for each of these points.

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: 2

🧹 Nitpick comments (9)
src/hooks/useTarget.ts (2)

84-106: getGapOffset 分支覆盖完整,但可以适度收敛以提升可读性。

当前写法正确,已覆盖 number、[x,y]、Array<[x,y]> 三形态。可考虑将其收敛为“解析为元组”的一步,减少索引位两次查找的重复调用。

建议(保留现有语义不变):

  • 增加一个 resolveGapOffsetTuple(): [number, number],在内部完成三形态归一化(含 current 与默认回退)。
  • 在计算位置信息处只解一次元组,避免重复调用两次 getGapOffset(0)getGapOffset(1)

如需,我可以提交一个等价重构 PR。


25-34: 类型小建议:返回值与内部状态可能为 null,建议补全联合类型。

posInfotargetElement 在运行期都可能为 null,当前签名是 [PosInfo, HTMLElement]。为类型精确性可调整为:

可能的签名更新(注意会连带影响调用方类型):

-export default function useTarget(...): [PosInfo, HTMLElement] {
+export default function useTarget(...): [PosInfo | null, HTMLElement | null] {

如不计划在本 PR 处理,可留待后续统一处理类型精确性。

src/interface.ts (2)

19-24: 公开 TourRef 合理,契合 forwardRef 暴露的命令式 API。

next/prev/close/finish 四个方法覆盖主要用例。建议在 README 中统一称谓为 TourRef(或新增 export type TourInstance = TourRef 作为别名以匹配文档术语)。

可选增强(便于对齐 README 用语):

 export interface TourRef {
   next: () => void;
   prev: () => void;
   close: () => void;
   finish: () => void;
 }
+
+// 便于与 README 的“TourInstance”术语对齐
+export type TourInstance = TourRef;

29-45: 将 title 调整为可选 + 新增 contentRender:请同步补充到 README API。

  • title?: ReactNode 放宽了约束,合理。
  • 新增 contentRender(next, prev, close, finish) 能力很有用,但 README 未见对应说明。

我可以补一段 README “TourStep/TourStepProps” 的 API 表格行,描述 contentRender 的签名与用途,是否需要我按你们文档风格直接给出文案与示例?

src/Tour.tsx (2)

80-101: 事件处理内聚清晰;建议为越界场景加护栏。

当前 handlePrev/handleNext 直接 +-1,可能产生 -1 或溢出到 steps.length,依赖外层逻辑将 open 置为 false。更稳妥的行为是:

  • 在首步点击 prev 时不变更;
  • 在末步点击 next 时触发 finish(或保持不变)。

示例改进:

-  const handlePrev = () => {
-    onInternalChange(mergedCurrent - 1);
-  };
-  const handleNext = () => {
-    onInternalChange(mergedCurrent + 1);
-  };
+  const handlePrev = () => {
+    if (mergedCurrent > 0) {
+      onInternalChange(mergedCurrent - 1);
+    }
+  };
+  const handleNext = () => {
+    if (mergedCurrent < steps.length - 1) {
+      onInternalChange(mergedCurrent + 1);
+    } else {
+      handleFinish();
+    }
+  };

154-162: 按步传入 mergedCurrent 到 useTarget:使每步 offset 生效,LGTM。另建议传入 mergedOpen 以修正非受控场景滚动。

  • 现在的变更确保 useTarget 能根据当前步选择对应的 [x,y] 偏移。
  • useTarget 的第二个参数用于决定是否 scrollIntoView。当组件为“非受控 open”(props.open 未传,使用 defaultOpen + 内部状态)时,传入 open 可能为 undefined,从而导致未滚动。建议传 mergedOpen

在 useTarget 调用处,将第二个参数从 open 改为 mergedOpen

   const [posInfo, targetElement] = useTarget(
     target,
-    open,
+    mergedOpen,
     gap,
     mergedScrollIntoViewOptions,
     inlineMode,
     placeholderRef,
     mergedCurrent,
   );

这有助于在非受控用法下也能正确滚动目标元素到视窗中。

docs/demo/gap.md (1)

20-22: 已确认示例文件存在且内容正确

  • docs/examples/gap-offset-array.tsx 中包含两个偏移元组 [[10,10], [20,20]] 及三个步骤,示例可正常运行
  • 建议在 demo 描述中补充“当 offset 数组长度小于步骤数时的回退行为(默认 6)”的说明
docs/examples/gap-offset-array.tsx (1)

11-14: 示例中 offset 数组长度少于 steps,建议补齐避免读者困惑

当前 steps 有 3 步,但 offset 仅提供 2 组。尽管实现可能有后备策略,示例建议一一对应以清晰展示“每步不同偏移”的效果。

可直接补齐第三组偏移:

-  const offset: [number, number][] = [
-    [10, 10],
-    [20, 20],
-  ]
+  const offset: [number, number][] = [
+    [10, 10],
+    [20, 20],
+    [30, 30],
+  ];
tests/index.test.tsx (1)

1293-1308: ref 属性存在性断言可更明确(可选)

当前仅在 if (ref.current) 内做属性断言。可在 effect 中先显式断言 ref.current 非空,失败时更易定位问题。

例如:

  • 在进入分支前加 expect(ref.current).toBeTruthy();
📜 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 d06be8a and 4d9b279.

📒 Files selected for processing (8)
  • README.md (1 hunks)
  • docs/demo/gap.md (1 hunks)
  • docs/examples/gap-offset-array.tsx (1 hunks)
  • src/Tour.tsx (6 hunks)
  • src/hooks/useTarget.ts (5 hunks)
  • src/index.tsx (1 hunks)
  • src/interface.ts (3 hunks)
  • tests/index.test.tsx (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/interface.ts (2)
src/index.tsx (2)
  • TourRef (2-2)
  • TourStepInfo (2-2)
src/TourStep/index.tsx (1)
  • TourStepInfo (7-7)
src/Tour.tsx (1)
src/interface.ts (2)
  • TourRef (19-24)
  • TourProps (61-99)
tests/index.test.tsx (2)
src/index.tsx (1)
  • TourRef (2-2)
src/interface.ts (1)
  • TourRef (19-24)
🪛 LanguageTool
README.md

[grammar] ~146-~146: There might be a mistake here.
Context: ...rettier-ignore --> | 属性 | 类型 | 说明 | 版本 | | --- | --- | --- | --- | | next | `() ...

(QB_NEW_EN)


[grammar] ~147-~147: There might be a mistake here.
Context: ...类型 | 说明 | 版本 | | --- | --- | --- | --- | | next | () => void | 跳转到下一步 | 2.2.0 ...

(QB_NEW_EN)


[grammar] ~148-~148: There might be a mistake here.
Context: ... next | () => void | 跳转到下一步 | 2.2.0 | | prev | () => void | 跳转到上一步 | 2.2.0 ...

(QB_NEW_EN)


[grammar] ~149-~149: There might be a mistake here.
Context: ... prev | () => void | 跳转到上一步 | 2.2.0 | | close | () => void | 关闭步骤 | 2.2.0 |...

(QB_NEW_EN)


[grammar] ~150-~150: There might be a mistake here.
Context: ...| close | () => void | 关闭步骤 | 2.2.0 | | finish | () => void | 结束步骤 | 2.2.0 ...

(QB_NEW_EN)

🔇 Additional comments (12)
README.md (1)

144-152: 同步 API 表格的 gap.offset 类型到 [number, number][],避免实现与文档不一致。

当前 README “Tour” 表格仍展示 gap.offset 为 number | [number, number],而代码已支持 Array<[number, number]>。这会误导用户。

建议在 README 的 “Tour” 表格中更新类型说明:

-| gap | `{ offset?: number \| [number, number]; radius?: number }` | - | 控制引导显示间距 |
+| gap | `{ offset?: number \| [number, number] \| [number, number][]; radius?: number }` | - | 控制引导显示间距 |

如果本次还引入了 offset 的默认回退值(useTarget.ts 中 DEFAULT_GAP_OFFSET = 6),建议在文档中一并说明其默认行为与优先级。

Likely an incorrect or invalid review comment.

src/hooks/useTarget.ts (3)

8-8: 类型扩展正确:已兼容每步二维偏移数组。

Gap.offset?: number | [number, number] | [number, number][] 与 PR 目标一致,能按步骤单独配置偏移。


19-20: 引入 DEFAULT_GAP_OFFSET = 6:请确认兼容性并在文档中说明默认回退。

这会影响 gap.offset 未定义或数组越界时的最终遮罩尺寸。为避免意外视觉变更,建议在 README 或 demo 内明确说明默认回退为 6。

是否此前就存在相同的默认值?如果是新引入的默认值,请在变更日志与 README API 中注明。


138-139: 依赖添加 current 正确,确保按步更新偏移计算。

加入 currentuseMemo 依赖后,步进时掩码矩形会随每步偏移数组正确更新。

src/interface.ts (2)

17-17: 使用 Record 改写索引签名更简洁。

HTMLAriaDataAttributes 从索引签名改为 Record<\data-${string}`, unknown>` 可读性更好,功能等价。


58-58: 在 TourStepProps 中重复声明 contentRender:避免歧义需统一文档描述。

类型层面 OK,但需在文档里明确优先级(如果 Tour 级与 Step 级都提供时哪个生效)或仅 Step 级生效的约定。

请确认当前实现里 contentRender 的应用层级与覆盖策略,并在 README 中标注。

src/Tour.tsx (3)

32-33: 将 Tour 改为 forwardRef 并暴露 TourRef:实现对齐预期,LGTM。

搭配 useImperativeHandle 暴露 next/prev/close/finish,与接口定义一致。


102-108: useImperativeHandle 挂载命令式 API:实现无误。

四个方法引用稳定的 handler,行为与预期一致。


204-209: 将事件回调集中为 handlePrev/Next/Finish/Close,降低重渲染闭包抖动:赞。

传入 TourStep 的回调更简洁,且与 ref API 复用相同逻辑,避免分叉。

src/index.tsx (1)

2-2: 导出 TourRef 到公共 API:LGTM

与 forwardRef 的命令式 API 对齐,类型仅导出(export type)不会引入运行时影响,满足测试对 TourRef 的类型引用需求。

tests/index.test.tsx (2)

6-6: 类型导入与公共导出一致:LGTM

TourRefsrc/index 的类型导出正常,可确保测试对命令式 API 的类型校验。


1379-1459: 按步切换的 gap.offset 数组行为覆盖充分:LGTM

用三步依次验证宽高随偏移对称扩展(x2)的计算,契合“每步偏移”设计目标。测试可读性与稳定性良好。

@QdabuliuQ QdabuliuQ changed the title feat: feat: support array of gap offsets for Tour steps feat: support array of gap offsets for Tour steps Aug 21, 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: 1

🧹 Nitpick comments (4)
src/hooks/useTarget.ts (4)

8-9: 扩展 Gap.offset 到 [number, number][] 很赞;建议补充轴向约定与回退策略说明

实现方向正确,API 向后兼容。为减少歧义,建议在类型/注释中明确:

  • 元组索引与轴向的映射:index=0 表示 X(水平),index=1 表示 Y(垂直)。
  • 当传入数组长度小于 step 总数或索引越界时,回退到 DEFAULT_GAP_OFFSET=6 的行为是预期设计。
    这有助于文档与使用者理解,亦便于未来维护与测试断言。

19-20: 引入 DEFAULT_GAP_OFFSET=6 合理;建议集中管理与测试断言覆盖默认值

当前常量仅在本文件使用。为避免“魔法数”在多处散落,建议:

  • 将默认值集中到单处(例如 constants.ts)并在 docs 中引用同一来源;
  • 在单测中对未提供 offset、空数组、越界索引等情形断言默认回退为 6。

84-104: getGapOffset 逻辑正确;建议强类型化 axis、clamp 步骤索引并精简分支

为提升可读性与鲁棒性(负值/小数步索引、异常数据),建议:

  • index 强化为字面量联合类型 0 | 1
  • currentMath.trunc>=0 的 clamp;
  • 使用局部 offset 变量减少可选链重复。

仅在所选行范围内的重构如下(保持原有默认回退为 6 的语义):

-  const getGapOffset = (index: number): number => {
-    if (gap?.offset === undefined) return DEFAULT_GAP_OFFSET;
-    
-    if (typeof gap.offset === 'number') {
-      return gap.offset;
-    }
-    
-    if (Array.isArray(gap.offset)) {
-      if (typeof gap.offset[0] === 'number') {
-        const tuple = gap.offset as [number, number];
-        return tuple[index] ?? DEFAULT_GAP_OFFSET;
-      }
-      if (Array.isArray(gap.offset[0])) {
-        const arrayOfTuples = gap.offset as [number, number][];
-        const stepIndex = current ?? 0;
-        return arrayOfTuples[stepIndex]?.[index] ?? DEFAULT_GAP_OFFSET;
-      }
-    }
-    
-    return DEFAULT_GAP_OFFSET;
-  };
+  const getGapOffset = (axis: 0 | 1): number => {
+    const offset = gap?.offset;
+    if (offset === undefined) return DEFAULT_GAP_OFFSET;
+
+    if (typeof offset === 'number') return offset;
+
+    if (Array.isArray(offset)) {
+      // [number, number]
+      if (typeof offset[0] === 'number') {
+        const tuple = offset as [number, number];
+        const val = tuple[axis];
+        return isValidNumber(val) ? val : DEFAULT_GAP_OFFSET;
+      }
+      // [number, number][]
+      if (Array.isArray(offset[0])) {
+        const arr = offset as [number, number][];
+        const idx = Math.max(0, Math.trunc(current));
+        const val = arr[idx]?.[axis];
+        return isValidNumber(val) ? val : DEFAULT_GAP_OFFSET;
+      }
+    }
+    return DEFAULT_GAP_OFFSET;
+  };

136-136: useMemo 依赖建议更精细化,避免不必要的重算

当前依赖为整个 gap 对象,任何与位置无关的属性变化也会触发重算。建议仅依赖所需的两个字段:

-  }, [posInfo, gap, current]);
+  }, [posInfo, current, gap?.offset, gap?.radius]);
📜 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 4d9b279 and 6490259.

📒 Files selected for processing (5)
  • docs/examples/gap-offset-array.tsx (1 hunks)
  • src/Tour.tsx (3 hunks)
  • src/hooks/useTarget.ts (5 hunks)
  • src/interface.ts (1 hunks)
  • tests/index.test.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • tests/index.test.tsx
  • src/interface.ts
  • docs/examples/gap-offset-array.tsx
  • src/Tour.tsx

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: 0

🧹 Nitpick comments (1)
src/Tour.tsx (1)

124-132: 建议将 useTarget 的第二个参数由 props.open 切换为 mergedOpen

为了避免在非受控模式下 props.open(可能为 undefined)导致 useTarget 内不触发测量或滚动的问题,保持与组件实际可见性状态一致。

需修改:

  • 文件:src/Tour.tsx
  • 行号:124(useTarget 调用处)

代码变更如下:

 const [posInfo, targetElement] = useTarget(
   target,
-  open,
+  mergedOpen,
   gap,
   mergedScrollIntoViewOptions,
   inlineMode,
   placeholderRef,
   mergedCurrent
 );
📜 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 6490259 and ae22a5f.

📒 Files selected for processing (1)
  • src/Tour.tsx (2 hunks)
🔇 Additional comments (1)
src/Tour.tsx (1)

267-267: 默认导出保持不变,LGTM

没有功能性变化,可保留。

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: 0

♻️ Duplicate comments (1)
src/hooks/useTarget.ts (1)

31-33: 避免使用 React.RefObject 命名空间类型;改用解构的 RefObject 并在本文件显式导入

此前已有相同建议。继续沿用 React 命名空间在部分配置下可能触发 “Cannot find namespace 'React'”。使用解构的 RefObject 类型更稳健。

仅对所选行的变更如下:

   inlineMode?: boolean,
-  placeholderRef?: React.RefObject<HTMLDivElement>,
+  placeholderRef?: RefObject<HTMLDivElement>,
   current: number = 0,
-): [PosInfo, HTMLElement] {
+): [PosInfo, HTMLElement] {

在文件顶部(所选行之外)补充类型导入:

// 顶部现有 import 后追加
import type { RefObject } from 'react';

可选(类型一致性,所选行之外):若希望严格反映运行时的三态/空态,考虑将局部状态与返回类型允许 null,并去除 targetElement 的 undefined 分支,以减少下游判空分支漂移:

-const [posInfo, setPosInfo] = useState<PosInfo>(null);
+const [posInfo, setPosInfo] = useState<PosInfo | null>(null);

-const [targetElement, setTargetElement] = useState<
-  null | HTMLElement | undefined
->(undefined);
+const [targetElement, setTargetElement] = useState<HTMLElement | null>(null);

-): [PosInfo, HTMLElement] {
+): [PosInfo | null, HTMLElement | null] {

为防止类型回归,请在本仓库根目录执行简单检查:

#!/bin/bash
set -euo pipefail

# 1) 确认仓库内再无 React.RefObject 直接引用(类型一致性)
rg -nP '\bReact\.RefObject\b' -g '*.ts' -g '*.tsx' -C2 || true

# 2) 进行类型检查(确保返回值/状态类型调整不会破坏构建)
# 如仓库使用的是 pnpm/yarn/npm,请替换下行命令
npm run -s typecheck || npx tsc -p .
🧹 Nitpick comments (3)
src/hooks/useTarget.ts (3)

7-10: 扩展 Gap.offset 类型 👍;建议提取类型别名以提升可读性与复用性

当前联合类型可读性一般,后续文档/测试中也可能多次引用。建议提取为命名类型(并标注 readonly),接口处仅引用别名,提升可维护性。

应用于所选行的最小变更:

 export interface Gap {
-  offset?: number | [number, number] | [number, number][];
+  offset?: GapOffset;
   radius?: number;
 }

在文件 import 之后新增类型别名(所选行之外):

// 放在本文件顶部 import 之后
export type GapOffset =
  | number
  | readonly [number, number]
  | ReadonlyArray<readonly [number, number]>;

19-20: 将默认偏移量常量化很好;可考虑导出以便测试/文档复用

如果测试或示例需要断言默认行为,导出该常量会更方便;若不需要对外复用,可忽略此建议。

-const DEFAULT_GAP_OFFSET = 6;
+export const DEFAULT_GAP_OFFSET = 6;

84-104: getGapOffset 健壮性提升:处理 NaN/Infinity、负/非整数 current,并收紧索引类型

当前实现对 NaN/Infinity 与负/非整数步索引未做约束;同时 index 传参可以限定为 0|1。下面改写在保持语义的同时增强健壮性。

-  const getGapOffset = (index: number): number => {
-    if (gap?.offset === undefined) return DEFAULT_GAP_OFFSET;
-    
-    if (typeof gap.offset === 'number') {
-      return gap.offset;
-    }
-    
-    if (Array.isArray(gap.offset)) {
-      if (typeof gap.offset[0] === 'number') {
-        const tuple = gap.offset as [number, number];
-        return tuple[index] ?? DEFAULT_GAP_OFFSET;
-      }
-      if (Array.isArray(gap.offset[0])) {
-        const arrayOfTuples = gap.offset as [number, number][];
-        const stepIndex = current ?? 0;
-        return arrayOfTuples[stepIndex]?.[index] ?? DEFAULT_GAP_OFFSET;
-      }
-    }
-    
-    return DEFAULT_GAP_OFFSET;
-  };
+  const getGapOffset = (index: 0 | 1): number => {
+    const offset = gap?.offset;
+    if (offset === undefined) return DEFAULT_GAP_OFFSET;
+
+    // 单值:同时作用于 X/Y
+    if (typeof offset === 'number') {
+      return isValidNumber(offset) ? offset : DEFAULT_GAP_OFFSET;
+    }
+
+    if (Array.isArray(offset)) {
+      // 元组 [x, y]
+      if (typeof offset[0] === 'number') {
+        const tuple = offset as [number, number];
+        const v = tuple[index];
+        return isValidNumber(v) ? v : DEFAULT_GAP_OFFSET;
+      }
+      // 按步骤的元组数组 [ [x,y], [x,y], ... ]
+      if (Array.isArray(offset[0])) {
+        const tuples = offset as [number, number][];
+        const stepIndex = Math.max(0, Math.trunc(current));
+        const v = tuples[stepIndex]?.[index];
+        return isValidNumber(v) ? v : DEFAULT_GAP_OFFSET;
+      }
+    }
+
+    return DEFAULT_GAP_OFFSET;
+  };

可选后续:若希望当 current 越界时使用“最后一个已配置偏移”而非默认值,可将 stepIndex 由 clamp 到 [0, tuples.length - 1],即:Math.min(tuples.length - 1, Math.max(0, Math.trunc(current))).

📜 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 ae22a5f and a9bd27e.

📒 Files selected for processing (1)
  • src/hooks/useTarget.ts (5 hunks)
🔇 Additional comments (1)
src/hooks/useTarget.ts (1)

136-137: 将 current 加入 useMemo 依赖项:赞

这保证了步进变化时位置能即时按每步的 offset 重新计算。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant