Skip to content

[BUG] Splitter ResizeTrigger drag offset issue - Panel doesn't follow cursor #95

@elonehoo

Description

@elonehoo

Description

When dragging the ResizeTrigger in the Splitter component, the panel doesn't follow the cursor position accurately. There's a noticeable "jump" or offset that makes the drag interaction feel unresponsive and not following the user's hand movement (不跟手).

Root Cause

After investigating the source code, I found the issue in packages/components/splitter/src/machine.ts.

Current implementation:

const offset = pointValue - before.end

This calculates the offset based on the absolute pointer position relative to the panel boundary, rather than the relative movement from where the drag started.

// Store initial cursor position when drag starts
initialCursorPosition = cursorPosition

// Calculate delta based on relative movement
const offsetPixels = cursorPosition - initialCursorPosition
const offsetPercentage = (offsetPixels / groupSizeInPixels) * 100

Steps to Reproduce

  1. Create a basic Splitter layout with two panels
  2. Click anywhere on the ResizeTrigger (not exactly at the panel boundary)
  3. Start dragging
  4. Observe that the panel "jumps" to align with the cursor position instead of moving relative to where you started dragging

Expected Behavior

When the user clicks on the ResizeTrigger and starts dragging:

  1. The initial click position should be recorded
  2. Panel size changes should be calculated based on how far the cursor has moved from the initial position
  3. The panel should smoothly follow the cursor without any initial "jump"

Actual Behavior

  • The panel jumps to align with the absolute cursor position
  • If you click on the left side of a wide ResizeTrigger, the panel boundary jumps to that position
  • The drag feels "disconnected" from the cursor movement

Suggested Fix

In machine.ts, the dragging state initialization should store the initial cursor position:

// When entering dragging state
entry: ['focusResizeHandle', 'storeInitialCursorPosition'],

// New action
storeInitialCursorPosition: (ctx, evt) => {
  ctx.initialCursorPosition = evt.point
  ctx.initialPanelSizes = {
    before: ctx.previousPanels.before.size,
    after: ctx.previousPanels.after.size,
  }
}

Then in setPointerValue, calculate the delta relative to the initial position:

setPointerValue: (ctx, evt) => {
  const rootEl = dom.getRootEl(ctx)
  const rootSize = ctx.orientation === 'horizontal' ? rootEl.offsetWidth : rootEl.offsetHeight

  // Calculate relative delta
  const currentPosition = getRelativePoint(evt.point, rootEl)
  const initialPosition = getRelativePoint(ctx.initialCursorPosition, rootEl)
  const deltaPercent = ((currentPosition - initialPosition) / rootSize) * 100

  // Apply delta to initial sizes
  const newBeforeSize = ctx.initialPanelSizes.before + deltaPercent
  const newAfterSize = ctx.initialPanelSizes.after - deltaPercent

  // ... apply constraints and update
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions