Skip to content

mass2527/react-headless-dock-layout

Repository files navigation

react-headless-dock-layout

A lightweight, headless dock layout library for React.

Features

  • Headless - You control all rendering and styling. This library only provides state and behavior.
  • Lightweight - Implements only core dock layout features. No tabs, floating windows, or complex UI.
  • Panel Management - Add, remove, drag-and-drop, and resize panels through split bars.

When to Use

Use this library if:

  • You need full control over UI design and styling
  • You want a simple, focused dock layout solution
  • Your requirements are basic panel operations (add/remove/move/resize)

Don't use this library if:

  • You need pre-styled components ready to use
  • You require tabs, floating windows, or complex docking features
  • You want a complete IDE-like layout system

Requirements

  • React >= 18.0.0

Installation

npm install react-headless-dock-layout
# or
yarn add react-headless-dock-layout
# or
pnpm add react-headless-dock-layout

Usage

import { useDockLayout } from 'react-headless-dock-layout';

function App() {
  const {
    addPanel,
    removePanel,
    containerRef,
    layoutRects,
    draggingRect,
    getRectProps,
    getDropIndicatorProps,
    getDragHandleProps,
  } = useDockLayout<HTMLDivElement>(null);

  return (
    <div>
      <button type="button" onClick={() => addPanel("explorer")}>
        Add Explorer Panel
      </button>
      <button type="button" onClick={() => addPanel("terminal")}>
        Add Terminal Panel
      </button>

      <div ref={containerRef} style={{ height: "90vh", position: "relative" }}>
        {layoutRects.map((rect) => {
          if (rect.type === "split") {
            const { style, ...props } = getRectProps(rect);
            return (
              <div
                key={rect.id}
                style={{ ...style, backgroundColor: "gray" }}
                {...props}
              />
            );
          }

          if (rect.type === "panel") {
            const { style, ...props } = getRectProps(rect);
            const dropIndicatorProps = getDropIndicatorProps(rect);

            return (
              <div
                key={rect.id}
                style={{
                  ...style,
                  opacity: draggingRect?.id === rect.id ? 0.5 : 1,
                }}
                {...props}
              >
                {dropIndicatorProps && (
                  <div
                    style={{
                      ...dropIndicatorProps.style,
                      backgroundColor: "blue",
                      opacity: 0.5,
                    }}
                  />
                )}

                <button {...getDragHandleProps(rect)}>Drag</button>
                <button type="button" onClick={() => removePanel(rect.id)}>
                  Close
                </button>

                {rect.id === "explorer" && <div>Explorer Content</div>}
                {rect.id === "terminal" && <div>Terminal Content</div>}
              </div>
            );
          }

          return null;
        })}
      </div>
    </div>
  );
}

Advanced

Placement Strategies

A placement strategy determines where new panels are placed when added. The default strategy is equalWidthRightStrategy, which adds panels to the right with equal widths.

To use a custom strategy, pass it in the options. Here's an example that adds panels to the rightmost panel, alternating split directions:

import { useDockLayout, type PlacementStrategy, type LayoutNode, type PanelNode, type SplitNode } from 'react-headless-dock-layout';

function findRightMostPanel(node: LayoutNode): PanelNode {
  if (node.type === "panel") return node;
  if (node.type === "split") return findRightMostPanel(node.right);
  throw new Error("Unexpected node type");
}

function findParentNode(root: LayoutNode, id: string): SplitNode | null {
  function find(node: LayoutNode): SplitNode | null {
    if (node.type === "panel") return null;
    if (node.left.id === id || node.right.id === id) return node;
    return find(node.left) ?? find(node.right);
  }
  return find(root);
}

const myStrategy: PlacementStrategy = {
  getPlacementOnAdd(root) {
    const rightMostPanel = findRightMostPanel(root);
    const parentNode = findParentNode(root, rightMostPanel.id);

    return {
      targetId: rightMostPanel.id,
      direction: parentNode?.orientation === "horizontal" ? "bottom" : "right",
      ratio: 0.5,
    };
  },
};

const { addPanel } = useDockLayout(null, {
  placementStrategy: myStrategy,
});

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages