-
Notifications
You must be signed in to change notification settings - Fork 37
feat: support array of gap offsets for Tour steps #81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Note Other AI code review bot(s) detectedCodeRabbit 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 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. 📒 Files selected for processing (1)
Walkthrough在文档中新增“Gap Offset 数组”示例;核心实现扩展 useTarget 支持按步骤的 gap.offset 数组并新增 DEFAULT_GAP_OFFSET 与 current 参数;Tour 将 mergedCurrent 传给 useTarget;导出类型新增 TourRef,interface 中 data-* 类型改为 Record;新增对应测试。 Changes
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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
-
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. ↩
There was a problem hiding this 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 makingtitle
optional and addingcontentRender
, 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.
There was a problem hiding this 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,建议补全联合类型。
posInfo
与targetElement
在运行期都可能为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.
📒 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 正确,确保按步更新偏移计算。加入
current
到useMemo
依赖后,步进时掩码矩形会随每步偏移数组正确更新。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
TourRef
从src/index
的类型导出正常,可确保测试对命令式 API 的类型校验。
1379-1459
: 按步切换的 gap.offset 数组行为覆盖充分:LGTM用三步依次验证宽高随偏移对称扩展(x2)的计算,契合“每步偏移”设计目标。测试可读性与稳定性良好。
There was a problem hiding this 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
;- 对
current
做Math.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.
📒 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
There was a problem hiding this 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.
📒 Files selected for processing (1)
src/Tour.tsx
(2 hunks)
🔇 Additional comments (1)
src/Tour.tsx (1)
267-267
: 默认导出保持不变,LGTM没有功能性变化,可保留。
There was a problem hiding this 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.
📒 Files selected for processing (1)
src/hooks/useTarget.ts
(5 hunks)
🔇 Additional comments (1)
src/hooks/useTarget.ts (1)
136-137
: 将 current 加入 useMemo 依赖项:赞这保证了步进变化时位置能即时按每步的 offset 重新计算。
🤔 This is a ...
🔗 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
Summary by CodeRabbit
新功能
文档
测试