MCPcopy Index your code
hub / github.com/caplin/FlexLayout

github.com/caplin/FlexLayout @v0.9.2

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.9.2 ↗ · + Follow
751 symbols 1,910 edges 71 files 190 documented · 25%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

FlexLayout

GitHub npm npm

FlexLayout is a layout manager for React that arranges components in multiple tabsets. Tabs can be resized, moved, and organized into complex layouts.

FlexLayout Demo Screenshot

Run the Demo

Try it now using CodeSandbox

API Doc

FlexLayout's only dependency is React.

Features: * Splitters for resizing * Tabs (scrolling or wrapped) * Tab dragging and ordering * Tabset dragging (move all tabs in a tabset in one operation) * Docking to tabsets or edges of the frame * Maximizing tabsets (double-click tabset header or use icon) * Tab overflow (menu for hidden tabs, mouse wheel scrolling) * Border tabsets * Popout tabs into floating panels or new browser windows * Submodels (layouts inside layouts) * Tab renaming (double-click tab text) * Theming (light, dark, underline, etc., and combined) * Mobile support (iPad, Android) * Multiple ways to add tabs (drag, active tabset, by ID) * Comprehensive tab and tabset attributes (enableTabStrip, enableDock, enableDrop, etc.) * Customizable tab and tabset rendering * Preservation of component state when tabs are moved * Playwright tests * TypeScript type declarations

Example Interaction

FlexLayout Animation

Installation

FlexLayout is available on npm. Install it using:

npm install flexlayout-react

Import FlexLayout and its model in your modules:

import { Layout, Model } from 'flexlayout-react';

Include a theme. Choose from alpha_light, alpha_dark, alpha_rounded, light, dark, underline, gray, rounded, or combined (see the demo for examples):

import 'flexlayout-react/style/alpha_light.css';  

Learn how to change the theme dynamically in code

Usage

The <Layout> component renders the tabsets and splitters. It takes the following props:

Required props:

Prop Description
model The layout model
factory A factory function for creating React components

Additional optional props

The model is a tree of Node objects that define the structure of the layout.

The factory is a function that takes a Node object and returns a React component to be hosted within a tab.

Models can be created using the Model.fromJson(jsonObject) static method and saved using the model.toJson() method.

Example Configuration:

const json = {
    global: {},
    borders: [],
    layout: {
        type: "row",
        weight: 100,
        children: [
            {
                type: "tabset",
                weight: 50,
                children: [
                    {
                        type: "tab",
                        name: "One",
                        component: "placeholder",
                    }
                ]
            },
            {
                type: "tabset",
                weight: 50,
                children: [
                    {
                        type: "tab",
                        name: "Two",
                        component: "placeholder",
                    }
                ]
            }
        ]
    }
};

Example Code

const model = Model.fromJson(json);

function App() {

  const factory = (node) => {
    const component = node.getComponent();

    if (component === "placeholder") {
      return 

{node.getName()}

;
    }
  }

  return (
    <Layout
      model={model}
      factory={factory} />
  );
}

The above code renders two tabsets horizontally, each containing a single tab that hosts a div component (returned from the factory). Tabs can be moved and resized by dragging and dropping. Additional tabs can be added to the layout by sending actions to the model.

Simple layout

Note: The <Layout> component must be hosted in a container element (with CSS position: absolute or relative). The layout will fill the containing element.

Try it now using CodeSandbox

A simple TypeScript example can be found here:

https://github.com/nealus/flexlayout-vite-example

The model JSON contains four top-level elements:

  • global - (optional) Global options.
  • layout - The main row/tabset/tabs layout hierarchy.
  • borders - (optional) Up to four borders ("top", "bottom", "left", "right").
  • subLayouts - (optional) Where sub layouts for popout windows, floating panels and tabs are defined.

The layout element is built using three types of nodes:

  • row - Rows contain a list of tabsets and child rows. The top-level row renders horizontally by default (unless the global attribute rootOrientationVertical is set). Child rows render in the opposite orientation to their parent row.
  • tabset - Tabsets contain a list of tabs and the index of the selected tab.
  • tab - Tabs specify the component to host (loaded via the factory) and the tab's display text.

