Skip to content

feat(carousel): add draggable carousel#4006

Open
ourcx wants to merge 5 commits into
opentiny:devfrom
ourcx:Feature—Carousel

Hidden character warning

The head ref may contain hidden characters: "Feature\u2014Carousel"
Open

feat(carousel): add draggable carousel#4006
ourcx wants to merge 5 commits into
opentiny:devfrom
ourcx:Feature—Carousel

Conversation

@ourcx

@ourcx ourcx commented Jan 19, 2026

Copy link
Copy Markdown
Contributor

PR

PR Checklist

Please check if your PR fulfills the following requirements:

  • The commit message follows our Commit Message Guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe:

What is the current behavior?

Issue Number: 3873

What is the new behavior?

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

增加了拖动可以切换走马灯,还有增加了文档的例子和测试

Summary by CodeRabbit

Release Notes

  • New Features

    • Added draggable prop to Carousel component, allowing users to drag to switch slides.
    • Added drag-and-drop functionality to Collapse component.
    • Implemented mouse interaction handlers for improved drag experience.
  • Documentation

    • Added demo examples for draggable Carousel and Collapse components.
  • Tests

    • Added end-to-end tests for draggable Carousel interactions.

@github-actions github-actions Bot added the enhancement New feature or request (功能增强) label Jan 19, 2026
@coderabbitai

coderabbitai Bot commented Jan 19, 2026

Copy link
Copy Markdown

Walkthrough

The pull request introduces drag-and-drop functionality to carousel and collapse components. It adds draggable props, mouse event handlers (mousedown, mousemove, mouseup, mouseleave) in the renderless layer, wires events to Vue components, includes new demo files and E2E tests, updates component documentation, and adds associated styling.

Changes

