Skip to content

Conversation

@Yeom-JinHo
Copy link
Contributor

@Yeom-JinHo Yeom-JinHo commented Oct 23, 2025

Description

This PR refines the reorder interaction by ensuring that the drag icon correctly resets its scale in all cases — including simple clicks — and improves both accessibility and mobile usability.
Previously, the icon could remain scaled when clicked without dragging, and mobile users occasionally experienced scroll gesture interference.


Changes

  • Fixed scale persistence issue
    The icon now properly resets its scale (scale: 1) after drag or click interactions.
  • Added accessibility improvements
    Added aria-label="Reorder" to the motion button to make it screen-reader friendly.
  • Improved mobile drag behavior
    Applied touchAction: 'none' to the motion button to prevent scroll or zoom interference during dragging.
  • Simplified cursor styling
    Removed redundant cursor-grab from the inner SVG, leaving it only on the button for consistency.

Motivation

The reorder icon previously relied solely on the drag lifecycle (onDragStart / onDragEnd),
which caused the icon to stay scaled when the user clicked without dragging.
Additionally, touch devices could mistakenly interpret drag gestures as native scrolling.
This update ensures consistent visual feedback, accessibility compliance, and smoother mobile drag interactions.


Screenshots

ASIS

504498366-2a114a62-0215-4911-9e3f-f4c6660d7d83.mov

TOBE

2025-10-23.10.16.51.mov

Summary by CodeRabbit

  • Accessibility

    • Reordered control icon is now a proper button with accessible label and improved pointer/press handling for better keyboard and assistive tech interaction.
  • Bug Fixes

    • More consistent visual feedback during drag operations via a unified active state; press state reliably resets on drag end or pointer cancel, reducing interaction glitches.

@coderabbitai
Copy link

coderabbitai bot commented Oct 23, 2025

Walkthrough

A drag-items component refactors its ReorderIcon API: replaces isDragging and onPressStart with isActive and onPress, moves visual press state into the item, and changes the icon from a motion.div to an accessible motion.button driven by a combined active flag.

Changes

