Skip to content

Conversation

@QdabuliuQ
Copy link
Contributor

@QdabuliuQ QdabuliuQ commented Aug 13, 2025

🤔 This is a ...

  • 🆕 New feature
  • 🐞 Bug fix
  • 📝 Site / documentation improvement
  • 📽️ Demo improvement
  • 💄 Component style improvement
  • 🤖 TypeScript definition improvement
  • 📦 Bundle size optimization
  • ⚡️ Performance optimization
  • ⭐️ Feature enhancement
  • 🌐 Internationalization
  • 🛠 Refactoring
  • 🎨 Code style optimization
  • ✅ Test Case
  • 🔀 Branch merge
  • ⏩ Workflow
  • ⌨️ Accessibility improvement
  • ❓ Other (about what?)

🔗 Related Issues

close ant-design/ant-design#54597

💡 Background and Solution

add resizable feature to Drawer component
add ResizableLine component to control the size of drawer

📝 Change Log

Language Changelog
🇺🇸 English add resizable feature to Drawer component
🇨🇳 Chinese Drawer 组件添加 resizable 属性

Summary by CodeRabbit

  • 新功能
    • 抽屉支持拖拽调整大小(左/右/上/下),新增 size/maxSize/defaultSize、resizable 回调(onResize/onResizeStart/onResizeEnd)、保留兼容 width/height,并导出 Placement 类型,新增 useDrag 钩子与拖拽类名/样式接口。
  • 样式
    • 新增可视化拖拽手柄,统一类名前缀,扩展边缘变体、遮罩、阴影与开合过渡,调整内容溢出处理。
  • 文档
    • 更新 README API(onMaskClick 重命名为 onClose,新增 defaultWidth/defaultHeight 与 resizable),新增 Resizable 示例页与示例代码。
  • 测试
    • 增加覆盖拖拽与尺寸行为的单元测试,包含受控/非受控、最小/最大/默认与容器回退场景。

@vercel
Copy link

vercel bot commented Aug 13, 2025

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

Project Deployment Preview Comments Updated (UTC)
drawer Ready Ready Preview Comment Sep 3, 2025 7:19am

@coderabbitai
Copy link

coderabbitai bot commented Aug 13, 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.

Walkthrough

为 Drawer 增加可拖拽调整尺寸(resizable)能力:新增 useDrag Hook、拖拽句柄与样式;扩展尺寸相关 Props(size/maxSize/defaultSize/兼容 width/height/resizable)、导出 Placement;新增示例、文档、动效与单元测试;样式命名重构为基于 prefix 的结构。

Changes

Cohort / File(s) Summary
文档与示例
README.md, docs/demo/resizable.md, docs/examples/resizable.tsx, docs/examples/assets/motion.less
README 增加 defaultWidth/defaultHeight 与 resizable API、onMaskClick → onClose;新增 Resizable 示例页面与示例组件;motion.less 增加 top/bottom 面板动效。
组件实现
src/Drawer.tsx, src/DrawerPopup.tsx
Drawer/DrawerPopup 新增 size/maxSize/defaultSize/resizable/兼容 width/height 支持;Drawer 将尺寸与 resizable 转发至 DrawerPopup;DrawerPopup 实现 currentSize/mergedSize、wrapperStyle、拖拽句柄渲染与拖拽期间渲染/样式调整,并接入 useDrag。
Hook:拖拽逻辑
src/hooks/useDrag.ts
新增 useDrag 实现:方向判断、document 级别鼠标事件、尺寸计算与 maxSize 限制;返回 dragElementProps 与 isDragging。
样式重构
assets/index.less
样式改为基于 .@{prefixCls}-* 结构;新增 .@{prefixCls}-resizable-dragger 及四方向/拖拽态变体;移除旧注释块并调整遮罩/容器/过渡与溢出处理。
类型与导出
src/index.ts, src/inter.ts
index 追加导出 Placement 类型;接口新增 DrawerClassNames.draggerDrawerStyles.dragger 字段。
工具与测试
src/util.ts, tests/index.spec.tsx, test-todo.tsx, tsconfig.json
parseWidthHeight 字符串解析改写;新增单测覆盖横/纵向拖拽、min/max/default/受控及容器回退场景;新增测试示例组件;tsconfig include 移除 .dumi/**/*

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User as 用户
  participant Dragger as 拖拽句柄(.rc-drawer-resizable-dragger)
  participant useDrag as useDrag Hook
  participant Popup as DrawerPopup
  participant App as 使用者回调

  User->>Dragger: mousedown
  Dragger->>useDrag: onMouseDown()
  useDrag->>Popup: onResizeStart(startSize)
  Popup->>App: resizable.onResizeStart()

  loop 拖拽中
    User->>useDrag: mousemove
    useDrag->>Popup: onResize(newSize)
    Popup->>App: resizable.onResize(newSize)
    Popup->>Popup: 更新 currentSize 与 wrapperStyle
  end

  User->>useDrag: mouseup
  useDrag->>Popup: onResizeEnd(finalSize)
  Popup->>App: resizable.onResizeEnd()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

Assessment against linked issues