Cohort / File(s) Summary
Carousel Renderless Drag Logic
packages/renderless/src/carousel/index.ts, packages/renderless/src/carousel/vue.ts
Added mousedown, mousemove, mouseup, mouseleave event handlers for drag state management, pointer tracking, and drag direction/delta calculations. Introduced throttledArrowClick and throttledIndicatorHover exports. Wired drag events to Vue component API.
Carousel Component Integration
packages/vue/src/carousel/src/index.ts, packages/vue/src/carousel/src/pc.vue
Added draggable boolean prop (default true) to component. Bound mousedown/mousemove/mouseup/mouseleave events and conditional tiny-carousel--draggable class to root element. Added ref for programmatic access.
Carousel Theme Styling
packages/theme/src/carousel/index.less
Added new .tiny-carousel--draggable class with grabbing cursor and text-selection disabled styling.
Carousel API Metadata
examples/sites/demos/apis/carousel.js
Added draggable prop metadata with name, type (boolean), defaultValue (false), descriptions (zh-CN/en-US), mode (pc/mobile-first), and stable version 3.29.0. Updated aspect-ratio description spacing formatting.
Carousel Demo & Documentation
examples/sites/demos/pc/app/carousel/draggable.vue, examples/sites/demos/pc/app/carousel/webdoc/carousel.js, examples/sites/demos/pc/app/carousel/draggable.spec.ts
Added draggable.vue demo component showcasing TinyCarousel with draggable attribute and four items. Updated webdoc with new draggable demo entry (demoId, name, description, codeFiles, meta). Added Playwright E2E test verifying component visibility, active state, and mouse/touch event handling.
Collapse Draggable Demo & Documentation
examples/sites/demos/mobile-first/app/collapse/draggable.vue, examples/sites/demos/mobile-first/app/collapse/webdoc/collapse.js
Added draggable.vue demo component with TinyCollapse, four items, v-model binding, and change event handler displaying info modal. Updated webdoc with new draggable demo entry (demoId, name, description, codeFiles). Minor zh-CN formatting fixes in descriptions.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant PC.Vue as Carousel PC Component
    participant VueAPI as Vue Renderless API
    participant CoreLogic as Renderless Drag Logic
    participant State as Component State

    User->>PC.Vue: mousedown on carousel
    activate PC.Vue
    PC.Vue->>VueAPI: mousedown event triggered
    activate VueAPI
    VueAPI->>CoreLogic: mousedown({ props, state, api })
    activate CoreLogic
    CoreLogic->>State: Set isDragging = true
    CoreLogic->>State: Record pointer position
    CoreLogic->>State: Clear/pause timer
    deactivate CoreLogic
    deactivate VueAPI
    deactivate PC.Vue

    User->>PC.Vue: mousemove while dragging
    activate PC.Vue
    PC.Vue->>VueAPI: mousemove event triggered
    activate VueAPI
    VueAPI->>CoreLogic: mousemove({ props, state, vm })
    activate CoreLogic
    CoreLogic->>State: Calculate delta from movement
    CoreLogic->>State: Determine drag direction
    CoreLogic->>State: Update carousel items offset
    deactivate CoreLogic
    deactivate VueAPI
    deactivate PC.Vue

    User->>PC.Vue: mouseup to complete drag
    activate PC.Vue
    PC.Vue->>VueAPI: mouseup event triggered
    activate VueAPI
    VueAPI->>CoreLogic: mouseup({ props, state, api })
    activate CoreLogic
    CoreLogic->>State: Set isDragging = false
    CoreLogic->>State: Calculate slide change
    CoreLogic->>State: Restart timer
    CoreLogic->>State: Reset delta
    deactivate CoreLogic
    deactivate VueAPI
    deactivate PC.Vue
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰✨ A carousel now twirls at the touch,
Draggable slides, oh it matters so much!
With mousedown and up, we dance through the frames,
Collapse joins the fun with the draggable games!
From renderless logic to Vue, all aligned—
A hop, skip, and drag of the finest kind! 🎠

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat(carousel): add draggable carousel' clearly and directly summarizes the main change: adding draggable functionality to the carousel component, which aligns perfectly with all the modifications across the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • 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

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@examples/sites/demos/mobile-first/app/collapse/webdoc/collapse.js`:
- Around line 158-169: The demo description incorrectly refers to a "draggable
slot" but the feature is exposed via the draggable prop; update the descriptive
text in the demo entry with demoId 'draggable' (the desc values) to mention the
draggable prop instead of a slot (e.g., "by configuring the built-in draggable
prop of Collapse" for both 'zh-CN' and 'en-US') so the documentation matches the
implementation.

In `@examples/sites/demos/pc/app/carousel/draggable.spec.ts`:
- Around line 29-32: The test dispatches 'touchstart'/'touchend' via
carousel.dispatchEvent without supplying touch point data, but the component's
touch handler reads event.touches[0].clientX/Y; update the test to dispatch
proper TouchEvent objects that include a touches array with a Touch-like point
(clientX/clientY) so the handler can read coordinates. Replace the simple
dispatch calls for 'touchstart' and 'touchend' with creation of TouchEvent
instances (or a CustomEvent with a touches property) containing a touch point
matching the carousel element (refer to carousel.dispatchEvent and the
component's touchstart handler expecting event.touches[0].clientX/Y) and use
those events in the awaits.

In `@packages/renderless/src/carousel/index.ts`:
- Around line 399-404: The current code calls throttle(100, true, () => true)
inside a conditional, but throttle returns a throttled function (not a boolean),
so the early return is never reached; fix by creating the throttled wrapper once
(e.g., define a const throttledHandler = throttle(100, true, originalHandler)
during setup or module init) and then call throttledHandler() from the place
where the conditional currently exists (replace the if-block using throttle(...)
with a call to the pre-created throttled function); reference the throttle
function and the handler wrapper (throttledHandler/originalHandler) when
updating the code.

In `@packages/vue/src/carousel/src/pc.vue`:
- Around line 18-22: There are two `@mouseleave` handlers (handleMouseLeave and
mouseleave) which both fire on leave and can double-trigger timer logic; remove
the duplicate directive (remove `@mouseleave`="mouseleave") and consolidate
behavior by updating handleMouseLeave in the renderless layer to also perform
the drag cleanup currently in mouseleave (e.g., stop drag state, clear flags,
and call any drag-specific cleanup) so a single handler manages both timer
restart and drag cleanup consistently.
🧹 Nitpick comments (7)
packages/theme/src/carousel/index.less (1)

257-261: Align the draggable hook with the CSS prefix and drag-state cursor.

Hard-coding .tiny-carousel--draggable bypasses @{carousel-prefix-cls} overrides, and cursor: grabbing is usually reserved for the active drag state. Consider using the prefix variable and a grab/grabbing split.

♻️ Proposed refactor
-.tiny-carousel--draggable {
-    //鼠标效果
-    cursor: grabbing;
-    user-select: none;
-}
+.@{carousel-prefix-cls}--draggable {
+  // 鼠标效果
+  cursor: grab;
+  user-select: none;
+
+  &:active {
+    cursor: grabbing;
+  }
+}
examples/sites/demos/apis/statistic.js (1)

106-109: Represent the string default with quotes for clarity.

Other string defaults are shown with quotes (e.g., 'hover'). Using defaultValue: ',' may render ambiguously; consider defaultValue: "','".

♻️ Proposed tweak
-          defaultValue: ',',
+          defaultValue: "','",
examples/sites/demos/pc/app/carousel/draggable.spec.ts (1)

23-27: Avoid waitForTimeout in favor of explicit conditions.

Using page.waitForTimeout() leads to flaky tests. Consider using page.waitForSelector() or explicit state assertions instead. Also, dispatchEvent may not properly initialize event coordinates - consider using page.mouse.down(), page.mouse.move(), page.mouse.up() for more realistic simulation.

Suggested approach using Playwright's mouse API
-    await carousel.dispatchEvent('mousedown')
-    await page.waitForTimeout(50)
-    await carousel.dispatchEvent('mousemove', { clientX: 100, clientY: 100 })
-    await page.waitForTimeout(50)
-    await carousel.dispatchEvent('mouseup')
+    const box = await carousel.boundingBox()
+    if (box) {
+        const startX = box.x + box.width / 2
+        const startY = box.y + box.height / 2
+        await page.mouse.move(startX, startY)
+        await page.mouse.down()
+        await page.mouse.move(startX - 100, startY)
+        await page.mouse.up()
+    }
packages/vue/src/carousel/src/pc.vue (1)

14-14: Class binding should reflect dragging state, not just the prop.

The tiny-carousel--draggable class (which sets cursor: grabbing) is bound to the draggable prop. This means the grabbing cursor will always show when the prop is enabled. Typically, cursor: grab should show when hoverable, and cursor: grabbing only while actively dragging.

Suggested: Bind class to active dragging state
-    :class="{ 'tiny-carousel--card': type === 'card', 'tiny-carousel--draggable': draggable }"
+    :class="{ 'tiny-carousel--card': type === 'card', 'tiny-carousel--draggable': draggable && state.moving }"

And consider adding a separate tiny-carousel--drag-enabled class for the default cursor: grab style.

packages/renderless/src/carousel/vue.ts (1)

142-146: Minor formatting inconsistencies.

Missing spaces after commas in the object destructuring patterns, and the trailing comment on line 146 seems misplaced.

Formatting fix
     canActive: canActive(props),
-    mousedown: mousedown({ props, state, api }), 
-    mousemove: mousemove({ props, state, vm }),
-    mouseup: mouseup({ props,state, api }),
-    mouseleave: mouseleave({ props,state, api }),
-    //拖动相关的事件
+    mousedown: mousedown({ props, state, api }),
+    mousemove: mousemove({ props, state, vm }),
+    mouseup: mouseup({ props, state, api }),
+    mouseleave: mouseleave({ props, state, api })
packages/renderless/src/carousel/index.ts (2)

391-436: Consider extracting shared logic with touch handlers.

The mousemove and touchmove handlers share nearly identical logic (direction detection, delta calculation, boundary checks, item updates). Similarly, mouseup and touchend are duplicated. This increases maintenance burden.

Consider extracting the shared movement logic into a helper function that both mouse and touch handlers can use:

const handleDragMove = ({ state, props, carousel }, clientX, clientY) => {
  // shared delta calculation, direction detection, item updates
}

Then both handlers call it with the appropriate coordinates from their respective event objects.


464-474: Unused event parameter.

The event parameter in mouseleave is declared but never used. Consider removing it or prefixing with underscore to indicate intentional non-use.

Remove unused parameter
 export const mouseleave =
   ({ state, api }) =>
-    (event) => {
+    () => {
       if (state.moving) {

Comment on lines +158 to +169
//draggable
{
demoId: 'draggable',
name: {
'zh-CN': '可拖拽',
'en-US': 'Draggable'
},
desc: {
'zh-CN': '<p>通过配置 Collapse 内置的 draggable 插槽实现拖拽<p>',
'en-US': '<p>Drag and drop is implemented by configuring the built-in draggable slot of Collapse.</p>'
},
codeFiles: ['draggable.vue']

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Doc text should reference the draggable prop, not a slot.

The demo uses a prop (draggable), so “draggable slot” is misleading.

✏️ Suggested wording
-      desc: {
-        'zh-CN': '<p>通过配置 Collapse 内置的 draggable 插槽实现拖拽<p>',
-        'en-US': '<p>Drag and drop is implemented by configuring the built-in draggable slot of Collapse.</p>'
-      },
+      desc: {
+        'zh-CN': '<p>通过配置 Collapse 的 <code>draggable</code> 属性实现拖拽<p>',
+        'en-US': '<p>Enable drag by setting the <code>draggable</code> prop on Collapse.</p>'
+      },
📝 Committable suggestion

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

Suggested change
//draggable
{
demoId: 'draggable',
name: {
'zh-CN': '可拖拽',
'en-US': 'Draggable'
},
desc: {
'zh-CN': '<p>通过配置 Collapse 内置的 draggable 插槽实现拖拽<p>',
'en-US': '<p>Drag and drop is implemented by configuring the built-in draggable slot of Collapse.</p>'
},
codeFiles: ['draggable.vue']
//draggable
{
demoId: 'draggable',
name: {
'zh-CN': '可拖拽',
'en-US': 'Draggable'
},
desc: {
'zh-CN': '<p>通过配置 Collapse 的 <code>draggable</code> 属性实现拖拽<p>',
'en-US': '<p>Enable drag by setting the <code>draggable</code> prop on Collapse.</p>'
},
codeFiles: ['draggable.vue']
🤖 Prompt for AI Agents
In `@examples/sites/demos/mobile-first/app/collapse/webdoc/collapse.js` around
lines 158 - 169, The demo description incorrectly refers to a "draggable slot"
but the feature is exposed via the draggable prop; update the descriptive text
in the demo entry with demoId 'draggable' (the desc values) to mention the
draggable prop instead of a slot (e.g., "by configuring the built-in draggable
prop of Collapse" for both 'zh-CN' and 'en-US') so the documentation matches the
implementation.

Comment on lines +29 to +32
// 6. 测试触摸事件可以触发
await carousel.dispatchEvent('touchstart')
await page.waitForTimeout(50)
await carousel.dispatchEvent('touchend')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Touch events lack required touch point data.

The touchstart event handler in the renderless code expects event.touches[0].clientX/Y. Dispatching events without this data won't properly test touch functionality.

Consider using proper touch event initialization
-    await carousel.dispatchEvent('touchstart')
-    await page.waitForTimeout(50)
-    await carousel.dispatchEvent('touchend')
+    await carousel.evaluate((el) => {
+        const rect = el.getBoundingClientRect()
+        const touch = new Touch({
+            identifier: 1,
+            target: el,
+            clientX: rect.left + rect.width / 2,
+            clientY: rect.top + rect.height / 2
+        })
+        el.dispatchEvent(new TouchEvent('touchstart', { touches: [touch] }))
+        el.dispatchEvent(new TouchEvent('touchend', { changedTouches: [touch] }))
+    })
🤖 Prompt for AI Agents
In `@examples/sites/demos/pc/app/carousel/draggable.spec.ts` around lines 29 - 32,
The test dispatches 'touchstart'/'touchend' via carousel.dispatchEvent without
supplying touch point data, but the component's touch handler reads
event.touches[0].clientX/Y; update the test to dispatch proper TouchEvent
objects that include a touches array with a Touch-like point (clientX/clientY)
so the handler can read coordinates. Replace the simple dispatch calls for
'touchstart' and 'touchend' with creation of TouchEvent instances (or a
CustomEvent with a touches property) containing a touch point matching the
carousel element (refer to carousel.dispatchEvent and the component's touchstart
handler expecting event.touches[0].clientX/Y) and use those events in the
awaits.

Comment thread packages/renderless/src/carousel/index.ts Outdated
Comment on lines 18 to +22
@mouseleave.stop="handleMouseLeave"
@mousedown="mousedown"
@mousemove="mousemove"
@mouseup="mouseup"
@mouseleave="mouseleave"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Duplicate @mouseleave event handlers may cause issues.

There are two @mouseleave handlers: handleMouseLeave (line 18) and mouseleave (line 22). Both will fire on mouseleave, potentially causing api.startTimer() to be called twice and creating race conditions with the timer.

Consider consolidating the logic into a single handler or ensuring the drag-related mouseleave handler properly coordinates with handleMouseLeave.

Option: Remove duplicate and handle both concerns in one handler
     `@mouseenter.stop`="handleMouseEnter"
-    `@mouseleave.stop`="handleMouseLeave"
+    `@mouseleave.stop`="handleMouseLeave"
     `@mousedown`="mousedown"
     `@mousemove`="mousemove"
     `@mouseup`="mouseup"
-    `@mouseleave`="mouseleave"

Then update handleMouseLeave in the renderless layer to also call the drag cleanup logic from mouseleave.

🤖 Prompt for AI Agents
In `@packages/vue/src/carousel/src/pc.vue` around lines 18 - 22, There are two
`@mouseleave` handlers (handleMouseLeave and mouseleave) which both fire on leave
and can double-trigger timer logic; remove the duplicate directive (remove
`@mouseleave`="mouseleave") and consolidate behavior by updating handleMouseLeave
in the renderless layer to also perform the drag cleanup currently in mouseleave
(e.g., stop drag state, clear flags, and call any drag-specific cleanup) so a
single handler manages both timer restart and drag cleanup consistently.

},
//draggable
{
name: 'draggable',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

新的特性需要添加版本信息哈

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@ourcx 可参考以下文档添加版本信息:
给组件的新 API 添加 meta 信息

Comment thread examples/sites/demos/apis/statistic.js Outdated
mfDemo: ''
},
{
name: 'use-animation',

@shenjunjian shenjunjian Jan 31, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

其它组件有类似属性,好像 animate:true 这样来用。 所以不建议使用 use-animation这个属性名字

Comment thread examples/sites/demos/apis/carousel.js Outdated
},
mode: ['pc', 'mobile-first'],
pcDemo: 'draggable',
mfDemo: 'draggable'

@kagol kagol Jan 31, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@ourcx 该特性建议默认为 true,在手机端默认可以通过手指触摸滑动的方式切换轮播图

Comment thread examples/sites/demos/apis/statistic.js Outdated
Comment on lines +117 to +172
@@ -133,4 +169,4 @@ interface ITitleType {
`
}
]
}
} No newline at end of file

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@ourcx 该 PR 用于给 Carousel 组件增加 draggable 特性,不应该修改 statistic 和 collapse 组件,如果要修改statistic 和 collapse 组件,建议另开新 PR

