npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hermes-solution/baekdu-dnd

v0.1.0

Published

Lightweight, headless React tree drag-and-drop library with before/inside/after positioning. Zero CSS, full control.

Readme

@aims/baekdu-dnd

Lightweight, headless React tree drag-and-drop library with before/inside/after positioning. Zero CSS, full control.

✨ Features

  • 🎯 Headless - Complete control over UI and styling
  • 🎨 Zero CSS - Bring your own styles (Tailwind, CSS Modules, CSS-in-JS)
  • 📍 Precise Positioning - Drop before/inside/after with visual indicators
  • 🪶 Lightweight - No dependencies, native HTML5 Drag and Drop API
  • 🎭 Fully Typed - Written in TypeScript with full type safety
  • 🌲 Tree Optimized - Built specifically for hierarchical data structures

📦 Installation

npm install @aims/baekdu-dnd

🚀 Quick Start

The minimal import is now just the provider, item, and collection hook:

import {TreeDndProvider, TreeItem, useTreeCollection} from '@aims/baekdu-dnd';

function FileExplorer({initialTree}) {
  const {flattened, handleDrop} = useTreeCollection(initialTree);

  return (
    <TreeDndProvider onDrop={handleDrop}>
      {flattened.map((item) => (
        <TreeItem key={item.id} id={item.id} parentId={item.parentId} depth={item.depth} index={item.index} value={item.node}>
          {({draggableProps, renderIndicator}) => (
            <div {...draggableProps}>
              {renderIndicator()}
              {item.node.name}
            </div>
          )}
        </TreeItem>
      ))}
    </TreeDndProvider>
  );
}

flattened already contains depth, parentId, index, and a stable path, so you can focus on rendering only. Forward the canDrop handler returned by the hook to TreeDndProvider if you want the indicator to disappear immediately for disallowed gaps.

renderIndicator() returns the built-in drop indicator. Omit it if you plan to render your own visuals based on dropPosition. draggableProps already includes ref, onDragEnter, and all necessary drag handlers—just spread it onto your focusable element.

renderIndicator() returns the built-in drop indicator. Omit it if you plan to render your own visuals based on dropPosition. draggableProps already includes ref, onDragEnter, and all necessary drag handlers—just spread it onto your focusable element.

Drop Event Payload

onDrop receives:

  • draggedId – the id of the item being moved
  • targetId – canonical drop target (after/before deduped)
  • When you drop after the final root, targetId will be the virtual tail id (__tree-dnd-root-tail__); rely on slot.parentId (null) and slot.index for ordering there.
  • dropPosition'before', 'inside', or 'after'
  • slot – stable slot metadata (key, parentId, index) for ordering
  • originalTargetId – raw DOM target id that fired the drop
  • indicator – full indicator state (rect, depth, visualMode, etc.)
  • draggedData – metadata you registered with the dragged item (via TreeItem)
  • targetData – metadata you registered with the canonical target

indicator.visualMode === 'subtree' means you are aiming at the gap after a branch; the default indicator now wraps the entire subtree in that case so users can see the full scope of the move. Use indicator.visualRect if you need screen-space measurements to draw custom overlays.

The provider renders a tiny, invisible tail element (data-tree-dnd-tail="") after your tree so users can drop below the final node. Feel free to restyle it (height, margin) to match your layout.

Use slot.key plus slot.parentId to decide where in your tree to insert without worrying about the “after vs before” overlap on adjacent nodes.

Preventing Illegal Drops

Pass a canDrop guard to TreeDndProvider to veto specific slots before the user releases the mouse:

<TreeDndProvider
  canDrop={({dropPosition, targetData}) => {
    if (dropPosition === 'inside' && targetData?.meta?.lockChildren) {
      return false; // indicator disappears immediately
    }
    return true;
  }}
/>

canDrop receives the same payload as onDrop, plus the metadata you registered via TreeItem. Returning false clears the indicator so the drop never fires.

Built-in Motion

Nodes animate into their new positions by default using a lightweight FLIP transition. To tweak or disable the behaviour, pass itemAnimations to the provider:

<TreeDndProvider itemAnimations={{ duration: 220, easing: 'cubic-bezier(.19,1,.22,1)' }}>
  {/* tree */}
</TreeDndProvider>

<TreeDndProvider itemAnimations={false}>
  {/* disable motion entirely */}
</TreeDndProvider>

Inside the TreeItem render prop you also receive the resolved animation config, making it easy to coordinate your own CSS transitions.

Tree Utilities

Need simple path-based helpers while prototyping? The package now exports reusable utilities such as cloneTree, flattenTree, removeNodeAtPath, insertInside, insertSibling, adjustTargetPathAfterRemoval, and reorderTreeByPath, plus the shared TreePath and FlattenedTreeItem types. These are the same building blocks used by the dev playground, so you can reuse the exact reorder logic inside your app without re-implementing tree math.

📖 Core Concepts

Headless Architecture

What the library provides:

  • Drag and drop state management
  • Drop position calculation (before/inside/after)
  • Event handling and coordination

What you control:

  • All styling (CSS, Tailwind, CSS-in-JS, etc.)
  • UI rendering and animations

Drop Positioning

┌─────────────────┐
│   BEFORE (25%)  │ ← Drop above
├─────────────────┤
│   INSIDE (50%)  │ ← Drop inside (make child)
├─────────────────┤
│   AFTER (25%)   │ ← Drop below
└─────────────────┘

Customize ratios:

<TreeDndProvider dropZoneRatios={{ before: 20, inside: 60, after: 20 }} />

📄 License

MIT © ISO 42001 ALM Team