solid-dnd

repository·main·Indexed 20 days ago

https://github.com/thisbeyond/solid-dnd

A lightweight, zero-dependency drag and drop toolkit for SolidJS. It leverages fine-grained reactivity to provide performant experiences without component re-renders. Features include draggables, droppables, DragOverlay, sortable lists, and built-in collision detection algorithms such as mostIntersecting, closestCorners, and closestCenter.

Tokens
2.4K
Snippets
7
Records
14
Agent score
68%

What's inside @thisbeyond/solid-dnd

  1. Core features and capabilities of solid-dnd

    main

    The library provides several primitives and features for building complex drag and drop interfaces:

    • Draggables: Use createDraggable to integrate drag behavior into elements while maintaining full control over their appearance.
    • Droppables: Use createDroppable to manage drop zones. These can be conditionally enabled or disabled based on the current context.
    • DragOverlay: Use this component when you want to drag a visual representation of an element that is removed from the normal document flow.
    • Sensors: Supports different sensors for detecting drag interactions (a pointer sensor is provided by default).
    • Collision Detection: Includes built-in layout collision algorithms: mostIntersecting, closestCorners, and closestCenter. You can also implement custom algorithms.
    • Sortable Lists: Provides primitives for reordering lists (currently supports vertical sorting).
    • Isolation: You can use multiple or nested DragDropProvider instances to create isolated drag and drop containers.
  2. Quickstart: Implement basic drag and drop

    main

    To implement drag and drop, you need to wrap your application in a DragDropProvider and DragDropSensors. Use createDraggable for elements that can be moved and createDroppable for target areas. Use the useDragDropContext hook to listen for events like onDragEnd.

    Note: solid-dnd does not automatically move a draggable into a droppable on drop; you are responsible for updating your application state to reflect the new position or ownership.

    import {
      DragDropProvider,
      DragDropSensors,
      useDragDropContext,
      createDraggable,
      createDroppable,
    } from "@thisbeyond/solid-dnd";
    
    const Draggable = (props) => {
      const draggable = createDraggable(props.id);
      return <div use:draggable>draggable</div>;
    };
    
    const Droppable = (props) => {
      const droppable = createDroppable(props.id);
      return <div use:droppable>droppable</div>;
    };
    
    const Sandbox = () => {
      const [, { onDragEnd }] = useDragDropContext();
    
      onDragEnd(({draggable, droppable}) => {
        if (droppable) {
          // Handle the drop. Note that solid-dnd doesn't move a draggable into a
          // droppable on drop. It leaves it up to you how you want to handle the
          // drop.
        }
      });
    
      return (
        <div>
          <Draggable id="draggable-1" />
          <Droppable id="droppable-1" />
        </div>
      );
    };
    
    const App = () => {
      return (
        <DragDropProvider>
          <DragDropSensors>
            <Sandbox />
          </DragDropSensors>
        </DragDropProvider>
      );
    };
    
    export default App;
  3. Initialize Drag and Drop with DragDropProvider

    main

    To use solid-dnd, you must wrap your application (or the relevant part of your UI) in the DragDropProvider. This provider manages the shared state for drag-and-drop operations. You can then access this context using the useDragDropContext hook in child components.

    import { DragDropProvider } from '@thisbeyond/solid-dnd';
    
    function App() {
      return (
        <DragDropProvider>
          {/* Your draggable and droppable components go here */}
        </DragDropProvider>
      );
    }
  4. Implement sortable lists with SortableProvider and createSortable

    main

    For list reordering functionality, use the sortable specialized API. Wrap your list in a SortableProvider and use useSortableContext to define the collection of items. Individual items within the list should be made sortable using createSortable.

    import { SortableProvider, useSortableContext, createSortable } from '@thisbeyond/solid-dnd';
    
    // 1. Wrap the list in SortableProvider
    <SortableProvider>
      {/* 2. Use useSortableContext to define the items in the list */}
      <MyListContext items={items} />
    </SortableProvider>
    
    // 3. Inside the list items, use createSortable
    function SortableItem(props) {
      const sortable = createSortable(props.id);
      return <div {...sortable.props}>{props.children}</div>;
    }
  5. Implement a custom CollisionDetector

    main

    A CollisionDetector is a function type used to determine which Droppable target a Draggable element is currently colliding with. You can implement your own logic to decide how collisions are calculated based on the spatial relationship between the draggable and the available droppables.

    A CollisionDetector must follow this signature:

    ( 
      draggable: Draggable, 
      droppables: Droppable[], 
      context: { activeDroppableId: Id | null } 
    ) => Droppable | null;
    • draggable: The current state of the element being dragged, including its transformed layout.
    • droppables: An array of all available drop targets.
    • context: Contains the activeDroppableId, which can be used as a tie-breaker to prioritize the currently active target if multiple targets are equally valid.
    type CollisionDetector = (
      draggable: Draggable,
      droppables: Droppable[],
      context: { activeDroppableId: Id | null }
    ) => Droppable | null;
  6. Configure drag sensors with DragDropSensors

    main
    DragDropSensors allows you to define how drag interactions are triggered (e.g., via pointer, keyboard, etc.). You can create specific sensors using functions like createPointerSensor and pass them to the provider.
  7. Apply drag styles with layoutStyle and transformStyle

    main

    The library provides utility functions to apply visual transformations to elements during a drag operation:

    • layoutStyle: Applies styles related to the element's position in the layout.
    • transformStyle: Applies CSS transforms (like translate) to move elements.
    • maybeTransformStyle: A conditional version of the transform style utility.
  8. Use built-in collision detection algorithms

    main

    The library exports three pre-defined collision detection algorithms that you can use to determine drop targets:

    1. closestCenter: Calculates collision based on the distance between the center point of the Draggable and the center point of each Droppable. It returns the droppable with the minimum distance.
    2. closestCorners: Calculates collision by summing the distances between the corresponding corners (top-left, top-right, bottom-right, bottom-left) of the Draggable and each Droppable. It returns the droppable with the minimum total corner distance.
    3. mostIntersecting: Calculates collision based on the area of intersection. It returns the Droppable that has the highest intersectionRatioOfLayouts with the Draggable.
    export { closestCenter, closestCorners, mostIntersecting };
    export type { CollisionDetector };
  9. Configure collision detection algorithms

    main

    You can specify how the library detects when a draggable item overlaps a droppable zone using CollisionDetector implementations. Available algorithms include:

    • closestCenter: Detects collision based on the center of the elements.
    • closestCorners: Detects collision based on the closest intersection of corners.
    • mostIntersecting: Detects collision based on the largest area of intersection.
    import { closestCenter, closestCorners, mostIntersecting } from '@thisbeyond/solid-dnd';
    
    // These can be used to configure collision detection behavior
  10. Use DragOverlay for visual feedback

    main
    The DragOverlay component is used to render a visual representation of the item currently being dragged, typically positioned outside the normal flow of the list to prevent layout shifts during the drag operation.