axis

axis

Restrict movement to a single axis (x or y)

The axis plugin locks movement to horizontal (x) or vertical (y) direction. Perfect for sliders, scrollbars, or any UI requiring one-dimensional dragging.

axis('x'); // Only horizontal movement
axis('y'); // Only vertical movement
axis(null); // Free movement (removes constraint)

Basic Usage

import { createSignal } from 'solid-js';
import { axis, useDraggable } from '@neodrag/solid';

function AxisExamples() {
  const [horizontalRef, setHorizontalRef] =
    createSignal<HTMLElement | null>(null);
  const [verticalRef, setVerticalRef] =
    createSignal<HTMLElement | null>(null);
  const [freeRef, setFreeRef] = createSignal<HTMLElement | null>(
    null,
  );

  useDraggable(horizontalRef, [axis('x')]);
  useDraggable(verticalRef, [axis('y')]);
  useDraggable(freeRef, [axis(null)]);

  return (
    <div>
      <div ref={setHorizontalRef}>Horizontal only</div>
      <div ref={setVerticalRef}>Vertical only</div>
      <div ref={setFreeRef}>Any direction</div>
    </div>
  );
}

Dynamic Axis Switching

import { createSignal } from 'solid-js';
import {
  axis,
  useDraggable,
  createCompartment,
} from '@neodrag/solid';

function DynamicAxis() {
  const [ref, setRef] = createSignal<HTMLElement | null>(null);
  const [currentAxis, setCurrentAxis] = createSignal<'x' | 'y'>('x');

  const axisComp = createCompartment(() => axis(currentAxis()));

  useDraggable(ref, [axisComp]);

  function switchAxis() {
    setCurrentAxis(currentAxis() === 'x' ? 'y' : 'x');
  }

  return (
    <div>
      <div ref={setRef}>
        Currently locked to: {currentAxis()} axis
      </div>
      <button onClick={switchAxis}>
        Switch to {currentAxis() === 'x' ? 'Y' : 'X'} axis
      </button>
    </div>
  );
}

Real-World Examples

Horizontal Slider Component

import { createSignal } from 'solid-js';
import {
  axis,
  bounds,
  BoundsFrom,
  events,
  useDraggable,
} from '@neodrag/solid';

function Slider() {
  const [track, setTrack] = createSignal<HTMLElement | null>(null);
  const [handle, setHandle] = createSignal<HTMLElement | null>(null);
  const [value, setValue] = createSignal(50);

  const handleDrag = (data) => {
    if (track()) {
      const trackWidth = track()!.offsetWidth;
      setValue(Math.round((data.offset.x / trackWidth) * 100));
    }
  };

  useDraggable(handle, [
    axis('x'),
    bounds(() => BoundsFrom.element(track()!)),
    events({ onDrag: handleDrag }),
  ]);

  return (
    <div>
      <div ref={setTrack} class="track">
        <div ref={setHandle} class="handle" />
      </div>
      <p>Value: {value()}%</p>
    </div>
  );
}

Split Pane Resizer

// Vertical divider for horizontal split
const plugins = [
  axis('x'),
  bounds(
    BoundsFrom.parent({
      left: 100, // Min pane width
      right: 100, // Min pane width
    }),
  ),
];

How It Works

The axis plugin modifies the proposed movement during dragging:

  • axis('x'): Sets proposed.y = null (no vertical movement)
  • axis('y'): Sets proposed.x = null (no horizontal movement)
  • axis(null): No changes (free movement)

This happens in the plugin’s drag hook, so other plugins receive the constrained movement values.

API Reference

function axis(value?: 'x' | 'y' | null | undefined): Plugin;

Parameters:

  • value - The axis to constrain movement to
    • 'x' - Horizontal only
    • 'y' - Vertical only
    • null or undefined - No constraint

Returns: A plugin object for use with draggable.