@ourcx ourcx marked this pull request as draft February 5, 2026 08:39
@ourcx ourcx force-pushed the Feature—Carousel branch from 8d2262a to 6798c24 Compare February 5, 2026 09:58
@ourcx ourcx marked this pull request as ready for review February 5, 2026 09:59
@ourcx

ourcx commented Feb 5, 2026

Copy link
Copy Markdown
Contributor Author

把 static 的修改删除了,因为当时切换分支的时候的遗漏

@Issues-translate-bot

Copy link
Copy Markdown

Bot detected the issue body's language is not English, translate it automatically.


The static modification was deleted because it was omitted when switching branches.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@examples/sites/demos/apis/carousel.js`:
- Around line 203-218: Update the API docs for the "draggable" prop so its
defaultValue matches the implementation: change defaultValue from 'false' to
'true' (to reflect the prop default: true in
packages/vue/src/carousel/src/index.ts), and remove the redundant comment
"//draggable" above the prop entry since the name field already documents it.

In `@packages/renderless/src/carousel/index.ts`:
- Around line 458-468: The mouseleave handler (mouseleve) currently only resets
state.moving and clears deltas but omits the same finalization logic used in
mouseup; update the mouseleave function to mirror mouseup by checking each
item's itemsTranslate delta and calling resetAnimatingMf() when delta > 1 for
the affected item(s), then call api.startTimer(); also remove the unused event
parameter from the mouseleave signature to avoid an unused variable. Ensure you
reference the same symbols: mouseleave, state.moving, state.itemsTranslate,
state.items, setDelta, resetAnimatingMf, and api.startTimer when making the
change.
🧹 Nitpick comments (3)
packages/theme/src/carousel/index.less (1)

257-261: Cursor state should differentiate between idle and active dragging.

The cursor: grabbing style is applied statically when draggable is true, but grabbing should only show while actively dragging. The idle state should use cursor: grab to indicate the element is draggable.

Consider splitting into two states:

  • Default draggable state: cursor: grab
  • Active dragging state: cursor: grabbing
♻️ Proposed fix
 .tiny-carousel--draggable {
-    //鼠标效果
-    cursor: grabbing;
+    cursor: grab;
     user-select: none;
+
+    &.is-dragging {
+        cursor: grabbing;
+    }
 }

Then toggle the is-dragging class dynamically in the component when state.moving is true.

packages/renderless/src/carousel/index.ts (1)

399-399: Remove stale comment.

The comment "增加节流 - 我觉得可能引发一些问题,后续可以多看这个逻辑" (adding throttle - I think this might cause some problems, need to look at this logic later) is now stale since the throttle is correctly applied by wrapping the entire handler. This leftover comment may confuse future maintainers.

🧹 Remove stale comment
-      //增加节流 - 我觉得可能引发一些问题,后续可以多看这个逻辑
-
       state.deltaPos.X = event.clientX - state.startPos.X
packages/vue/src/carousel/src/pc.vue (1)

14-14: Consider dynamic class for active drag state.

The tiny-carousel--draggable class is applied statically based on the draggable prop. To properly show cursor: grabbing only while dragging (as suggested in the CSS review), you'll need to also bind a class based on state.moving:

♻️ Proposed enhancement
-    :class="{ 'tiny-carousel--card': type === 'card', 'tiny-carousel--draggable': draggable }"
+    :class="{ 
+      'tiny-carousel--card': type === 'card', 
+      'tiny-carousel--draggable': draggable,
+      'is-dragging': draggable && state.moving 
+    }"

Comment on lines +203 to 218
//draggable
{
name: 'draggable',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否允许拖拽切换幻灯片',
'en-US': 'Whether to allow dragging to switch slides'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'draggable',
mfDemo: 'draggable',
meta: {
stable: '3.29.0'
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the default value in the component definition
rg -n "draggable" packages/vue/src/carousel/src/index.ts -A 3 -B 1

Repository: opentiny/tiny-vue

Length of output: 167


🏁 Script executed:

#!/bin/bash
# Check the actual code in the API docs file at the specified lines
sed -n '203,218p' examples/sites/demos/apis/carousel.js

Repository: opentiny/tiny-vue

Length of output: 483


Fix default value mismatch and remove redundant comment.

The API documentation specifies defaultValue: 'false', but the implementation in packages/vue/src/carousel/src/index.ts defines default: true. Correct the API docs to match the actual default value.

Also remove the redundant comment //draggable on line 203; the name field below is self-explanatory.

🤖 Prompt for AI Agents
In `@examples/sites/demos/apis/carousel.js` around lines 203 - 218, Update the API
docs for the "draggable" prop so its defaultValue matches the implementation:
change defaultValue from 'false' to 'true' (to reflect the prop default: true in
packages/vue/src/carousel/src/index.ts), and remove the redundant comment
"//draggable" above the prop entry since the name field already documents it.

Comment on lines +458 to +468
// 鼠标离开窗口
export const mouseleave =
({ state, api }) =>
(event) => {
if (state.moving) {
state.moving = false
state.itemsTranslate.forEach((item, index) => {
state.items[index].setDelta(0)
})
api.startTimer()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

mouseleave cleanup is incomplete compared to mouseup.

When the mouse leaves while dragging, mouseleave only resets moving and clears deltas, but unlike mouseup (lines 447-453), it doesn't call resetAnimatingMf() when delta > 1. This inconsistency could leave carousel items in an intermediate visual state.

Also, the event parameter is unused.

🛠️ Suggested fix for consistent cleanup
 export const mouseleave =
   ({ state, api }) =>
-    (event) => {
+    () => {
       if (state.moving) {
         state.moving = false
         state.itemsTranslate.forEach((item, index) => {
           state.items[index].setDelta(0)
         })
+        if (Math.abs(state.delta) > 1) {
+          state.items.forEach((item) => {
+            item.resetAnimatingMf()
+          })
+        }
         api.startTimer()
       }
     }
🤖 Prompt for AI Agents
In `@packages/renderless/src/carousel/index.ts` around lines 458 - 468, The
mouseleave handler (mouseleve) currently only resets state.moving and clears
deltas but omits the same finalization logic used in mouseup; update the
mouseleave function to mirror mouseup by checking each item's itemsTranslate
delta and calling resetAnimatingMf() when delta > 1 for the affected item(s),
then call api.startTimer(); also remove the unused event parameter from the
mouseleave signature to avoid an unused variable. Ensure you reference the same
symbols: mouseleave, state.moving, state.itemsTranslate, state.items, setDelta,
resetAnimatingMf, and api.startTimer when making the change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request (功能增强)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants