CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-sortable-hoc

Higher-order components to turn any list into animated, accessible and touch-friendly sortable lists

Pending
Overview
Eval results
Files

sortable-container.mddocs/

Sortable Container

Higher-order component that transforms any React component into a container capable of holding sortable elements. Provides comprehensive configuration for drag behavior, animations, constraints, and event handling.

Capabilities

SortableContainer HOC

Creates a sortable container from any React component.

/**
 * Higher-order component that makes a component capable of containing sortable elements
 * @param wrappedComponent - The React component to enhance with sortable functionality
 * @param config - Optional configuration object
 * @returns Enhanced React component with sortable container capabilities
 */
function SortableContainer<P>(
  wrappedComponent: WrappedComponent<P>,
  config?: Config
): React.ComponentClass<P & SortableContainerProps>;

interface Config {
  withRef: boolean;
}

interface SortableContainerProps {
  // Sorting Direction & Constraints
  axis?: Axis;
  lockAxis?: Axis;
  lockToContainerEdges?: boolean;
  lockOffset?: Offset | [Offset, Offset];
  
  // Animation & Visual
  helperClass?: string;
  transitionDuration?: number;
  keyboardSortingTransitionDuration?: number;
  hideSortableGhost?: boolean;
  
  // Trigger Behavior
  pressDelay?: number;
  pressThreshold?: number;
  distance?: number;
  useDragHandle?: boolean;
  
  // Event Handlers
  shouldCancelStart?: (event: SortEvent | SortEventWithTag) => boolean;
  updateBeforeSortStart?: SortStartHandler;
  onSortStart?: SortStartHandler;
  onSortMove?: SortMoveHandler;
  onSortEnd?: SortEndHandler;
  onSortOver?: SortOverHandler;
  
  // Scrolling & Container
  useWindowAsScrollContainer?: boolean;
  disableAutoscroll?: boolean;
  getContainer?: ContainerGetter;
  helperContainer?: HTMLElement | HelperContainerGetter;
  getHelperDimensions?: (sort: SortStart) => Dimensions;
  
  // Keyboard Navigation
  keyCodes?: KeyCodes;
}

interface KeyCodes {
  lift?: number[];
  drop?: number[];
  cancel?: number[];
  up?: number[];
  down?: number[];
}

Usage Examples:

import React from 'react';
import { SortableContainer, SortableElement } from 'react-sortable-hoc';

// Basic sortable list
const SortableList = SortableContainer(({ items }) => {
  return (
    <ul>
      {items.map((value, index) => (
        <SortableItem key={`item-${value}`} index={index} value={value} />
      ))}
    </ul>
  );
});

// With configuration options
const AdvancedSortableList = SortableContainer(({ items }) => (
  <div className="grid">
    {items.map((item, index) => (
      <SortableGridItem key={item.id} index={index} item={item} />
    ))}
  </div>
));

// Usage with props
<AdvancedSortableList
  items={items}
  axis="xy"
  helperClass="sortable-helper"
  transitionDuration={200}
  pressDelay={100}
  onSortEnd={handleSortEnd}
  useDragHandle={true}
/>

Core Configuration Props

Sorting Direction

/** Items can be sorted horizontally, vertically or in a grid */
axis?: 'x' | 'y' | 'xy'; // default: 'y'

/** Lock movement to an axis while sorting */
lockAxis?: 'x' | 'y';

Animation & Visual Props

/** CSS class to add to the sortable helper element */
helperClass?: string;

/** Duration of transition when elements shift positions (ms) */
transitionDuration?: number; // default: 300

/** Duration for keyboard sorting transitions (ms) */
keyboardSortingTransitionDuration?: number; // defaults to transitionDuration

/** Whether to auto-hide the element being sorted */
hideSortableGhost?: boolean; // default: true

Trigger Behavior Props

/** Time to wait before sorting begins (ms) - good for mobile */
pressDelay?: number; // default: 0

/** Pixels of movement to tolerate before ignoring press event */
pressThreshold?: number; // default: 5

/** Distance to drag before sorting begins (pixels) */
distance?: number; // default: 0

/** Whether to use drag handles for sorting */
useDragHandle?: boolean; // default: false

Event Handler Props

Core Event Handlers

/** Function to determine if sorting should be cancelled */
shouldCancelStart?: (event: SortEvent | SortEventWithTag) => boolean;

/** Async function called before sorting begins */
updateBeforeSortStart?: SortStartHandler;

/** Callback when sorting begins */
onSortStart?: SortStartHandler;

/** Callback during sorting as cursor moves */
onSortMove?: SortMoveHandler;