Cohort / File(s) Summary
ReorderIcon + Item press handling
apps/ui-layout/registry/components/drag/drag-items.tsx
Reworked ReorderIcon API and props: PropsReorderIconProps, signature updated to ReorderIcon({ dragControls, isActive, onPress }: ReorderIconProps). Replaced isDragging with isActive, onPressStart with onPress. Element changed from motion.div to motion.button, added aria-label, and onPointerDown calls onPress before drag. Item now tracks local pressed state, clears it on drag end / pointer up / cancel, and computes `isActive = isDragging

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Item
    participant ReorderIcon
    participant DragControls

    Note over Item,ReorderIcon: New flow — press + drag unified by isActive
    User->>Item: PointerDown
    Item->>ReorderIcon: onPress()
    ReorderIcon->>Item: pressed = true
    Item->>Item: isActive = isDragging || pressed
    Item->>DragControls: startDrag (when applicable)
    User->>Item: PointerUp / PointerCancel / DragEnd
    Item->>Item: pressed = false
    Item->>ReorderIcon: isActive = false
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A tiny button hops where divs once stood,

Pressed and active, animated and good.
Drag wakes a ripple, then quiet returns,
Aria sings — accessible concerns.
I nibble the change and thump joyful turns.

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "fix: improve reorder icon scale reset and add aria/touch optimizations" accurately reflects the main changes in the pull request. The title explicitly addresses the primary objective of fixing the scale reset issue for both drag and click interactions, and it captures the secondary improvements around accessibility (aria-label) and touch/mobile optimization (touchAction: 'none'). The summary of changes confirms these elements: the introduction of local pressed state to handle scale reset, the conversion from motion.div to motion.button, the addition of aria-label attributes, and the optimization for touch devices. The title is concise, specific, and uses appropriate semantic versioning without unnecessary noise.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac430a1 and d75eb71.

📒 Files selected for processing (1)
  • apps/ui-layout/registry/components/drag/drag-items.tsx (3 hunks)
🔇 Additional comments (4)
apps/ui-layout/registry/components/drag/drag-items.tsx (4)

70-70: LGTM: Pressed state correctly tracks button interactions.

The new pressed state variable properly tracks click interactions independent of dragging, enabling the scale-reset logic for both drag and click-only scenarios.


79-84: LGTM: Event handlers correctly reset state in all interaction scenarios.

The refactored event handlers properly clean up both isDragging and pressed state:

  • onDragEnd resets both states after a drag completes
  • onPointerUp handles click-without-drag by resetting pressed
  • onPointerCancel handles edge cases where pointer leaves the element

The item-level placement of these handlers is appropriate since pointer events bubble up from the button.


90-91: LGTM: Combined active state provides unified visual feedback.

The isActive={isDragging || pressed} prop correctly combines both interaction states, ensuring the icon shows active styling during both drag operations and simple clicks. The onPress callback properly triggers the pressed state.


97-137: LGTM: ReorderIcon refactor successfully implements all PR objectives.

The component refactor is well-executed:

Accessibility & semantics (lines 105-107):

  • Changed to motion.button with proper type="button"
  • Added aria-label="Reorder" for screen readers

Scale reset logic (line 108):

  • animate={{ scale: isActive ? 0.85 : 1 }} correctly uses the unified isActive prop
  • Combined with the state management in Item, ensures reliable scale reset after both drag and click interactions

Mobile optimization (line 116):

  • touchAction: 'none' prevents native scroll/zoom interference during drag operations

Cursor styling cleanup (lines 115, 123):

  • Button retains cursor-grab active:cursor-grabbing classes
  • SVG redundant cursor classes correctly removed (only fill-primary-foreground remains)

Interface naming (line 97):

  • ReorderIconProps avoids the naming collision with IconProps from Icons.tsx (past review comment resolved)

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
apps/ui-layout/registry/components/drag/drag-items.tsx (1)

110-114: Consider keyboard accessibility for drag initiation.

The onPointerDown handler enables mouse and touch interaction, but there's no keyboard equivalent (e.g., onKeyDown with Space or Enter keys). While drag-and-drop keyboard accessibility is complex, consider adding basic keyboard support to make the reorder functionality accessible to keyboard-only users.

Example enhancement:

onKeyDown={(e) => {
  if (e.key === ' ' || e.key === 'Enter') {
    e.preventDefault();
    onPress();
    // Note: dragControls.start() requires pointer event
    // Consider alternative keyboard reorder pattern
  }
}}

Note: Full keyboard drag-and-drop may require a different interaction pattern (e.g., arrow keys to reorder) since dragControls.start() requires a pointer event.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f6d4fd5 and ac430a1.

📒 Files selected for processing (1)
  • apps/ui-layout/registry/components/drag/drag-items.tsx (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/ui-layout/registry/components/drag/drag-items.tsx (1)
apps/ui-layout/assets/icons/Icons.tsx (1)
  • IconProps (4-4)
🔇 Additional comments (3)
apps/ui-layout/registry/components/drag/drag-items.tsx (3)

70-70: LGTM! Pressed state addition addresses the core issue.

The pressed state correctly tracks icon press events independent of drag state, ensuring the scale animation resets properly after click-without-drag interactions.


79-84: LGTM! Comprehensive state cleanup ensures reliable scale reset.

The combination of onDragEnd, onPointerUp, and onPointerCancel handlers ensures the pressed state is reliably cleared in all scenarios, including drag completion, pointer release, and pointer interruption events.


88-92: LGTM! ReorderIcon integration correctly implements the new API.

The isActive prop effectively combines both drag and press states, and the onPress callback properly triggers the pressed state, providing consistent visual feedback across both interaction types.

@naymurdev naymurdev merged commit 25ccb4d into ui-layouts:main Oct 23, 2025
1 check passed
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.

2 participants