The layout structure is defined with rows within rows that contain tabsets that themselves contain tabs.

Within the demo app, you can view the layout structure by checking the 'Show layout' box. Rows are shown in blue, and tabsets in orange.

FlexLayout Demo Showing Layout

The optional borders element is made up of border nodes:

  • border - Borders contain a list of tabs and the index of the selected tab. They can only be used within the borders top-level element.

The JSON model tree structure is defined as TypeScript interfaces; see JSON Model.

Each node type has a defined set of required and optional attributes.

Weights on rows and tabsets specify their relative size within the parent row. The absolute values do not matter, only their proportions (e.g., two tabsets with weights 30 and 70 would render the same as if they had weights 3 and 7).

NOTE: The easiest way to create your initial layout JSON is to use the demo app. Modify an existing layout by dragging, dropping, and adding nodes, then press the 'print' button to print the JSON to the browser's developer console.

By changing global or node attributes, you can modify the layout's appearance and functionality. For example, setting tabSetEnableTabStrip: false in the global options would change the layout into a multi-splitter (without tabs or drag-and-drop):

 global: {tabSetEnableTabStrip:false},

Dynamically Changing the Theme

The combined.css theme includes all other themes and supports dynamic theme switching.

When using combined.css, add a className (in the form flexlayout__theme_[theme-name]) to the div containing the <Layout> to select the desired theme.

For example:




        <Layout model={model} factory={factory} />



Change the theme in code by changing the className on the containing div.

For example:

    containerRef.current!.className = "flexlayout__theme_alpha_dark"

Customizing Tabs

You can use the <Layout> prop onRenderTab to customize tab rendering:

FlexLayout Tab structure

Update the renderValues parameter as needed:

renderValues.leading: The area shown in red.

renderValues.content: The area shown in green.

renderValues.buttons: The area shown in yellow.

For example:

onRenderTab = (node: TabNode, renderValues: ITabRenderValues) => {
    // renderValues.leading = <img style={{width:"1em", height:"1em"}}src="https://github.com/caplin/FlexLayout/raw/v0.9.2/images/folder.svg"/>;
    // renderValues.content += " *";
    renderValues.buttons.push(<img key="menu" style={{width:"1em", height:"1em"}} src="https://github.com/caplin/FlexLayout/raw/v0.9.2/images/menu.svg"/>);
}

Customizing Tabsets

You can use the <Layout> prop onRenderTabSet to customize tabset rendering:

FlexLayout Tab structure

Update the renderValues parameter as needed:

renderValues.leading: The area shown in blue.

renderValues.stickyButtons: The area shown in red.

renderValues.buttons: The area shown in green.

For example:

onRenderTabSet = (node: (TabSetNode | BorderNode), renderValues: ITabSetRenderValues) => {
    renderValues.stickyButtons.push(
        <button
            key="Add"
            title="Add"
            className="flexlayout__tab_toolbar_button"
            onClick={() => {
                model.doAction(Actions.addTab({
                    component: "placeholder",
                    name: "Added " + nextAddIndex.current++
                }, node.getId(), DockLocation.CENTER, -1, true));
            }}
        ><AddIcon/></button>);

    renderValues.buttons.push(<img key="menu" style={{width:"1em", height:"1em"}} src="https://github.com/caplin/FlexLayout/raw/v0.9.2/images/menu.svg"/>);
}

Model Actions

Once the model JSON has been loaded, all changes are applied through actions. In the Demo app, you can view these actions in the 'Action Log':

Action Log

Apply actions using the model.doAction() method. This method takes a single argument created by one of the action generators (accessible via FlexLayout.Actions.<actionName>):

Actions Documentation

Example

model.doAction(FlexLayout.Actions.addTab(
    {type:"tab", component:"grid", name:"a grid", id:"5"},
    "1", FlexLayout.DockLocation.CENTER, 0));

This example adds a new grid component to the center of the tabset with ID "1" at the first position (0). Use -1 to add to the end of the tabs.