Objective Addressed Explanation
提供 Drawer 的可拖拽调整宽度能力(#54597)
新增 resizable API 以启用该能力(#54597)
提供演示/文档说明用法(#54597)
基本交互测试覆盖(拖拽与约束)(#54597)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
样式命名空间重构为 .@{prefixCls}-*assets/index.less 命名体系重构为全局 CSS 结构改动,超出原 issue 对“可拖拽宽度”功能的最低实现需求。
README 中将 onMaskClick 改为 onCloseREADME.md 文档 API 名称调整,与 resizable 功能实现无直接关联。
支持纵向(height/defaultHeight)与 top/bottom 拖拽(src/DrawerPopup.tsxdocs/examples/resizable.tsx 原 issue 聚焦水平宽度,新增纵向支持为功能扩展,超出最小目标范围。

Poem

我是小兔抓把柄,轻触拖动换新形,
左右上下随心定,尺寸回调铃声响,
代码草丛藏花影,组件更欢开发轻。 🐰✨


📜 Recent 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 043326b and 4da4ce2.

📒 Files selected for processing (1)
  • src/util.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/util.ts
✨ 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 or @coderabbit 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
Contributor

@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 new feature to the Drawer component, allowing it to be resizable. Users can now dynamically adjust the width or height of the drawer by dragging its edges, providing greater flexibility in UI layouts. This enhancement includes new properties to enable the feature and provides callbacks for managing the resizing process, along with dedicated styling and a new internal component to handle the drag interactions. The changes are fully documented and include a new example for easy adoption.

Highlights

  • Resizable Drawer Feature: The Drawer component now supports a resizable prop, which when set to true, allows users to change the drawer's size by dragging its edges. This enhances user interaction and flexibility.
  • New Resize Event Callbacks: Three new callback functions, onResize, onResizeStart, and onResizeEnd, have been added to the Drawer component. These provide developers with hooks to execute custom logic during different stages of the resizing process, such as updating state or performing validations.
  • Introduction of ResizableLine Component: A dedicated ResizableLine component has been introduced to encapsulate the logic for handling mouse events (mousedown, mousemove, mouseup) and calculating the new size of the drawer during a drag operation. This modular approach keeps the core Drawer component cleaner.
  • Styling for Resizable Handles: New CSS rules have been added to style the resizable line, including visual feedback for hover and dragging states. This ensures a clear and intuitive user experience when interacting with the resize handles.
  • Documentation and Example Updates: Comprehensive updates to the documentation and a new interactive example have been provided. This allows other developers to quickly understand how to implement and utilize the new resizable drawer feature in their applications.
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.

@QdabuliuQ QdabuliuQ mentioned this pull request Aug 13, 2025
17 tasks
Copy link
Contributor

@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 a resizable drawer feature, which is a great addition. The implementation is mostly solid, with a new ResizableLine component handling the drag logic and updates to the Drawer and DrawerPopup components to support it. I've found a few issues: an incorrect CSS value for overflow, a bug in the animation styles for top/bottom placements, and an API inconsistency in the new resize event handlers. The documentation and examples have been updated accordingly. My review includes suggestions to fix these issues.

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

🔭 Outside diff range comments (1)
src/inter.ts (1)

1-13: 命名不一致:接口使用 section,文档与样式使用 content,建议新增别名并标注弃用

DrawerClassNames/DrawerStyles 中是 section,但 README 与样式体系(如 -content)更贴近 “content”。为避免使用者困惑与类型不匹配,建议在类型上新增 content,并将 section 标注为 deprecated,保证向后兼容。

建议变更:

 export interface DrawerClassNames {
   mask?: string;
   wrapper?: string;
-  section?: string;
+  /** @deprecated 请使用 `content` */
+  section?: string;
+  content?: string;
   resizableLine?: string;
 }

 export interface DrawerStyles {
-  mask?: React.CSSProperties;
-  wrapper?: React.CSSProperties;
-  section?: React.CSSProperties;
+  mask?: React.CSSProperties;
+  wrapper?: React.CSSProperties;
+  /** @deprecated 请使用 `content` */
+  section?: React.CSSProperties;
+  content?: React.CSSProperties;
   resizableLine?: React.CSSProperties;
 }

(如代码中有读取 section 的实现,建议同时支持 content ?? section。)

🧹 Nitpick comments (18)
tsconfig.json (1)

16-16: 明确排除 .dumi 目录,避免被通配重新包含

当前 include 使用 "/*.ts"、"/*.tsx" 会再次把 .dumi 目录纳入编译范围。若期望排除 .dumi(以及构建产物),建议补充 exclude,提高 ts 编译性能并避免噪音类型报错。

建议变更:

-  "include": [".dumirc.ts", "**/*.ts", "**/*.tsx"]
+  "include": [".dumirc.ts", "**/*.ts", "**/*.tsx"],
+  "exclude": [".dumi", "node_modules", "dist"]
src/inter.ts (1)

5-5: 新增 resizableLine 类型后请同步 README 的 classNames/styles 说明

接口已增加 resizableLine?: stringresizableLine?: React.CSSProperties,但 README 的 API 表格中 classNamesstyles 的对象结构未包含该键,文档与实现不一致,建议同步更新文档。

我可以附带一段 README 的表格更新 diff,是否需要我起一个跟进 PR?

Also applies to: 12-12

README.md (3)

47-48: 补充 resizableLine 的类型说明,保持与代码一致

classNames/styles 的类型签名未包含 resizableLine,与 src/inter.ts 定义不一致,建议补上:

建议变更(文档示意):

-| classNames      | { mask?: string; content?: string; wrapper?: string; }                      | -                                      | pass className to target area                                                 |
-| styles          | { mask?: CSSProperties; content?: CSSProperties; wrapper?: CSSProperties; } | -                                      | pass style to target area                                                     |
+| classNames      | { mask?: string; content?: string; wrapper?: string; resizableLine?: string; }                      | - | pass className to target area |
+| styles          | { mask?: CSSProperties; content?: CSSProperties; wrapper?: CSSProperties; resizableLine?: CSSProperties; } | - | pass style to target area |

55-55: 文档类型更精确:placement 可标注为联合类型

代码侧已导出 Placement 类型(如 PR 摘要所述),文档仍是 string。建议文档同步为更精确的联合类型('left' | 'right' | 'top' | 'bottom' 或引用 Placement),提升消费者可读性。


59-59: 拼写错误:React.createElement

React.creactElement 应为 React.createElement

建议变更:

- | getContainer    | string \| func \| HTMLElement                                               | `body`                                 | Return the mount node for Drawer. if is `null` use React.creactElement        |
+ | getContainer    | string \| func \| HTMLElement                                               | `body`                                 | Return the mount node for Drawer. If it is `null`, use React.createElement    |
assets/index.less (3)

62-107: 提升可用性:防止拖拽时选中文本与多指缩放干扰

建议为可拖拽线增加 user-select: nonetouch-action: none,在移动端和触摸板上会更稳。

建议变更:

   &-resizable-line {
     position: absolute;
     z-index: 2;
     pointer-events: auto;
+    user-select: none;
+    touch-action: none;

68-74: 避免硬编码品牌色,改为可配置变量

#1890ff 是 Ant Design v4 的主色,rc-drawer 作为独立库建议使用可配置变量,避免与主题脱钩。可新增局部变量,默认值保留原色以兼容。

建议变更:

+@resizer-active-color: #1890ff; // 可按需覆写
 ...
   &-resizable-line {
@@
-    &:hover {
-      background-color: #1890ff !important;
-    }
+    &:hover {
+      background-color: @resizer-active-color !important;
+    }
@@
-    &-dragging {
-      background-color: #1890ff !important;
-    }
+    &-dragging {
+      background-color: @resizer-active-color !important;
+    }

110-117: z-index 定义重复且数值不一致,建议统一

本文件前部 .@{prefixCls} 已设置 z-index: @zIndex(当前为 1050),此处再次设置为 9999。两处相同选择器的重复定义会以较晚出现者为准,建议统一到变量,避免维护困惑与潜在层级问题。

建议其一(更易维护):改为使用变量

-  z-index: 9999;
+  z-index: @zIndex;

建议其二(若确需抬高层级):统一在顶部提升变量值

-@zIndex: 1050;
+@zIndex: 9999;

请确认期望行为,并选择其一。

docs/demo/resizable.md (1)

2-2: 标题大小写与导航不一致,建议统一

Front matter 中的 title 为小写“resizable”,而 nav.title 为“Resizable”。为保持文档一致性与站点导航显示统一,建议首字母大写。

-title: resizable
+title: Resizable
tests/index.spec.tsx (1)

479-516: 增强用例稳定性并覆盖回调行为断言

  • 使用 getContainer={false},减少与 Portal/文档滚动相关的偶发性影响。
  • 将拖拽序列包裹在 act 中,避免 React 状态更新告警。
  • 覆盖 onResize/onResizeStart/onResizeEnd 回调是否触发及参数正确性的断言,提升行为验证完整性。
 it('should support resizable', () => {
-  const { unmount } = render(
-    <Drawer resizable open placement="left" width={200} />,
-  );
+  const onResizeStart = jest.fn();
+  const onResize = jest.fn();
+  const onResizeEnd = jest.fn();
+  const { unmount } = render(
+    <Drawer
+      resizable
+      open
+      placement="left"
+      width={200}
+      getContainer={false}
+      onResizeStart={onResizeStart}
+      onResize={onResize}
+      onResizeEnd={onResizeEnd}
+    />,
+  );

   // Mock getBoundingClientRect for the content wrapper to simulate real DOM dimensions
   const contentWrapper = document.querySelector(
     '.rc-drawer-content-wrapper',
   ) as HTMLElement;
   const mockGetBoundingClientRect = jest.fn(
     () =>
       ({
         width: 200,
         height: 400,
         top: 0,
         left: 0,
         bottom: 400,
         right: 200,
         x: 0,
         y: 0,
         toJSON: () => ({}),
       }) as DOMRect,
   );
   contentWrapper.getBoundingClientRect = mockGetBoundingClientRect;

   const resizableLine = document.querySelector('.rc-drawer-resizable-line');
   expect(resizableLine).toBeTruthy();

-  // Simulate drag from 200px to 100px (should reduce width by 100px)
-  fireEvent.mouseDown(resizableLine, { clientX: 200 });
-  fireEvent.mouseMove(document, { clientX: 100, clientY: 0 });
-  fireEvent.mouseUp(document, { clientX: 100, clientY: 0 });
+  // Simulate drag from 200px to 100px (should reduce width by 100px)
+  act(() => {
+    fireEvent.mouseDown(resizableLine!, { clientX: 200 });
+    fireEvent.mouseMove(document, { clientX: 100, clientY: 0 });
+    fireEvent.mouseUp(document, { clientX: 100, clientY: 0 });
+  });

   expect(document.querySelector('.rc-drawer-content-wrapper')).toHaveStyle({
     width: '100px',
   });
+  expect(onResizeStart).toHaveBeenCalledTimes(1);
+  expect(onResize).toHaveBeenCalled();
+  // 最后一笔应为 100 宽度(或高度,取决于 placement)
+  expect(onResize).toHaveBeenLastCalledWith(100);
+  expect(onResizeEnd).toHaveBeenCalledTimes(1);
   unmount();
 });
docs/examples/resizable.tsx (3)

8-8: 建议优化类型导入路径

从'@/index'导入类型可能导致构建路径解析问题。建议使用相对路径或明确的包名导入。

-import type { Placement } from '@/index';
+import type { Placement } from 'rc-drawer';

42-42: key属性可能导致不必要的重新挂载

使用placement作为key会在每次placement变化时强制重新挂载Drawer组件,这可能不是期望的行为。考虑移除key属性或使用更稳定的标识符。

-key={placement}

47-52: 改进示例文案的用户体验

文案可以更清晰地说明调整大小的操作方式。

-<p>
-  You can drag the drawer edge to resize (only works when
-  &quot;Resizable&quot; is checked).
-</p>
+<p>
+  拖拽抽屉边缘可调整大小(仅在勾选"Resizable"时有效)。
+</p>
src/ResizableLine.tsx (4)

24-24: 重新考虑minSize默认值

默认minSize为100可能过大,特别是对于移动端或小屏幕设备。建议设置更小的默认值或根据方向动态调整。

-minSize = 100,
+minSize = 48,

82-84: maxSize验证逻辑可以简化

当前的maxSize检查有些冗余,可以简化条件判断。

-// Only apply maxSize if it's a valid positive number
-if (maxSize !== undefined && maxSize > 0 && newSize > maxSize) {
+if (maxSize && newSize > maxSize) {

100-112: handleMouseUp中的重复代码

与handleMouseDown中的父元素获取逻辑重复,建议提取为共用函数。

+const getParentSize = React.useCallback(() => {
+  const parentElement = lineRef.current?.parentElement;
+  if (!parentElement) return 0;
+  const rect = parentElement.getBoundingClientRect();
+  return isHorizontal ? rect.width : rect.height;
+}, [isHorizontal]);

 const handleMouseUp = React.useCallback(() => {
   if (isDragging) {
     onDraggingChange?.(false);
-    // Get the final size after resize
-    const parentElement = lineRef.current?.parentElement;
-    if (parentElement) {
-      const rect = parentElement.getBoundingClientRect();
-      const finalSize = isHorizontal ? rect.width : rect.height;
+    const finalSize = getParentSize();
+    if (finalSize > 0) {
       onResizeEnd?.(finalSize);
-    }
   }
-}, [isDragging, onResizeEnd, isHorizontal, onDraggingChange]);
+}, [isDragging, onResizeEnd, onDraggingChange, getParentSize]);

135-140: resizeLineStyle可以优化

style对象的构建可以更高效,避免每次渲染时创建新对象。

-const resizeLineStyle: React.CSSProperties = {
-  position: 'absolute',
-  zIndex: 2,
-  cursor: isHorizontal ? 'col-resize' : 'row-resize',
-  ...style,
-};
+const resizeLineStyle = React.useMemo<React.CSSProperties>(() => ({
+  position: 'absolute',
+  zIndex: 2,
+  cursor: isHorizontal ? 'col-resize' : 'row-resize',
+  ...style,
+}), [isHorizontal, style]);
src/DrawerPopup.tsx (1)

297-301: 状态初始化可以优化

建议为currentSize提供更明确的初始值,而不是undefined。

-const [currentSize, setCurrentSize] = React.useState<number>();
+const [currentSize, setCurrentSize] = React.useState<number | undefined>(undefined);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2418e05 and 1545c74.

📒 Files selected for processing (13)
  • README.md (1 hunks)
  • assets/index.less (2 hunks)
  • docs/demo/resizable.md (1 hunks)
  • docs/examples/assets/motion.less (1 hunks)
  • docs/examples/getContainer-false.tsx (1 hunks)
  • docs/examples/resizable.tsx (1 hunks)
  • src/Drawer.tsx (3 hunks)
  • src/DrawerPopup.tsx (5 hunks)
  • src/ResizableLine.tsx (1 hunks)
  • src/index.ts (1 hunks)
  • src/inter.ts (1 hunks)
  • tests/index.spec.tsx (1 hunks)
  • tsconfig.json (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
docs/examples/resizable.tsx (4)
docs/examples/getContainer-false.tsx (1)
  • open (21-114)
src/index.ts (1)
  • Placement (5-5)
src/Drawer.tsx (1)
  • Placement (15-15)
src/DrawerPopup.tsx (1)
  • Placement (26-26)
src/ResizableLine.tsx (3)
src/index.ts (1)
  • Placement (5-5)
src/Drawer.tsx (1)
  • Placement (15-15)
src/DrawerPopup.tsx (1)
  • Placement (26-26)
🔇 Additional comments (11)
README.md (1)

67-76: LGTM:新增 resizable 与 resize 回调文档与实现一致

新文档项与 PR 目标一致,描述清晰。

assets/index.less (1)

33-35: 确认移除 overflow: hidden 的影响

content-wrapper 注释掉 overflow: hidden 能让拖拽线溢出 3px 可点,但同时也可能导致内容溢出时出现视觉残留。确认是否在拖拽中通过行内样式或状态条件控制 overflow,以避免滚动条/阴影露出。

docs/examples/getContainer-false.tsx (1)

55-56: 示例新增 resizable 用法,LGTM

在 getContainer={false} 的场景展示可拖拽调整尺寸是合理的,变更清晰可读。

src/index.ts (1)

3-5: 导出 Placement 类型,LGTM

类型面向外部暴露更完整,便于下游消费与类型提示。

src/Drawer.tsx (2)

56-59: 解构新增回调以便传递,LGTM

onResize/onResizeStart/onResizeEnd 在此解构后下传,符合预期的数据流。


120-123: 回调透传到 DrawerPopup,LGTM

事件处理集合中纳入 resize 系列回调,保持 API 一致性与集中传递,合理。

src/DrawerPopup.tsx (5)

80-84: LGTM!可调整尺寸相关的API设计合理

新增的resizable相关属性和回调函数API设计清晰,类型定义完整。


397-397: 动画类名条件逻辑很好

在拖拽时禁用动画类名是明智的设计,避免拖拽过程中的动画干扰。


406-420: ResizableLine组件集成正确

ResizableLine的属性传递和事件处理集成得很好,maxSize和isDragging状态管理恰当。


393-393: wrapperRef 使用验证通过

已在 src/DrawerPopup.tsx 中确认 wrapperRef 的定义与使用场景一致:

  • 定义为 React.useRef<HTMLDivElement>(null)
  • calculateMaxSize 回调及 JSX 中通过 ref={wrapperRef} 正确绑定至 DOM 元素

未发现引用不一致或遗漏,使用正常。


304-313: calculateMaxSize函数需要添加边界检查

函数缺少对rect的null检查,可能导致运行时错误。

 const calculateMaxSize = React.useCallback(() => {
   if (wrapperRef.current) {
     const rect = wrapperRef.current.parentElement?.getBoundingClientRect();
-    const newMaxSize =
+    if (!rect) return;
+    const newMaxSize =
       placement === 'left' || placement === 'right'
-        ? (rect?.width ?? 0)
-        : (rect?.height ?? 0);
+        ? rect.width
+        : rect.height;
     setMaxSize(newMaxSize);
   }
 }, [placement]);

Likely an incorrect or invalid review comment.

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

♻️ Duplicate comments (1)
src/DrawerPopup.tsx (1)

358-362: overflow 设置为 'none' 是无效值,应按场景使用 'visible' 或 'hidden'

这里将 style.overflow 设为 'none',CSS 不支持该值。若要让拖拽手柄(有 -3px 偏移)在 wrapper 外可见,应在 resizable 时使用 'visible';而非 resizable 场景建议维持 'hidden'(与历史行为一致,避免 wrapper 出现滚动条)。

-    if (resizable) {
-      style.overflow = 'none';
-    } else {
-      style.overflow = 'auto';
-    }
+    if (resizable) {
+      style.overflow = 'visible';
+    } else {
+      style.overflow = 'hidden';
+    }
🧹 Nitpick comments (9)
assets/index.less (1)

62-107: 拖拽手柄建议增强可用性与可访问性(触控/选择/命中区域)

当前手柄宽高为 6px,hover/dragging 才着色,缺少移动端和防选中文本的样式,实际交互可能较难命中并与内容选择冲突。

建议增强:

  • 添加 touch-action: none; user-select: none; 避免移动端滚动/文本选择干扰。
  • 默认 background-color: transparent,hover/dragging 再显色。
  • 可考虑将热区扩大到 8–10px(按需)。

示例增补(可合入 &-resizable-dragger 基类内):

 &-resizable-dragger {
   position: absolute;
   z-index: 2;
   pointer-events: auto;
+  background-color: transparent;
+  touch-action: none;
+  user-select: none;
src/inter.ts (1)

1-13: 新增 dragger 自定义入口命名与定位合理,建议补充注释以稳定 API 语义

新增 dragger?: stringdragger?: React.CSSProperties 能覆盖拖拽线的 className 与 style,命名与现有讨论一致。为便于下游理解与避免误用(与 handle/section 概念混淆),建议在接口上补充 JSDoc 注释,说明其仅作用于“可调整尺寸的分隔线/热区”。

示例注释(非必须):

/** resizable 模式下的拖拽手柄(分隔线)容器样式/类名 */
dragger?: string;
dragger?: React.CSSProperties;
docs/examples/resizable.tsx (3)

14-19: 移除多余类型断言,直接类型化 buttons 数组

placement 的状态已是 Placement,此处对每一项使用 as Placement 属于冗余断言。直接在数组上声明类型更清晰安全。

-  const buttons = [
-    { placement: 'left' as Placement, label: 'Left Drawer' },
-    { placement: 'right' as Placement, label: 'Right Drawer' },
-    { placement: 'top' as Placement, label: 'Top Drawer' },
-    { placement: 'bottom' as Placement, label: 'Bottom Drawer' },
-  ];
+  const buttons: Array<{ placement: Placement; label: string }> = [
+    { placement: 'left', label: 'Left Drawer' },
+    { placement: 'right', label: 'Right Drawer' },
+    { placement: 'top', label: 'Top Drawer' },
+    { placement: 'bottom', label: 'Bottom Drawer' },
+  ];

42-42: 移除冗余断言

placement 已是 Placement 类型,无需再次断言。

-        placement={placement as Placement}
+        placement={placement}

30-36: 给按钮添加 type="button" 以避免在 Form 环境误触发提交

示例中按钮若被嵌入到 form 内,默认 type="submit" 可能触发表单提交。加上 type="button" 更稳妥。

-          <button
+          <button
+            type="button"
             key={placement}
             onClick={() => openDrawer(placement)}
             style={{ padding: '8px 16px' }}
           >
src/DrawerPopup.tsx (3)

347-366: 在拖拽时禁用过渡已处理,但建议对 width/height 的单位与数值混用做防御

当前在 currentSize 存在时用数值覆盖 width/height,与可能的字符串(如 '20vw')互斥,这符合预期。但在开启拖拽后从一个字符串单位切换为纯数值时,视觉上会突然以 px 为单位进行变化,这是可接受的设计权衡。若要更平滑,可以在开始拖拽时解析并固定为像素基准(可选)。


298-313: maxSize 计算仅依赖 parentElement 尺寸,嵌套/inline 场景需再确认

wrapperRef.current.parentElement?.getBoundingClientRect()getContainer={false} 或复杂嵌套下可能不是你期望的可用空间(例如父级有 padding/滚动容器)。建议后续结合实际容器(portal 容器或视口)确认,或暴露 maxSize/getMaxSize 参数以供高级用法覆盖。


416-417: 拖拽手柄无语义/无可访问性支持,建议增加角色与可见文本隐藏说明(可选)

可考虑为手柄添加 role="separator"aria-orientation,并通过 aria-labeltitle 提示“拖拽以调整尺寸”。键盘可达的可调整大小(如使用左右/上下箭头)属于更高阶改进,可择机迭代。

src/hooks/useDrag.ts (1)

1-26: 建议新增 Pointer 事件支持以兼容触屏设备(可选)

当前仅监听 mouse 事件。实际在移动端/触摸屏上会缺乏交互。可以使用 Pointer 事件统一处理(pointerdown/move/up),或补充 touch* 事件。

📜 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 1545c74 and e16a142.

📒 Files selected for processing (6)
  • assets/index.less (2 hunks)
  • docs/examples/resizable.tsx (1 hunks)
  • src/DrawerPopup.tsx (5 hunks)
  • src/hooks/useDrag.ts (1 hunks)
  • src/inter.ts (1 hunks)
  • tests/index.spec.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/index.spec.tsx
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/hooks/useDrag.ts (1)
src/DrawerPopup.tsx (1)
  • Placement (26-26)
src/DrawerPopup.tsx (1)
src/hooks/useDrag.ts (1)
  • useDrag (28-161)
docs/examples/resizable.tsx (4)
docs/examples/getContainer-false.tsx (1)
  • open (21-114)
src/DrawerPopup.tsx (1)
  • Placement (26-26)
src/index.ts (1)
  • Placement (5-5)
src/Drawer.tsx (1)
  • Placement (15-15)
🔇 Additional comments (5)
assets/index.less (1)

30-35: content-wrapper 注释掉 overflow: hidden 的影响需要确认

这里将 overflow: hidden; 注释掉(Line 33)。结合下方第二块样式中顶层容器仍是 overflow: hidden;,以及组件运行时又通过内联样式动态设置 overflow,可能出现拖拽手柄被裁剪或滚动条出现的不一致行为。

建议:明确策略(例如“仅在 resizable 时让 wrapper overflow: visible,其余场景 hidden”),避免多个层级对 overflow 的相互覆盖造成视觉/命中异常。配合组件代码修正(参见 DrawerPopup.tsx 对 overflow 的设置问题)。

docs/examples/resizable.tsx (1)

8-8: 确认路径别名是否在示例构建环境中可用

import type { Placement } from '@/index'; 依赖 @ 路径别名。若 docs 示例单独构建或运行,需要确保 tsconfig/webpack 等已声明 paths/alias。

如需验证,请检查根目录下 tsconfig/构建配置是否有 @/* 的 alias 映射到 src/*

src/hooks/useDrag.ts (3)

28-41: 适配新签名,内部统一从 ref 读取容器

[ suggest_essential_refactor ]

 export default function useDrag(options: UseDragOptions): UseDragReturn {
   const {
     prefixCls = 'resizable',
     direction,
     className,
     style,
     minSize = 100,
     maxSize,
     disabled = false,
-    container,
+    containerRef,
     onResize,
     onResizeEnd,
     onResizeStart,
   } = options;

64-73: mousedown 时从 ref 读取容器并回调 startSize

[ suggest_essential_refactor ]

-      // Get the current size of the container
-      if (container) {
-        const rect = container.getBoundingClientRect();
+      // Get the current size of the container
+      const container = containerRef?.current;
+      if (container) {
+        const rect = container.getBoundingClientRect();
         const currentSize = isHorizontal ? rect.width : rect.height;
         setStartSize(currentSize);
         onResizeStart?.(currentSize);
       }
     },
-    [disabled, isHorizontal, container, onResizeStart],
+    [disabled, isHorizontal, containerRef, onResizeStart],

113-125: mouseup 时同样从 ref 读取容器最终尺寸

[ suggest_essential_refactor ]

-  const handleMouseUp = React.useCallback(() => {
+  const handleMouseUp = React.useCallback(() => {
     if (isDragging && !disabled) {
       setIsDragging(false);

       // Get the final size after resize
-      if (container) {
-        const rect = container.getBoundingClientRect();
+      const container = containerRef?.current;
+      if (container) {
+        const rect = container.getBoundingClientRect();
         const finalSize = isHorizontal ? rect.width : rect.height;
         onResizeEnd?.(finalSize);
       }
     }
-  }, [isDragging, disabled, container, onResizeEnd, isHorizontal]);
+  }, [isDragging, disabled, containerRef, onResizeEnd, isHorizontal]);

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

♻️ Duplicate comments (2)
src/DrawerPopup.tsx (2)

358-362: overflow 样式设置逻辑可能需要调整

当前实现中,resizabletrue 时设置 overflow: 'visible',这可能会导致内容溢出。建议保持 overflow: 'hidden',因为内部内容区域已经有独立的滚动处理。

 if (resizable) {
-  style.overflow = 'visible';
+  style.overflow = 'hidden';
 } else {
   style.overflow = 'hidden';
 }

333-345: 容器引用传递问题会导致拖拽起始尺寸测量失败

当前通过 container: wrapperRef.current 传递的是值而非引用,首次渲染时 wrapperRef.currentnull,这会导致 useDrag hook 内部无法正确获取容器元素,进而影响拖拽尺寸的计算。

修改为传递 ref 引用:

 const { dragElementProps, isDragging } = useDrag({
   prefixCls: `${prefixCls}-resizable`,
   direction: placement,
   className: drawerClassNames?.dragger,
   style: styles?.dragger,
   minSize: 0,
   maxSize,
   disabled: !resizable,
-  container: wrapperRef.current,
+  containerRef: wrapperRef,
   onResize: handleResize,
   onResizeStart: handleResizeStart,
   onResizeEnd: handleResizeEnd,
 });

同时需要更新 useDrag hook 的接口定义,将 container 参数改为 containerRef: React.RefObject<HTMLElement>

🧹 Nitpick comments (3)
src/DrawerPopup.tsx (3)

302-312: calculateMaxSize 可能在父容器未渲染时计算不准确

calculateMaxSize 方法依赖于父容器的尺寸,但在某些情况下(如初始渲染或父容器尺寸变化)可能获取不到准确的尺寸。建议添加边界检查和重试机制。

 const calculateMaxSize = React.useCallback(() => {
   if (wrapperRef.current) {
     const rect = wrapperRef.current.parentElement?.getBoundingClientRect();
+    if (!rect || rect.width === 0 || rect.height === 0) {
+      // 延迟重试,等待容器渲染完成
+      requestAnimationFrame(() => calculateMaxSize());
+      return;
+    }
     const newMaxSize =
       placement === 'left' || placement === 'right'
         ? (rect?.width ?? 0)
         : (rect?.height ?? 0);
     setMaxSize(newMaxSize);
   }
 }, [placement]);

368-370: 需要监听容器尺寸变化

当前仅在初始化时计算 maxSize,如果容器尺寸发生变化(如窗口大小调整),maxSize 不会更新。建议添加 ResizeObserver 监听容器尺寸变化。

 // Initialize maxSize calculation
 React.useEffect(() => {
   calculateMaxSize();
+  
+  if (!wrapperRef.current?.parentElement) return;
+  
+  const resizeObserver = new ResizeObserver(() => {
+    calculateMaxSize();
+  });
+  
+  resizeObserver.observe(wrapperRef.current.parentElement);
+  
+  return () => {
+    resizeObserver.disconnect();
+  };
 }, [calculateMaxSize]);

416-416: 拖拽手柄需要添加无障碍属性

拖拽手柄缺少必要的无障碍属性,如 rolearia-label 等,这会影响屏幕阅读器用户的使用体验。

-{resizable && <div {...dragElementProps} />}
+{resizable && (
+  <div 
+    {...dragElementProps}
+    role="separator"
+    aria-label="调整抽屉大小"
+    aria-orientation={placement === 'left' || placement === 'right' ? 'vertical' : 'horizontal'}
+    aria-valuemin={0}
+    aria-valuemax={maxSize}
+    aria-valuenow={currentSize}
+    tabIndex={0}
+  />
+)}
📜 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 e16a142 and dac7c38.

📒 Files selected for processing (2)
  • docs/examples/assets/motion.less (1 hunks)
  • src/DrawerPopup.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/examples/assets/motion.less
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/DrawerPopup.tsx (1)
src/hooks/useDrag.ts (1)
  • useDrag (28-161)

@codecov
Copy link

codecov bot commented Aug 15, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (2418e05) to head (4da4ce2).
⚠️ Report is 4 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##            master      #528   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            5         6    +1     
  Lines          144       216   +72     
  Branches        53        79   +26     
=========================================
+ Hits           144       216   +72     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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 (1)
README.md (1)

74-76: onResize/Start/End 描述存在误导,建议更正措辞并明确返回参数含义

  • onResize 的描述明显是从键盘事件复制而来,容易误解。
  • 建议明确 size 的语义(px,且随 placement 决定是宽度或高度),并统一三者的触发时机说明。

可在 README 中对这三行做如下修改:

-| onResize           | (size: number) => void                                                      | -                                      | Trigger when mouse keyup on drawer panel
+| onResize           | (size: number) => void                                                      | -                                      | Fires while resizing via dragging. size is current width/height in px depending on placement.
-| onResizeStart      | () => void                                                                  | -                                      | Trigger when resize starts
+| onResizeStart      | () => void                                                                  | -                                      | Fires when resizing starts (mouse/touch down on the dragger).
-| onResizeEnd        | () => void                                                                  | -                                      | Trigger when resize ends
+| onResizeEnd        | () => void                                                                  | -                                      | Fires when resizing ends (mouse/touch up). Use onResize for latest size.

如实现里希望在结束时也拿到最终尺寸,可考虑将 onResizeEnd 的签名扩展为 (size: number) => void(需与实现保持一致后再改文档)。

📜 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 5341916 and 5592224.

📒 Files selected for processing (1)
  • README.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
README.md

[grammar] ~68-~68: There might be a mistake here.
Context: ... | | onMouseOver | React.MouseEventH...

(QB_NEW_EN)

@thinkasany
Copy link
Contributor

覆盖率掉了
image

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

♻️ Duplicate comments (1)
src/DrawerPopup.tsx (1)

390-401: resizable 时建议使用 overflow: visible,以确保拖拽条可见且可交互

注释已说明“允许 dragger 超出容器可见”,但代码两分支都设置为 'hidden',与目标相悖。过去也有机器人指出 overflow 值问题(此前有人误用 'none')。建议如下:

   const dynamicWrapperStyle = React.useMemo(() => {
     const style: React.CSSProperties = { ...wrapperStyle };
 
     if (resizable) {
-      // Allow dragger to be visible outside wrapper bounds
-      style.overflow = 'hidden';
+      // Allow dragger to be visible outside wrapper bounds
+      style.overflow = 'visible';
     } else {
       style.overflow = 'hidden';
     }
 
     return style;
   }, [wrapperStyle, resizable]);

请本地验证:拖拽把手是否需要超出内容区域;若样式层通过绝对定位/负 margin 已保证在可视区内,亦可保留 hidden。建议配合 CSS 查看 dragger 的定位与命中区域是否受影响。

🧹 Nitpick comments (4)
src/DrawerPopup.tsx (4)

367-375: 转发尺寸参数:onResizeStart/onResizeEnd 不应丢弃 size

内部 useDrag 会传入 startSize/finalSize,当前实现未接收参数,导致信息丢失。建议按下调整:

-  const handleResizeStart = React.useCallback(() => {
-    calculateMaxSize();
-    resizable?.onResizeStart?.();
-  }, [resizable, calculateMaxSize]);
+  const handleResizeStart = React.useCallback(
+    (startSize: number) => {
+      calculateMaxSize();
+      resizable?.onResizeStart?.(startSize);
+    },
+    [resizable, calculateMaxSize],
+  );
 
-  const handleResizeEnd = React.useCallback(() => {
-    resizable?.onResizeEnd?.();
-  }, [resizable]);
+  const handleResizeEnd = React.useCallback(
+    (endSize: number) => {
+      resizable?.onResizeEnd?.(endSize);
+    },
+    [resizable],
+  );

403-405: 窗口尺寸变化时重新计算 maxSize,避免约束失效

当前仅在挂载/开始拖拽时计算 maxSize,浏览器窗口缩放或父容器尺寸变化会导致约束过时。建议在 resize 事件中同步:

-  React.useEffect(() => {
-    calculateMaxSize();
-  }, [calculateMaxSize]);
+  React.useEffect(() => {
+    calculateMaxSize();
+    const handler = () => calculateMaxSize();
+    window.addEventListener('resize', handler);
+    return () => window.removeEventListener('resize', handler);
+  }, [calculateMaxSize]);

如果 Drawer 常嵌套在可变尺寸容器中,可考虑用 ResizeObserver 监听 wrapperRef.current.parentElement,进一步提升鲁棒性。需要我补一版 useSizeObserver 吗?

Also applies to: 349-357


327-347: 数值字符串到 number 的一致化处理(微调)

updateCurrentSize 仅在 typeof targetSize === 'number' 时更新内部值;若外部以 '300'(字符串)传入,在此不会初始化 currentSize。为了与后续 parseWidthHeight 保持一致,可在此也进行一次解析:

-    if (typeof targetSize === 'number') {
-      setCurrentSize(targetSize);
-    } else {
-      setCurrentSize(undefined);
-    }
+    const parsed = parseWidthHeight(targetSize);
+    if (typeof parsed === 'number') {
+      setCurrentSize(parsed);
+    } else {
+      setCurrentSize(undefined);
+    }

451-451: 为拖拽条增加基础无障碍语义(A11y)

给 dragger 增加 role="separator" 和方向,有助于读屏器理解其用途。后续可考虑键盘箭头调整尺寸。

-            {resizable && <div {...dragElementProps} />}
+            {resizable && (
+              <div
+                {...dragElementProps}
+                role="separator"
+                aria-orientation={isHorizontal ? 'vertical' : 'horizontal'}
+                aria-label="Resize drawer"
+              />
+            )}
📜 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 245b1ea and ded0c52.

📒 Files selected for processing (3)
  • README.md (2 hunks)
  • src/Drawer.tsx (4 hunks)
  • src/DrawerPopup.tsx (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/Drawer.tsx
  • README.md
🧰 Additional context used
🧬 Code graph analysis (1)
src/DrawerPopup.tsx (2)
src/util.ts (1)
  • parseWidthHeight (5-15)
src/hooks/useDrag.ts (1)
  • useDrag (28-158)
🔇 Additional comments (3)
src/DrawerPopup.tsx (3)

376-388: useDrag 的 containerRef 传递方式正确,避免初次渲染 null 带来的起始尺寸测量问题

此前通过 .current 传值会在首次渲染为 null,现在传入 ref 本身由 hook 内部读取,问题已规避。


438-449: 拖拽中屏蔽 motion className 的做法妥当

!isDragging && motionClassName 可避免拖拽过程与过渡动画相互干扰,体验更顺滑。


80-88: 无需同步类型或更新文档,样式对子组件 pointer-events 已正确设置

  • 类型定义:src/DrawerPopup.tsxsrc/Drawer.tsx 中对 resizable 的声明均为对象配置,并且示例(docs/examples/resizable.tsxdocs/demo/resizable.md)已使用对象写法,无需同步或变更。
  • 样式检查:全局样式中 .drawer-popup 根元素虽设置 pointer-events: none;,但对内层 .drawer-popup-inline.drawer-popup-content.drawer-popup-handle 等都明确覆盖为 pointer-events: auto;(assets/index.less 行 15、27、59、66),确保拖拽柄可正常接收事件。

该评论建议已验证,无需额外变更。

@thinkasany
Copy link
Contributor

thinkasany commented Aug 26, 2025

rc 的仓库第一次提交运行ci需要member ap 才行,有需要的话可以群里催一下。

或者手动@大佬帮忙,@yoyo837 @li-jia-nan

image

@li-jia-nan
Copy link
Member

ap了

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

Caution

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

⚠️ Outside diff range comments (1)
src/DrawerPopup.tsx (1)

500-535: 同步 DrawerProps.resizable 类型与文档

脚本检查未在 src/Drawer.tsxDrawerProps 中找到 resizable 定义,文档和示例也缺少用法。

  • src/Drawer.tsxDrawerProps 接口中添加 resizable? 联合类型(例如 boolean 或包含回调的对象类型)。
  • README.mddocs/(或对应 demo 示例)中新增 <Drawer resizable /> 的简写用法及其回调参数说明。
♻️ Duplicate comments (5)
src/DrawerPopup.tsx (5)

80-87: resizable 类型需支持布尔简写,回调参数签名需与 useDrag 对齐

目前仅定义为对象,且 onResizeStart/onResizeEnd 无参数;与 PR 目标(<Drawer resizable />)及 useDrag 行为不一致。建议:

  • 将类型改为 boolean | { … }
  • 将回调签名改为携带尺寸参数。

应用如下补丁:

   // resizable
   defaultWidth?: number | string;
   defaultHeight?: number | string;
-  resizable?: {
-    onResize?: (size: number) => void;
-    onResizeStart?: () => void;
-    onResizeEnd?: () => void;
-  };
+  resizable?: boolean | {
+    onResize?: (size: number) => void;
+    onResizeStart?: (startSize: number) => void;
+    onResizeEnd?: (endSize: number) => void;
+  };

399-407: 转发尺寸参数到 onResizeStart/onResizeEnd,保持与 useDrag 对齐

当前丢弃了 useDrag 传入的尺寸参数。

-  const handleResizeStart = React.useCallback(() => {
-    calculateMaxSize();
-    resizable?.onResizeStart?.();
-  }, [resizable, calculateMaxSize]);
+  const handleResizeStart = React.useCallback((startSize: number) => {
+    calculateMaxSize();
+    resizableCfg?.onResizeStart?.(startSize);
+  }, [resizableCfg, calculateMaxSize]);
 
-  const handleResizeEnd = React.useCallback(() => {
-    resizable?.onResizeEnd?.();
-  }, [resizable]);
+  const handleResizeEnd = React.useCallback((endSize: number) => {
+    resizableCfg?.onResizeEnd?.(endSize);
+  }, [resizableCfg]);

422-433: overflow 注释与实现不一致,应在可拖拽时使用 visible

注释写明“允许 dragger 超出 wrapper 可见”,但实现两分支均为 'hidden'。请修正:

   const dynamicWrapperStyle = React.useMemo(() => {
     const style: React.CSSProperties = { ...wrapperStyle };
 
-    if (resizable) {
-      // Allow dragger to be visible outside wrapper bounds
-      style.overflow = 'hidden';
-    } else {
-      style.overflow = 'hidden';
-    }
+    if (enableResizable) {
+      // Allow dragger to be visible outside wrapper bounds
+      style.overflow = 'visible';
+    } else {
+      style.overflow = 'hidden';
+    }
 
     return style;
-  }, [wrapperStyle, resizable]);
+  }, [wrapperStyle, enableResizable]);

388-397: 受控模式下不应更新内部状态,仅透传回调

与上文统一,避免受控场景下内部状态污染。

-  const handleResize = React.useCallback(
-    (size: number) => {
-      // In controlled mode, only trigger callback without updating internal state
-      if (!isControlledMode) {
-        setCurrentSize(size);
-      }
-      resizable?.onResize?.(size);
-    },
-    [resizable, isControlledMode],
-  );
+  const handleResize = React.useCallback(
+    (size: number) => {
+      if (!isControlled) {
+        setCurrentSize(size);
+      }
+      resizableCfg?.onResize?.(size);
+    },
+    [resizableCfg, isControlled],
+  );

311-343: 尺寸优先级需调整为“受控优先 → 内部拖拽态 → 默认值”

当前把 currentSize 放在 width/height 之前,受控模式下可能被内部状态覆盖。建议简化并修正优先级:

-  if (isHorizontal) {
-    let finalWidth: number | string;
-    if (isControlledMode) {
-      // In controlled mode, prioritize external width
-      finalWidth = width !== undefined ? width : defaultWidth;
-    } else if (currentSize !== undefined) {
-      // In uncontrolled mode, user dragged size takes precedence
-      finalWidth = currentSize;
-    } else if (width !== undefined) {
-      // Fallback to provided width
-      finalWidth = width;
-    } else {
-      // Final fallback to default
-      finalWidth = defaultWidth;
-    }
-    wrapperStyle.width = parseWidthHeight(finalWidth);
-  } else {
-    let finalHeight: number | string;
-    if (isControlledMode) {
-      // In controlled mode, prioritize external height
-      finalHeight = height !== undefined ? height : defaultHeight;
-    } else if (currentSize !== undefined) {
-      // In uncontrolled mode, user dragged size takes precedence
-      finalHeight = currentSize;
-    } else if (height !== undefined) {
-      // Fallback to provided height
-      finalHeight = height;
-    } else {
-      // Final fallback to default
-      finalHeight = defaultHeight;
-    }
-    wrapperStyle.height = parseWidthHeight(finalHeight);
-  }
+  if (isHorizontal) {
+    const finalWidth = (width ?? currentSize ?? defaultWidth);
+    wrapperStyle.width = parseWidthHeight(finalWidth);
+  } else {
+    const finalHeight = (height ?? currentSize ?? defaultHeight);
+    wrapperStyle.height = parseWidthHeight(finalHeight);
+  }
🧹 Nitpick comments (6)
tests/index.spec.tsx (4)

7-7: 测试直接引入内部 Hook,建议隔离为独立用例或通过公开 API 验证

直接从 src/hooks/useDrag 引入会放大内部重构对用例的脆弱性。建议:

  • 将此用例迁移到专门的 hooks 测试文件;
  • 或仅通过 <Drawer resizable /> 的公开 API 验证回退到容器尺寸的逻辑。

571-637: min/max 约束用例建议同时断言回调与最终 DOM 一致

当前仅断言最终 DOM 宽度到 0 与 500。建议补充断言 onResize 在越界时收到的值已被 clamp(尤其是 maxSize=父容器 500 的路径)。


720-750: props 优先级用例合理,但建议新增 resizable 布尔简写用例

当前验证了 prop 优先 default 的优先级。根据 PR 目标与讨论,<Drawer resizable /> 布尔简写应受支持,建议补充对应用例以防回归。


752-802: 请在受控模式下补充拖拽尺寸不变的断言并确认 onResize 回调被触发

当前用例只通过 rerender 验证了受控更新,但并未覆盖“拖拽不会修改受控宽高”这一行为。建议在 tests/index.spec.tsx 中,针对宽度和高度的受控分支,各自加入拖拽模拟并断言:

  • 选中拖拽句柄 .rc-drawer-resizable-dragger(已在全局样式/组件中定义)
  • 通过 fireEvent.mouseDownmouseMovemouseUp 模拟拖拽
  • 期望 DOM 尺寸保持受控值不变
  • 同时验证注入的 onResize 回调被调用

示例 Diff(仅示意,行号请根据实际位置调整):

@@ tests/index.spec.tsx:752
-    const { rerender: rerender1, unmount: unmount1 } = render(
+    const onResize1 = jest.fn();
+    const { rerender: rerender1, unmount: unmount1 } = render(
       <Drawer
         open
         placement="left"
         width={200}
-        resizable={{ onResize: () => {} }}
+        resizable={{ onResize: onResize1 }}
       >
@@ tests/index.spec.tsx:760
     rerender1(
       <Drawer
         open
         placement="left"
         width={300}
-        resizable={{ onResize: () => {} }}
+        resizable={{ onResize: onResize1 }}
       >
@@ tests/index.spec.tsx:765
-    expect(contentWrapper).toHaveStyle({ width: '300px' });
+    expect(contentWrapper).toHaveStyle({ width: '300px' });
+    // 模拟拖拽:受控宽度不应变化,且 onResize1 被调用
+    const dragger1 = document.querySelector('.rc-drawer-resizable-dragger')!;
+    fireEvent.mouseDown(dragger1, { clientX: 200 });
+    fireEvent.mouseMove(document, { clientX: 400 });
+    fireEvent.mouseUp(document, { clientX: 400 });
+    expect(contentWrapper).toHaveStyle({ width: '300px' });
+    expect(onResize1).toHaveBeenCalled();

@@ tests/index.spec.tsx:775
-    const { rerender: rerender2, unmount: unmount2 } = render(
+    const onResize2 = jest.fn();
+    const { rerender: rerender2, unmount: unmount2 } = render(
       <Drawer
         open
         placement="top"
         height={150}
-        resizable={{ onResize: () => {} }}
+        resizable={{ onResize: onResize2 }}
       >
@@ tests/index.spec.tsx:783
-    expect(contentWrapper).toHaveStyle({ height: '250px' });
+    expect(contentWrapper).toHaveStyle({ height: '250px' });
+    // 模拟拖拽:受控高度不应变化,且 onResize2 被调用
+    const dragger2 = document.querySelector('.rc-drawer-resizable-dragger')!;
+    fireEvent.mouseDown(dragger2, { clientY: 200 });
+    fireEvent.mouseMove(document, { clientY: 100 });
+    fireEvent.mouseUp(document, { clientY: 100 });
+    expect(contentWrapper).toHaveStyle({ height: '250px' });
+    expect(onResize2).toHaveBeenCalled();
src/DrawerPopup.tsx (2)

356-372: 在受控模式下应避免回写内部 currentSize

当前会根据 width/height/default* 设定 currentSize,可能与受控意图冲突。建议在受控模式下清空内部 currentSize,其起始值由 DOM 量测保证;非受控再按默认值/props number 初始化。

-  const updateCurrentSize = React.useCallback(() => {
+  const updateCurrentSize = React.useCallback(() => {
+    if (isControlled) {
+      setCurrentSize(undefined);
+      return;
+    }
     let targetSize: number | string | undefined;
@@
-    if (typeof targetSize === 'number') {
+    if (typeof targetSize === 'number') {
       setCurrentSize(targetSize);
     } else {
       setCurrentSize(undefined);
     }
-  }, [isHorizontal, width, height, defaultWidth, defaultHeight]);
+  }, [isHorizontal, width, height, defaultWidth, defaultHeight, isControlled]);

483-483: 为拖拽把手补充无障碍语义与可点交互保证

建议给把手增加语义与交互属性,提升可访问性并防止外层 pointer-events 风险时失效:

-            {resizable && <div {...dragElementProps} />}
+            {enableResizable && (
+              <div
+                {...dragElementProps}
+                role="separator"
+                aria-orientation={isHorizontal ? 'vertical' : 'horizontal'}
+                // 确保可交互
+                style={{ pointerEvents: 'auto', ...dragElementProps.style }}
+              />
+            )}

同时请在样式侧确认未对 wrapper 施加 pointer-events: none(社区已有反馈),避免拖拽失效。

📜 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 ded0c52 and a0087a8.

📒 Files selected for processing (2)
  • src/DrawerPopup.tsx (7 hunks)
  • tests/index.spec.tsx (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/DrawerPopup.tsx (2)
src/util.ts (1)
  • parseWidthHeight (5-15)
src/hooks/useDrag.ts (1)
  • useDrag (28-158)
tests/index.spec.tsx (1)
src/hooks/useDrag.ts (1)
  • useDrag (28-158)
🔇 Additional comments (6)
tests/index.spec.tsx (4)

639-687: 纵向拖拽用例覆盖良好

通过 placement="top" 验证了 Y 轴拖拽方向与数值更新,断言清晰可读。LGTM。


688-718: 默认尺寸兜底覆盖到位

验证了 number 与 string 两条路径下的默认宽/高设置,覆盖恰当。LGTM。


804-854: 直接验证 useDrag 回退容器尺寸的用例很有价值

用例严谨验证了 currentSize === undefined 时的起始尺寸与回调参数,覆盖到 X 轴位移与最终 endSize。LGTM。


480-569: 我已添加脚本以验证 DrawerProps 中的 width 属性声明,以及 DrawerPopup 中 isControlledMode 的实现逻辑,以确认当前测试对“受控/非受控”模式的处理是否符合预期。执行结果出来后,我们将据此决定是否需要修改测试或添加新的断言。

src/DrawerPopup.tsx (2)

408-420: useDrag 集成整体 OK

containerRefmaxSizecurrentSize 与回调链路衔接正确,isDragging 用于抑制 motion 类名也合理。LGTM。


471-475: 拖拽期间抑制过渡动画的处理很到位

!isDragging && motionClassName 规避了拖拽与过渡的冲突。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: 2

♻️ Duplicate comments (3)
src/DrawerPopup.tsx (3)

80-87: resizable 改为 boolean | { … },并让回调携带尺寸参数

PR 目标与社区期望是 <Drawer resizable /> 简写;当前类型仅对象且 onResizeStart/onResizeEnd 无尺寸参数,API 与实现和需求不一致。

   // resizable
   defaultWidth?: number | string;
   defaultHeight?: number | string;
-  resizable?: {
-    onResize?: (size: number) => void;
-    onResizeStart?: () => void;
-    onResizeEnd?: () => void;
-  };
+  resizable?: boolean | {
+    onResize?: (size: number) => void;
+    onResizeStart?: (startSize: number) => void;
+    onResizeEnd?: (endSize: number) => void;
+  };

同时请在 README/类型导出处同步更新。


137-142: 区分启用标志与配置对象,避免联合类型属性访问风险

在联合类型落地后请引入派发变量,后续只从 resizableCfg 读取回调属性。

   styles,
   drawerRender,
-  resizable,
+  resizable,
   defaultWidth,
   defaultHeight,
 } = props;
+const enableResizable = !!resizable;
+const resizableCfg = typeof resizable === 'object' ? resizable : undefined;

368-375: 透传尺寸参数:与 useDrag 对齐,并从 resizableCfg 读取

useDrag 会传入 startSize/finalSize,当前实现丢弃了该信息。建议统一携带尺寸参数,便于外部受控更新。

-const handleResizeStart = React.useCallback(() => {
-  calculateMaxSize();
-  resizable?.onResizeStart?.();
-}, [resizable, calculateMaxSize]);
+const handleResizeStart = React.useCallback(
+  (startSize: number) => {
+    calculateMaxSize();
+    resizableCfg?.onResizeStart?.(startSize);
+  },
+  [resizableCfg, calculateMaxSize],
+);
 
-const handleResizeEnd = React.useCallback(() => {
-  resizable?.onResizeEnd?.();
-}, [resizable]);
+const handleResizeEnd = React.useCallback(
+  (endSize: number) => {
+    resizableCfg?.onResizeEnd?.(endSize);
+  },
+  [resizableCfg],
+);
🧹 Nitpick comments (3)
src/DrawerPopup.tsx (3)

322-324: maxSizewrapperRef 的引入是必要的;可考虑监听父容器尺寸变更

为了在容器尺寸变化(窗口缩放、布局变更)时动态更新 maxSize,建议用 ResizeObserver 监听 wrapperRef.current.parentElement

可以在 calculateMaxSize 附近增加:

+React.useEffect(() => {
+  const parent = wrapperRef.current?.parentElement;
+  if (!parent) return;
+  const ro = new ResizeObserver(() => calculateMaxSize());
+  ro.observe(parent);
+  return () => ro.disconnect();
+}, [calculateMaxSize]);

347-355: maxSize 计算正确;可补空父节点兜底

已用可选链处理父节点为空。可进一步增加早退以避免多余 setState。

-  if (wrapperRef.current) {
+  if (wrapperRef.current?.parentElement) {
     const rect = wrapperRef.current.parentElement?.getBoundingClientRect();

452-452: 渲染条件与可访问性增强

  • 建议改用 enableResizable 作为渲染条件以配合联合类型。
  • 为拖拽条增加 ARIA 语义,提升可访问性(屏幕阅读器可识别为分隔条)。
-{resizable && <div {...dragElementProps} />}
+{enableResizable && (
+  <div
+    role="separator"
+    aria-orientation={isHorizontal ? 'vertical' : 'horizontal'}
+    aria-valuemin={0}
+    aria-valuemax={maxSize || undefined}
+    aria-valuenow={typeof currentSize === 'number' ? currentSize : undefined}
+    {...dragElementProps}
+  />
+)}
📜 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 a0087a8 and ba6e05a.

📒 Files selected for processing (2)
  • src/DrawerPopup.tsx (7 hunks)
  • tests/index.spec.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/index.spec.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
src/DrawerPopup.tsx (2)
src/util.ts (1)
  • parseWidthHeight (5-15)
src/hooks/useDrag.ts (1)
  • useDrag (28-158)
🔇 Additional comments (6)
src/DrawerPopup.tsx (6)

14-14: 引入 useDrag 没问题

依赖位置与用途匹配,命名规范。


264-267: currentSize 与方向判定初始化合理

本地状态与 placement 推导出的 isHorizontal 清晰,符合后续计算使用。


288-312: 受控/非受控优先级正确

优先使用受控的 width/height,再回退到拖拽态 currentSize,最后才是默认值,符合预期。


325-346: 初始化/同步 currentSize 的策略合理

仅在数值型时写入 currentSize,其它情况回退到实时测量,契合拖拽以像素为单位的实现。


377-389: useDrag 参数传递正确

已使用 containerRef: wrapperRef(避免初次渲染取空值)、传入 currentSize、方向与样式类名均合理。


439-449: 拖拽时关闭动效类名,合适的 UX 处理

!isDragging && motionClassName 可避免拖拽过程与动效冲突。

@socket-security
Copy link

socket-security bot commented Sep 3, 2025

@socket-security
Copy link

socket-security bot commented Sep 3, 2025

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
[email protected] has Obfuscated code.

Confidence: 0.94

Location: Package overview

From: ?npm/@rc-component/[email protected]npm/[email protected]

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at [email protected].

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/[email protected]. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

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

Caution

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

⚠️ Outside diff range comments (1)
src/DrawerPopup.tsx (1)

123-148: 区分启用标志与配置对象,避免布尔分支误用属性

在联合类型下直接访问 resizable?.onResize* 有类型与运行时风险。建议引入派发变量:

     styles,
     drawerRender,
-    resizable,
+    resizable,
     defaultSize,
   } = props;
+
+  // 派发:启用标志 & 配置对象
+  const enableResizable = !!resizable;
+  const resizableCfg = typeof resizable === 'object' ? resizable : undefined;
♻️ Duplicate comments (3)
src/Drawer.tsx (1)

29-48: 移除重复声明,避免与 DrawerPopupProps 漂移

DrawerProps 已继承 Omit<DrawerPopupProps, ...>,此处再次声明 width/height/size/maxSize/defaultSize/resizable 会造成维护冗余与类型漂移风险。建议直接依赖 DrawerPopupProps 的单一来源定义,并将“弃用”注释迁移到 DrawerPopupProps。

   styles?: DrawerStyles;
-  /**
-   * @deprecated Use `size` instead. Will be removed in next major version.
-   */
-  width?: number | string;
-  /**
-   * @deprecated Use `size` instead. Will be removed in next major version.
-   */
-  height?: number | string;
-  /** Size of the drawer (width for left/right placement, height for top/bottom placement) */
-  size?: number | string;
-  /** Maximum size of the drawer */
-  maxSize?: number;
-  /** Default size for uncontrolled resizable drawer */
-  defaultSize?: number | string;
-  /** Resizable configuration - object with optional callbacks */
-  resizable?: {
-    onResize?: (size: number) => void;
-    onResizeStart?: () => void;
-    onResizeEnd?: () => void;
-  };
src/DrawerPopup.tsx (2)

85-93: 将 resizable 改为联合类型并统一回调签名(携带尺寸)

为支持 <Drawer resizable /> 简写并与 useDrag 回调对齐,建议:

  • 类型:resizable?: boolean | { onResize?(size:number):void; onResizeStart?(start:number):void; onResizeEnd?(end:number):void }
  • 文档与示例同步更新(回调均携带尺寸参数)。
   /** Default size for uncontrolled resizable drawer */
   defaultSize?: number | string;
-  resizable?: {
-    onResize?: (size: number) => void;
-    onResizeStart?: () => void;
-    onResizeEnd?: () => void;
-  };
+  resizable?: boolean | {
+    onResize?: (size: number) => void;
+    onResizeStart?: (startSize: number) => void;
+    onResizeEnd?: (endSize: number) => void;
+  };

350-361: 修正 overflow:可调整时 visible,非可调整时 auto

当前两分支均为 'hidden',会剪裁拖拽柄且丢失原有滚动行为。建议:

   const dynamicWrapperStyle = React.useMemo(() => {
     const style: React.CSSProperties = { ...wrapperStyle };
-    if (resizable) {
-      // Allow dragger to be visible outside wrapper bounds
-      style.overflow = 'hidden';
-    } else {
-      style.overflow = 'hidden';
-    }
+    if (enableResizable) {
+      // 允许拖拽柄溢出可见
+      style.overflow = 'visible';
+    } else {
+      // 保持内容区域的滚动
+      style.overflow = 'auto';
+    }
     return style;
-  }, [wrapperStyle, resizable]);
+  }, [wrapperStyle, enableResizable]);
🧹 Nitpick comments (11)
tsconfig.json (1)

16-16: 限制编译范围,避免将 .dumi/docs/测试示例编入构建包

当前 "include": [".dumirc.ts", "**/*.ts", "**/*.tsx"] 可能把 .dumi/docs/、测试和临时示例(如 test-todo.tsx)一起编译,增加构建时长与发布体积,且易引入类型冲突。建议仅包含源码目录,并显式排除非产物目录。

可按需调整(示例):

-  "include": [".dumirc.ts", "**/*.ts", "**/*.tsx"]
+  "include": ["src/**/*"],
+  "exclude": [
+    "node_modules",
+    "dist",
+    "coverage",
+    "docs",
+    ".dumi",
+    "tests",
+    "test-todo.tsx",
+    "**/*.spec.ts",
+    "**/*.spec.tsx"
+  ]
tests/index.spec.tsx (5)

7-7: 将 useDrag 的直测拆分到独立用例,降低耦合

此处直接引入内部 hook 进行交互验证是合理的,但建议单独创建 tests/useDrag.spec.tsx,让 Drawer 行为测试与 hook 单测解耦,定位失败更清晰。


480-564: 补充断言以提高水平拖拽用例的信噪比

当前仅校验回调触发与 onResize 最终值,建议再断言:

  • onResizeStart 的起始尺寸(应为 200)
  • 拖拽句柄的方向类名存在(如 .rc-drawer-resizable-dragger-right-horizontal

可追加:

 expect(onResizeStart).toHaveBeenCalledTimes(1);
+expect(onResizeStart).toHaveBeenCalledWith(200);
+expect(dragger).toHaveClass('rc-drawer-resizable-dragger-horizontal');

566-630: 避免使用负坐标,降低环境相关性

mouseMove 使用 clientX: -100 在部分环境(如 JSDOM 版本差异、辅助工具)可能表现不一致。建议改为明显小于起点的正坐标(例如 0 或 50)来表达“向左拖动”的语义。

-fireEvent.mouseMove(document, { clientX: -100 });
+fireEvent.mouseMove(document, { clientX: 50 });

632-676: 补一条不变项断言,确保仅高度变化

为防止垂直拖拽误改宽度,可在断言高度前后,补充宽度未变化的断言,增强回归保护。

 expect(document.querySelector('.rc-drawer-content-wrapper')).toHaveStyle({
   height: '100px',
 });
+expect(document.querySelector('.rc-drawer-content-wrapper')).toHaveStyle({
+  width: '200px',
+});

348-363: 新增用例:带 px 的字符串不应产生警告

我们已对 parseWidthHeight 调整以避免 "200px" 触发误报警。建议增加一个与“纯数字字符串报警”相对的用例,覆盖不报警路径。

示例:

it('does not warn for string with px unit', () => {
  resetWarned();
  const errSpy = jest.spyOn(console, 'error');
  const { container } = render(<Drawer width="200px" open getContainer={false} />);
  expect(container.querySelector('.rc-drawer-content-wrapper')).toHaveStyle({
    width: '200px',
  });
  expect(errSpy).not.toHaveBeenCalled();
  errSpy.mockRestore();
});
src/Drawer.tsx (1)

146-157: 透传字段完整性 OK,但请与上游类型统一为联合类型

当你在 DrawerPopupProps 中将 resizable 改为 boolean | { … } 后,这里的透传无需调整,但请确保不在本文件重复定义类型(参考上方评论)。

src/DrawerPopup.tsx (2)

270-287: mergedSize 逻辑清晰,但避免重复 parse

mergedSize 已经通过 parseWidthHeight 归一化为 number;后续计算样式时无需再次 parse,可减少重复工作。

   const mergedSize = React.useMemo(() => {
     const legacySize = isHorizontal ? width : height;
     const nextMergedSize =
       size ??
       legacySize ??
       currentSize ??
       defaultSize ??
       (isHorizontal ? 378 : undefined);
-    return parseWidthHeight(nextMergedSize);
+    return parseWidthHeight(nextMergedSize);
   }, [size, width, height, defaultSize, isHorizontal, currentSize]);
@@
-  if (isHorizontal) {
-    nextWrapperStyle.width = parseWidthHeight(mergedSize);
-  } else {
-    nextWrapperStyle.height = parseWidthHeight(mergedSize);
-  }
+  if (isHorizontal) {
+    nextWrapperStyle.width = mergedSize;
+  } else {
+    nextWrapperStyle.height = mergedSize;
+  }

350-350: 可选:为拖拽柄补充可访问性属性

建议为拖拽柄添加 role="separator"aria-orientation(基于 isHorizontal)以及描述性 aria-label,提升可访问性。

src/hooks/useDrag.ts (2)

86-94: 健壮性:maxSize 判断使用显式数值校验

if (maxSize && newSize > maxSize)maxSize=0 时失效。建议使用显式数值判断。

-    if (maxSize && newSize > maxSize) {
+    if (typeof maxSize === 'number' && Number.isFinite(maxSize) && newSize > maxSize) {
       newSize = maxSize;
     }

98-109: 可选:无容器节点时也触发 onResizeEnd

containerRef.current 为空时可回退以 startSize 作为最终值,避免漏掉结束回调。

   const handleMouseUp = React.useCallback(() => {
     if (isDragging) {
       setIsDragging(false);
       // Get the final size after resize
-      if (containerRef?.current) {
+      if (containerRef?.current) {
         const rect = containerRef.current.getBoundingClientRect();
         const finalSize = isHorizontal ? rect.width : rect.height;
         onResizeEnd?.(finalSize);
-      }
+      } else {
+        onResizeEnd?.(startSize);
+      }
     }
-  }, [isDragging, containerRef, onResizeEnd, isHorizontal]);
+  }, [isDragging, containerRef, onResizeEnd, isHorizontal, startSize]);
📜 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 ba6e05a and 6780706.

📒 Files selected for processing (7)
  • src/Drawer.tsx (4 hunks)
  • src/DrawerPopup.tsx (8 hunks)
  • src/hooks/useDrag.ts (1 hunks)
  • src/util.ts (1 hunks)
  • test-todo.tsx (1 hunks)
  • tests/index.spec.tsx (2 hunks)
  • tsconfig.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
tests/index.spec.tsx (1)
src/hooks/useDrag.ts (1)
  • useDrag (28-142)
src/hooks/useDrag.ts (1)
src/Drawer.tsx (1)
  • Placement (15-15)
src/DrawerPopup.tsx (2)
src/util.ts (1)
  • parseWidthHeight (5-24)
src/hooks/useDrag.ts (1)
  • useDrag (28-142)
🔇 Additional comments (2)
src/Drawer.tsx (1)

58-76: Props 解构与透传 OK

新增 size/maxSize/defaultSize/resizable 的解构与透传到 DrawerPopup 一致,行为合理。

src/hooks/useDrag.ts (1)

28-41: 核心实现清晰,事件绑定与闭包处理得当

useEvent + 文档级监听配合良好,方向/位移与 startSize 计算正确。

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 (7)
src/DrawerPopup.tsx (7)

288-318: 溢出策略:可调整时 overflow: visible;否则保持滚动

为避免拖拽柄被裁剪,且在非可调整时保持原滚动行为,建议:

 const wrapperStyle: React.CSSProperties = React.useMemo(() => {
   const nextWrapperStyle: React.CSSProperties = {};
@@
   if (isHorizontal) {
     nextWrapperStyle.width = parseWidthHeight(mergedSize);
   } else {
     nextWrapperStyle.height = parseWidthHeight(mergedSize);
   }
+  // 允许拖拽柄超出 wrapper 可见;非可调整保持默认滚动
+  nextWrapperStyle.overflow = enableResizable ? 'visible' : 'auto';
   return nextWrapperStyle;
-}, [pushed, pushDistance, placement, isHorizontal, mergedSize]);
+}, [pushed, pushDistance, placement, isHorizontal, mergedSize, enableResizable]);

397-397: 渲染条件应使用 enableResizable,并补充无障碍属性

提升联合类型稳健性与可访问性。

-{resizable && <div {...dragElementProps} />}
+{enableResizable && (
+  <div
+    role="separator"
+    aria-orientation={isHorizontal ? 'vertical' : 'horizontal'}
+    {...dragElementProps}
+  />
+)}

85-93: 将 resizable 改为 boolean | { … },并让回调携带尺寸参数(与 PR 目标一致)

与 PR 目标 <Drawer resizable /> 不一致;且 onResizeStart/onResizeEnd 丢失尺寸参数,难以外部接入,亦与 useDrag 行为不对齐。建议如下:

   // resizable
   /** Default size for uncontrolled resizable drawer */
   defaultSize?: number | string;
-  resizable?: {
-    onResize?: (size: number) => void;
-    onResizeStart?: () => void;
-    onResizeEnd?: () => void;
-  };
+  resizable?: boolean | {
+    onResize?: (size: number) => void;
+    onResizeStart?: (startSize: number) => void;
+    onResizeEnd?: (endSize: number) => void;
+  };
#!/bin/bash
# 检查文档/示例是否使用布尔简写
rg -nP '\bresizable\b' -C2 --glob "*.{md,tsx,ts,jsx,js}" docs examples src

144-148: 区分启用标志与配置对象,避免联合类型属性误用

引入派发变量,后续仅从 resizableCfg 取回调;渲染与样式用 enableResizable

   styles,
   drawerRender,
-  resizable,
+  resizable,
   defaultSize,
 } = props;
+const enableResizable = !!resizable;
+const resizableCfg = typeof resizable === 'object' ? resizable : undefined;

270-273: 受控模式判定,避免内部状态污染受控尺寸

拖拽时应仅在非受控更新内部 currentSize

 const [currentSize, setCurrentSize] = React.useState<number>();
 const isHorizontal = placement === 'left' || placement === 'right';
+const isControlled =
+  size !== undefined ||
+  (isHorizontal ? width !== undefined : height !== undefined);

322-325: 仅在非受控时 setCurrentSize,且回调从 resizableCfg 读取

避免受控尺寸被内部状态覆盖;规避联合类型属性访问风险。

-const onInternalResize = useEvent((size: number) => {
-  setCurrentSize(size);
-  resizable?.onResize?.(size);
-});
+const onInternalResize = useEvent((next: number) => {
+  if (!isControlled) {
+    setCurrentSize(next);
+  }
+  resizableCfg?.onResize?.(next);
+});

336-338: 回调应转发自 resizableCfg,类型/行为与 useDrag 对齐

避免对布尔分支取属性;并保留尺寸参数。

-  onResizeStart: resizable?.onResizeStart,
-  onResizeEnd: resizable?.onResizeEnd,
+  onResizeStart: resizableCfg?.onResizeStart,
+  onResizeEnd: resizableCfg?.onResizeEnd,
🧹 Nitpick comments (1)
src/DrawerPopup.tsx (1)

57-61: 明确 size/maxSize 与 width/height 的优先级与受控语义

当前合并顺序看上去以 size 为最高优先级、其次 width/height。请在 README/API 中明确该约定,并补充用例(受控与非受控)。

是否需要我补一组测试:受控传入 width/heightsize 时拖拽不改变实际尺寸,仅触发回调?

📜 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 ab6fdf6 and 043326b.

📒 Files selected for processing (1)
  • src/DrawerPopup.tsx (8 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/DrawerPopup.tsx (2)
src/util.ts (1)
  • parseWidthHeight (5-24)
src/hooks/useDrag.ts (1)
  • useDrag (28-142)
🔇 Additional comments (4)
src/DrawerPopup.tsx (4)

14-18: 引入 useDrag / useEvent 合理

依赖选择正确,便于封装拖拽与稳定回调。


274-287: 合并尺寸逻辑整体 OK;垂直方向默认值需要确认

横向默认 378,纵向为 undefined。请确认是否需要为 top/bottom 也提供一致的默认高度(历史默认是否为 378)。


327-335: useDrag 参数(containerRef/currentSize/maxSize)传递正确

containerRef: wrapperRef 修正了首次渲染 ref.current 为空的问题;currentSize 透传有利于准确初始化。


382-389: 拖拽中禁用动效的处理合理

isDragging 时移除 motionClassName,避免拖拽与过渡冲突。

@zombieJ
Copy link
Member

zombieJ commented Sep 3, 2025

@QdabuliuQ 我做了以下改动:

  • 删除了动态检测 maxSize,在 getContainerparentElement 不一定是最大容器,按照父节点算最大尺寸是没有意义的。同时提供了 maxSize 属性让用户自定义。
  • 删除了 minSize,看代码里是 hard code 的,直接到 hooks 里改成了 0。
  • 删除了 overflow: hidden 样式,这两个看起来是一样的。转而添加了一个 -dragging className,如果有需要可以去 antd 里添加,不写死了:
截屏2025-09-03 14 55 35
  • 合并了 size defaultXXX 等等的逻辑为 mergedSize 从而简化下方应用样式的逻辑。
  • useCallback 替换为 useEvent 并移除 deps。
  • 将 handleResizeXXX 直接替换为用户配置的 resizable.onResizeXXX
  • 按照功能重新调整了代码顺序以分区。
  • 调整 parseWidthHeight 方法,现在合法的数字会直接返回数字类型。

diff: https://github.com/react-component/drawer/pull/528/files/ba6e05a5f5ce2a8f9898eaf2725ec34346ada784..043326bd7f7980e0af44e6b619b8b50ba2bd718c

@zombieJ zombieJ merged commit 9f2070c into react-component:master Sep 3, 2025
8 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Sep 22, 2025
17 tasks
classNames?: DrawerClassNames;
styles?: DrawerStyles;
/**
* @deprecated Use `size` instead. Will be removed in next major version.
Copy link
Member

Choose a reason for hiding this comment

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

这是可以直接替换的么?两个 props 的类型不一样,width 是支持 string 类型的,size 不支持 string

Copy link
Member

Choose a reason for hiding this comment

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

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.

[feature request]: Drawer supports resizable to custom drag width

6 participants