/** Callback when moving over an item */
onSortOver?: SortOverHandler;

/** Callback when sorting ends */
onSortEnd?: SortEndHandler;

Event Handler Examples:

const handleSortStart = ({ node, index, collection, isKeySorting }, event) => {
  console.log('Sorting started:', { index, collection, isKeySorting });
};

const handleSortEnd = ({ oldIndex, newIndex, collection, isKeySorting }, event) => {
  if (oldIndex !== newIndex) {
    const newItems = arrayMove(items, oldIndex, newIndex);
    setItems(newItems);
  }
};

const shouldCancel = (event) => {
  // Don't sort if clicking on buttons or inputs
  return ['INPUT', 'BUTTON', 'SELECT'].includes(event.target.tagName);
};

<SortableList
  items={items}
  onSortStart={handleSortStart}
  onSortEnd={handleSortEnd}
  shouldCancelStart={shouldCancel}
/>

Advanced Configuration Props

Container & Scrolling

/** Use window as the scrolling container */
useWindowAsScrollContainer?: boolean; // default: false

/** Disable autoscrolling while dragging */
disableAutoscroll?: boolean; // default: false

/** Function to return the scrollable container element */
getContainer?: ContainerGetter;

/** Container for the sortable helper element */
helperContainer?: HTMLElement | HelperContainerGetter; // default: document.body

/** Function to compute helper dimensions */
getHelperDimensions?: (sort: SortStart) => Dimensions;

Constraint Props

/** Lock movement to container edges */
lockToContainerEdges?: boolean; // default: false

/** Offset distance from container edges when locked */
lockOffset?: Offset | [Offset, Offset]; // default: '50%'

Constraint Examples:

// Lock to container with 10px margins
<SortableList
  items={items}
  lockToContainerEdges={true}
  lockOffset="10px"
  onSortEnd={handleSortEnd}
/>

// Different lock offsets for top/bottom
<SortableList
  items={items}
  lockToContainerEdges={true}
  lockOffset={["0%", "100%"]}
  onSortEnd={handleSortEnd}
/>

Keyboard Navigation

Key Code Configuration

/** Keyboard navigation key codes */
keyCodes?: {
  lift?: number[];    // default: [32] (SPACE)
  drop?: number[];    // default: [32] (SPACE) 
  cancel?: number[];  // default: [27] (ESC)
  up?: number[];      // default: [38, 37] (UP, LEFT)
  down?: number[];    // default: [40, 39] (DOWN, RIGHT)
};

Keyboard Navigation Example:

// Custom key bindings
<SortableList
  items={items}
  keyCodes={{
    lift: [13, 32],     // Enter or Space to lift
    drop: [13, 32],     // Enter or Space to drop
    cancel: [27],       // Escape to cancel
    up: [38, 87],       // Up arrow or W
    down: [40, 83],     // Down arrow or S
  }}
  onSortEnd={handleSortEnd}
/>

withRef Configuration

interface Config {
  /** Enable access to wrapped component instance */
  withRef: boolean;
}

withRef Example:

const SortableList = SortableContainer(ListComponent, { withRef: true });

// Access wrapped instance
const listRef = useRef();
const wrappedInstance = listRef.current?.getWrappedInstance();

<SortableList ref={listRef} items={items} onSortEnd={handleSortEnd} />

SortableContext

React context that provides access to the internal sortable manager. Primarily used for advanced integrations and custom components.

/**
 * React context providing access to sortable manager
 */
const SortableContext: React.Context<{
  manager: Manager;
}>;

Usage Examples:

import React, { useContext } from 'react';
import { SortableContext } from 'react-sortable-hoc';

// Access sortable manager from context
const CustomSortableComponent = () => {
  const { manager } = useContext(SortableContext);
  
  // Use manager for advanced operations
  const handleCustomAction = () => {
    console.log('Sortable elements:', manager.refs);
  };
  
  return (
    <div onClick={handleCustomAction}>
      Custom sortable component
    </div>
  );
};

// Use within SortableContainer
const SortableList = SortableContainer(({ items }) => (
  <ul>
    {items.map((value, index) => (
      <SortableItem key={`item-${index}`} index={index} value={value} />
    ))}
    <CustomSortableComponent />
  </ul>
));

Note: SortableContext is an advanced API primarily intended for library authors and complex integrations. Most applications should use the standard SortableContainer, SortableElement, and SortableHandle components.

Install with Tessl CLI

npx tessl i tessl/npm-react-sortable-hoc

docs

array-utilities.md

index.md

sortable-container.md

sortable-element.md

sortable-handle.md

tile.json