Note: You can retrieve the ID of a node (e.g., the node returned by the addTab action) using node.getId(). If an ID wasn't assigned when the node was created, one will be generated for you in the form #<uuid> (e.g., #0c459064-8dee-444e-8636-eb9ab910fb27).

Note: You can intercept actions resulting from GUI changes before they are applied by implementing the onAction callback property of the Layout.

Optional Layout Props

Many optional properties can be applied to the layout:

Layout Properties Documentation

JSON Model Definition

The JSON model is defined as a set of TypeScript interfaces. See the documentation for details on allowed attributes:

Model Attributes Documentation

Global Attributes Documentation

Row Attributes Documentation

Tabset Attributes Documentation

Note: Tabsets are dynamically created as tabs are moved and deleted when their last tab is removed (unless enableDeleteWhenEmpty is set to false).

Tab Attributes Documentation

Border Attributes Documentation

Layout API Methods to Create New Tabs

The Layout Ref provides methods for adding tabs:

Layout Methods Documentation

Example:

layoutRef.current.addTabToTabSet("NAVIGATION", { type: "tab", component: "grid", name: "a grid" });

This adds a new grid component to the tabset with ID "NAVIGATION". (where layoutRef is a React ref to the Layout element; see React Refs).

Tab Node Events

You can handle node events by adding a listener, typically within a component's useEffect hook:

Example:

function MyComponent({ node }) {
  useEffect(() => {
    const listenerId = node.setEventListener("save", () => {
      node.getConfig().subject = subject;
    });
    return () => node.removeEventListener(listenerId);
  }, [subject]);
}
Event Parameters Description
resize {rect} Called when the tab is resized during layout, before it is rendered with the new size.
close None Called when the tab is closed.
visibility {visible} Called when the tab's visibility changes.
save None Called before a TabNode is serialized to JSON. Use this to save node configuration by adding data to the object returned by node.getConfig().

Popout Windows

Tabs can be rendered into external browser windows (useful for multi-monitor setups) by using the enablePopout and enablePopoutIcon attributes. When enabled, a popout icon appears in the tab header.

Popout windows require an additional HTML page, popout.html, hosted at th

Extension points exported contracts — how you extend this code

IDropTarget (Interface)
(no doc) [6 implementers]
src/model/IDropTarget.ts
IPopupMenuProps (Interface)
@internal
src/view/PopupMenu.tsx
IPopupMenuProps (Interface)
@hidden @internal
demo/PopupMenu.tsx
MUIComponent (Interface)
(no doc)
demo/MUIComponent.tsx
IRow (Interface)
(no doc)
demo/aggrid.tsx
IActionEntry (Interface)
(no doc)
demo/ActionLog.tsx
IDraggable (Interface)
(no doc) [4 implementers]
src/model/IDraggable.ts
IOverlayProps (Interface)
(no doc)
src/view/Overlay.tsx

Core symbols most depended-on inside this repo

findPath
called by 161
tests-playwright/helpers.ts
setType
called by 104
src/model/Layout.ts
checkTab
called by 73
tests-playwright/helpers.ts
add
called by 73
src/model/Attributes.ts
setDescription
called by 68
src/model/Attributes.ts
addInherited
called by 46
src/model/Attributes.ts
findTabButton
called by 41
tests-playwright/helpers.ts
getClassName
called by 35
src/model/TabNode.ts

Shape

Method 420
Function 239
Interface 45
Class 41
Enum 6

Languages

TypeScript100%

Modules by API surface

src/view/layout/LayoutInternal.tsx79 symbols
src/model/TabNode.ts60 symbols
src/model/TabSetNode.ts52 symbols
src/model/Model.ts41 symbols
src/model/BorderNode.ts36 symbols
src/model/Node.ts35 symbols
demo/App.tsx31 symbols
src/model/Rect.ts28 symbols
src/model/RowNode.ts27 symbols
src/model/Layout.ts24 symbols
src/model/Attributes.ts24 symbols
src/model/Actions.ts24 symbols

For agents

$ claude mcp add FlexLayout \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact