A multi-tab layout manager

Last update: Aug 1, 2022

FlexLayout

FlexLayout is a layout manager that arranges React components in multiple tab sets, tabs can be resized and moved.

FlexLayout Demo Screenshot

Run the Demo

Try it now using JSFiddle

Screenshot of Caplin Liberator Explorer using FlexLayout

FlexLayout's only dependencies are React and uuid.

Features:

  • splitters
  • tabs
  • tab dragging and ordering
  • tabset dragging (move all the tabs in a tabset in one operation)
  • dock to tabset or edge of frame
  • maximize tabset (double click tabset header or use icon)
  • tab overflow (show menu when tabs overflow, scroll tabs using mouse wheel)
  • border tabsets
  • popout tabs into new browser windows (only enabled in latest browsers)
  • submodels, allow layouts inside layouts
  • tab renaming (double click tab text to rename)
  • themeing - light, gray and dark
  • touch events - works on mobile devices (iPad, Android)
  • add tabs using drag, indirect drag, add to active tabset, add to tabset by id
  • preferred pixel size tabsets (try to keep their size when window resizes)
  • headed tabsets
  • tab and tabset attributes: enableHeader, enableTabStrip, enableDock, enableDrop...
  • customizable tabs and tabset header rendering
  • esc cancels drag
  • typescript type declarations included
  • supports overriding css class names via the classNameMapper prop, for use in css modules

Installation

FlexLayout is in the npm repository. Simply install React and FlexLayout from npm:

npm install react --save
npm install react-dom --save
npm install flexlayout-react --save

Import React and FlexLayout in your modules:

import React from "react";
import ReactDOM from "react-dom";
import FlexLayout from "flexlayout-react";

Include the light, gray or dark style in your html:

<link rel="stylesheet" href="node_modules/flexlayout-react/style/light.css" />

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

Optional props:

Prop Description
font the tab font (overrides value in css). Example: font={{size:"12px", style:"italic"}}
icons object mapping keys among close, maximize, restore, more, popout to React nodes to use in place of the default icons, can alternatively return functions for creating the React nodes
onAction function called whenever the layout generates an action to update the model (allows for intercepting actions before they are dispatched to the model, for example, asking the user to confirm a tab close.) Returning undefined from the function will halt the action, otherwise return the action to continue
onRenderTab function called when rendering a tab, allows leading (icon), content section, buttons and name used in overflow menu to be customized
onRenderTabSet function called when rendering a tabset, allows header and buttons to be customized
onModelChange function called when model has changed
onExternalDrag function called when an external object (not a tab) gets dragged onto the layout, with a single dragenter argument. Should return either undefined to reject the drag/drop or an object with keys dragText, jsonDrop, to create a tab via drag (similar to a call to addTabToTabSet). Function onDropis passed the added tabNodeand thedrop DragEvent`, unless the drag was canceled.
classNameMapper function called with default css class name, return value is class name that will be used. Mainly for use with css modules.
i18nMapper function called for each I18nLabel to allow user translation, currently used for tab and tabset move messages, return undefined to use default values
supportsPopout if left undefined will do simple check based on userAgent
popoutURL URL of popout window relative to origin, defaults to popout.html
realtimeResize boolean value, defaults to false, resize tabs as splitters are dragged. Warning: this can cause resizing to become choppy when tabs are slow to draw
onTabDrag function called while dragging a tab, whether from the layout or using addTabWithDragAndDrop. Called with the TabNode being dragged / the tab json from addTabWithDragAndDrop, the TabNode being dragged over, the x and y coordinates relative to the dragged-over tab, and the DockLocation that would be used. Should return undefined for default behavior, or an object containing x, y, width, height, callback, cursor fields. Coordinates are in pixels relative to the dragged-over tab, and callback will be called with the same arguments if the tab is dropped. cursor is an optional string field that should contain a CSS cursor value, such as copy or row-resize. If callback is called, the layout does not perform its default behavior on drop.
onRenderDragRect callback for rendering the drag rectangles
onRenderFloatingTabPlaceholder callback for rendering the floating tab placeholder
onContextMenu callback for handling context actions on tabs and tabsets
onAuxMouseClick callback for handling mouse clicks on tabs and tabsets with alt, meta, shift keys, also handles center mouse clicks
onShowOverflowMenu callback for handling the display of the tab overflow menu
iconFactory a factory function for creating icon components for tab bar buttons.

NOTE: for greater customization of the tab use onRenderTab instead of this callback
titleFactory a factory function for creating title components for tab bar buttons.

NOTE: for greater customization of the tab use onRenderTab instead of this callback

The model is 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 that should be hosted by a tab in the layout.

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

this.state = {model: FlexLayout.Model.fromJson(json)};

render() {
	<Layout model={this.state.model} factory={factory}/>
}

Example Configuration:

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

Example Code

import React from "react";
import ReactDOM from "react-dom";
import FlexLayout from "flexlayout-react";

class Main extends React.Component {

    constructor(props) {
        super(props);
        this.state = {model: FlexLayout.Model.fromJson(json)};
    }

    factory = (node) => {
        var component = node.getComponent();
        if (component === "button") {
            return <button>{node.getName()}</button>;
        }
    }

    render() {
        return (
            <FlexLayout.Layout model={this.state.model} factory={this.factory}/>
        )
    }
}

ReactDOM.render(<Main/>, document.getElementById("container"));

(See the examples for full source code)

The above code would render two tabsets horizontally each containing a single tab that hosts a button component. The tabs could be moved and resized by dragging and dropping. Additional grids could be added to the layout by sending actions to the model.

Try it now using JSFiddle

A simple Create React App (CRA) example (using typescript) can be found here:

https://github.com/nealus/FlexLayout_cra_example

The model is built up using 4 types of 'node':

  • row - rows contains a list of tabsets and child rows, the top level 'row' will render horizontally (unless the global attribute rootOrientationVertical is set) , child 'rows' will render in the opposite orientation to their parent.

  • tabset - tabsets contain a list of tabs and the index of the selected tab

  • tab - tabs specify the name of the component that they should host (that will be loaded via the factory) and the text of the actual tab.

  • border - borders contain a list of tabs and the index of the selected tab, they can only be used in the borders top level element.

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

The model json contains 3 top level elements:

  • global - where global options are defined
  • layout - where the main row/tabset/tabs layout hierarchy is defined
  • borders - (optional) where up to 4 borders are defined ("top", "bottom", "left", "right").

Weights on rows and tabsets specify the relative weight of these nodes within the parent row, the actual values do not matter just their relative values (ie two tabsets of weights 30,70 would render the same if they had weights of 3,7).

NOTE: the easiest way to create your initial layout JSON is to use the demo app, modify one of the existing layouts by dragging/dropping and adding nodes then press the 'Show Layout JSON in console' button to print the JSON to the browser developer console.

example borders section:

    borders: [
         {
            type: "border",
            location: "left",
            children: [
                {
                    type: "tab",
                    enableClose: false,
                    name: "Navigation",
                    component: "grid",
                }
            ]
        },
        {
            type: "border",
            location: "right",
            children: [
                {
                    type: "tab",
                    enableClose: false,
                    name: "Options",
                    component: "grid",
                }
            ]
        },
        {
            type: "border",
            location: "bottom",
            children: [
                {
                    type: "tab",
                    enableClose: false,
                    name: "Activity Blotter",
                    component: "grid",
                },
                {
                    type: "tab",
                    enableClose: false,
                    name: "Execution Blotter",
                    component: "grid",
                }
            ]
        }
    ]

To control where nodes can be dropped you can add a callback function to the model:

model.setOnAllowDrop(this.allowDrop);

example:

    allowDrop(dragNode, dropInfo) {
        let dropNode = dropInfo.node;

        // prevent non-border tabs dropping into borders
        if (dropNode.getType() == "border" && (dragNode.getParent() == null || dragNode.getParent().getType() != "border"))
            return false;

        // prevent border tabs dropping into main layout
        if (dropNode.getType() != "border" && (dragNode.getParent() != null && dragNode.getParent().getType() == "border"))
            return false;

        return true;
    }

By changing global or node attributes you can change the layout 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},

Floating Tabs (Popouts)

Note: this feature only works for Chrome, Firefox, Safari, latest Edge (the Chrome based one) and Opera, it does NOT work for any version of IE or the previous version of Edge. For unsupported browsers the popout icons will not be shown and any saved layout with popouts will show with all their tabs in the main layout.

For supported browsers tabs can be rendered into external browser windows (for use in multi-monitor setups) by configuring them with the enableFloat attribute. When this attribute is present an additional icon is shown in the tab header bar allowing the tab to be popped out into an external window.

For popouts to work there needs to be an additional html page 'popout.html' hosted at the same location as the main page (copy the one from examples/demo). The popout.html is the host page for the popped out tab, the styles from the main page will be copied into it at runtime.

Because popouts are rendering into a different document to the main layout any code in the popped out tab that uses the global document or window objects will not work correctly (for example custom popup menus), they need to instead use the document/window of the popout. To get the document/window of the popout use the following method on one of the elements rendered in the popout (for example a ref or target in an event handler):

    const currentDocument = this.selfRef.current.ownerDocument;
    const currentWindow = currentDocument.defaultView!;

In the above code selfRef is a React ref to the toplevel element in the tab being rendered.

Note: some libraries already support popout windows by allowing you to specify the document to use, for example see the getDocument() callback in agGrid at https://www.ag-grid.com/javascript-grid-callbacks/

Global Config attributes

Attributes allowed in the 'global' element

Attribute Default Description
splitterSize 8 width in pixels of all splitters between tabsets/borders
splitterExtra 0 additional width in pixels of the splitter hit test area
legacyOverflowMenu false use the legacy text only overflow menu
enableEdgeDock true
tabEnableClose true allow user to close all tabs via close button
tabCloseType 1 see values in ICloseType
tabEnableDrag true allow user to drag all tabs to new location
tabEnableRename true allow user to rename all tabs by double clicking
tabEnableFloat false enable popouts in all tabs (in popout capable browser)
tabClassName null
tabIcon null
tabEnableRenderOnDemand true whether to avoid rendering component until tab is visible
tabDragSpeed 0.3 CSS transition speed of drag outlines (in seconds)
tabBorderWidth -1 width when added to border, -1 will use border size
tabBorderHeight -1 height when added to border, -1 will use border size
tabSetEnableDeleteWhenEmpty true
tabSetEnableDrop true allow user to drag tabs into all tabsets
tabSetEnableDrag true allow user to drag tabs out of all tabsets
tabSetEnableDivide true allow user to drag tabs to region of all tabsets, splitting into new tabset
tabSetEnableMaximize true allow user to maximize all tabsets to fill view via maximize button
tabSetEnableClose false allow user to close all tabsets via close button
tabSetAutoSelectTab true whether to select new/moved tabs in all tabsets
tabSetClassNameTabStrip null height in pixels of tab strips in all tabsets
tabSetClassNameHeader null
tabSetEnableTabStrip true enable tab strip and allow multiple tabs in all tabsets
tabSetHeaderHeight 0 height of tabset header in pixels; if left as 0 then the value will be calculated from the current fontSize
tabSetTabStripHeight 0 height of tabset tab bar in pixels; if left as 0 then the value will be calculated from the current fontSize
borderBarSize 0 size of the border bars in pixels; if left as 0 then the value will be calculated from the current fontSize
borderEnableAutoHide false hide border if it has zero tabs
borderEnableDrop true allow user to drag tabs into this border
borderAutoSelectTabWhenOpen true whether to select new/moved tabs in border when the border is already open
borderAutoSelectTabWhenClosed false whether to select new/moved tabs in border when the border is curently closed
borderClassName null
borderSize 200 initial width in pixels for left/right borders, height for top/bottom borders
borderMinSize 0 minimum width in pixels for left/right borders, height for top/bottom borders
tabSetMinHeight 0 minimum width (in px) for all tabsets
tabSetMinWidth 0 minimum height (in px) for all tabsets
tabSetTabLocation top show tabs in location top or bottom
rootOrientationVertical false the top level 'row' will layout horizontally by default, set this option true to make it layout vertically

Row Attributes

Attributes allowed in nodes of type 'row'.

Attribute Default Description
type row
weight 100
width null preferred pixel width
height null preferred pixel height
children required a list of row and tabset nodes

Tab Attributes

Attributes allowed in nodes of type 'tab'.

Inherited defaults will take their value from the associated global attributes (see above).

Attribute Default Description
type tab
name required name of tab to be displayed in the tab button
altName optional if there is no name specifed then this value will be used in the overflow menu
component required string identifying which component to run (for factory)
config null a place to hold json config for the hosted component
id auto generated
helpText optional An optional help text for the tab to be displayed upon tab hover.
enableClose inherited allow user to close tab via close button
closeType inherited see values in ICloseType
enableDrag inherited allow user to drag tab to new location
enableRename inherited allow user to rename tabs by double clicking
enableFloat inherited enable popout (in popout capable browser)
floating false
className inherited
icon inherited
enableRenderOnDemand inherited whether to avoid rendering component until tab is visible
borderWidth inherited width when added to border, -1 will use border size
borderHeight inherited height when added to border, -1 will use border size

Tab nodes have a getExtraData() method that initially returns an empty object, this is the place to add extra data to a tab node that will not be saved.

TabSet Attributes

Attributes allowed in nodes of type 'tabset'.

Inherited defaults will take their value from the associated global attributes (see above).

Note: tabsets can be dynamically created as tabs are moved and deleted when all their tabs are removed (unless enableDeleteWhenEmpty is false).

Attribute Default Description
type tabset
weight 100 relative weight for sizing of this tabset in parent row
width null preferred pixel width
height null preferred pixel height
name null named tabsets will show a header bar above the tabs
config null a place to hold json config used in your own code
selected 0 index of selected/visible tab in tabset
maximized false whether tabset is currently maximized to fill view
enableClose false allow user to close tabset via a close button
id auto generated
children required a list of tab nodes
enableDeleteWhenEmpty inherited
enableDrop inherited allow user to drag tabs into this tabset
enableDrag inherited allow user to drag tabs out this tabset
enableDivide inherited allow user to drag tabs to region of this tabset, splitting into new tabset
enableMaximize inherited allow user to maximize tabset to fill view via maximize button
autoSelectTab inherited whether to select new/moved tabs in tabset
classNameTabStrip inherited
classNameHeader inherited
enableTabStrip inherited enable tab strip and allow multiple tabs in this tabset
headerHeight inherited
tabStripHeight inherited height in pixels of tab strip
tabLocation inherited show tabs in location top or bottom
minHeight inherited minimum width (in px) for this tabset
minWidth inherited minimum height (in px) for this tabset

Border Attributes

Attributes allowed in nodes of type 'border'.

Inherited defaults will take their value from the associated global attributes (see above).

Attribute Default Description
type border
size inherited size of the tab body when selected
minSize inherited
selected -1 index of selected/visible tab in border; -1 means no tab unselected / border closed
id auto generated border_ + border name e.g. border_left
config null a place to hold json config used in your own code
show true show/hide this border
enableAutoHide false hide border if it has zero tabs
children required a list of tab nodes
barSize inherited size of this border's bar in pixels; if left as 0 then the value will be calculated from the current fontSize
enableDrop inherited
autoSelectTabWhenOpen inherited whether to select new/moved tabs in border when the border is already open
autoSelectTabWhenClosed inherited whether to select new/moved tabs in border when the border is currently closed
className inherited

Model Actions

All changes to the model are applied through actions. You can intercept actions resulting from GUI changes before they are applied by implementing the onAction callback property of the Layout. You can also apply actions directly using the Model.doAction() method. This method takes a single argument, created by one of the following action generators (typically accessed as FlexLayout.Actions.<actionName>):

Action Creator Description
Actions.addNode(newNodeJson, toNodeId, location, index, select?) add a new tab node to the given tabset node; select specifies whether to select new tab, defaulting to autoSelectTab attribute; returns the created Node
Actions.moveNode(fromNodeId, toNodeId, location, index, select?) move a tab node from its current location to the new node and location; select specifies whether to select tab, defaulting to new tabset's autoSelectTab attribute
Actions.deleteTab(tabNodeId) delete the given tab
Actions.renameTab(tabNodeId, newName) rename the given tab
Actions.selectTab(tabNodeId) select the given tab
Actions.setActiveTabset(tabsetNodeId) set the tabset as the active tabset
Actions.adjustSplit(splitterNodeId, value) adjust the size of the given splitter
Actions.adjustBorderSplit(borderNodeId, pos) updates the size of the given border node
Actions.maximizeToggle(tabsetNodeId) toggles whether the given tabset node is maximized
Actions.updateModelAttributes(attributes) updates the global attributes
Actions.updateNodeAttributes(nodeId, attributes) updates the attributes of the given node
Actions.floatTab(nodeId) popout the tab into a floating browser window
Actions.unFloatTab(nodeId) restore a popped out tab to the main layout

Examples

model.doAction(FlexLayout.Actions.updateModelAttributes({
    splitterSize:40,
    tabSetHeaderHeight:40,
    tabSetTabStripHeight:40
}));

The above example would increase the size of the splitters, tabset headers and tabs, this could be used to make adjusting the layout easier on a small device.

model.doAction(FlexLayout.Actions.addNode(
    {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 tabset with id "1" and at the 0'th tab position (use value -1 to add to the end of the tabs). Note: you can get the id of a node (e.g., the node returned by the addNode action) using the method node.getId(). If an id wasn't assigned when the node was created, then one will be created for you of the form #<uuid> (e.g. #0c459064-8dee-444e-8636-eb9ab910fb27).

Layout Component Methods to Create New Tabs

Methods on the Layout Component for adding tabs, the tabs are specified by their layout json.

Example:

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

This would add a new grid component to the tabset with id "NAVIGATION" (where this.layoutRef is a ref to the Layout element, see https://reactjs.org/docs/refs-and-the-dom.html ).

Layout Method Description
addTabToTabSet(tabsetId, json) adds a new tab to the tabset with the given Id
addTabToActiveTabSet(json) adds a new tab to the active tabset
addTabWithDragAndDrop(dragText, json, onDrop) adds a new tab by dragging a marker to the required location, with the drag starting immediately; on success, onDrop is passed the created tab Node; on cancel, no arguments are passed
addTabWithDragAndDropIndirect(dragText, json, onDrop) adds a new tab by dragging a marker to the required location, the marker is shown and must be clicked on to start dragging

Tab Node Events

You can handle events on nodes by adding a listener, this would typically be done in the components constructor() method.

Example:

    constructor(props) {
        super(props);
        let config = this.props.node.getConfig();

        // save state in flexlayout node tree
        this.props.node.setEventListener("save", (p) => {
             config.subject = this.subject;
        };
    }

Event parameters Description
resize called when tab is resized during layout, called before it is rendered with the new size
close called when a tab is closed
visibility called when the visibility of a tab changes
save called before a tabnode is serialized to json, use to save node config by adding data to the object returned by node.getConfig()

Running the Examples and Building the Project

First install dependencies:

yarn install

Compile the project and run the examples:

yarn start

Open your browser at http://localhost:8080/examples/ to show the examples directory, click on the examples to run them.

The 'yarn start' command will watch for changes to flexlayout and example source, so you can make changes to the code and then refresh the browser to see the result.

To run the tests in the Cypress interactive runner use:

yarn cypress

FlexLayout Cypress tests

To build the npm distribution run 'yarn build', this will create the artifacts in the dist dir.

GitHub

https://github.com/caplin/FlexLayout/
Comments
  • 1. Allow custom drops to know when they are invalidated

    Use case: You want people to be able to scroll the list while they are dragging an item in it

    Depends on #266

    https://user-images.githubusercontent.com/4723091/136687188-f6dca673-e876-4293-adb2-f5ed2a22c16f.mp4

    ~~Currently the outline is not updated on scroll, I want to come up with a solution for that too. Stay tuned and I will add it to this PR~~ Fixed

    Reviewed by LoganDark at 2021-10-10 07:46
  • 2. Creating a Tabset inside a Tab

    Hi all,

    I am trying to create a new Tabset inside an active Tab.

    You can refer to the illustration below where Tabset 2 is inside Tab 1. When user moves away from Tab 1, the bottom Tabset 2 should not render since it is stored inside Tab 1.

    flex-layout

    Use Case The active Tab has a 1 to many relationship to the Tabs in Tabset 2. For instance, the active Tab contains a Parent Report and the tabs inside Tabset 2 are sub reports.

    What I have tried

    1. Adding tabs into the border array. However, when I switch to another Tab, the tabs in the border remains.
    2. Adding normal tabs with DockLocation.BOTTOM. And this obviously does not work because the bottom positioned tab will remain even when I switch to other tabs.

    Possible solutions

    1. Create a new FlexLayout instance inside the active Tab. Is that possible?

    Thank you, and looking forward to any suggestions.

    Reviewed by maxnathaniel at 2020-09-24 03:07
  • 3. Drag external objects onto layout to make tabs

    This PR adds a configuration option onExternalDrag to allow the layout to handle drags from outside the layout (e.g. files or links or even draggable objects from other parts of the app outside the layout) that cause the creation of tabs. In my app, I want to be able to drag URLs from e.g. another browser window in order to create a tab that iframes that URL.

    Most of the changes are local to DragDrop, which has extra event handlers for the many various drag/drop events (and the usual counter workaround to handle dragging entering/leaving various elements). The main other change is that Layout needs to listen to dragenter as yet another way to start a new drag operation.

    I hope you like it! Let me know if you want it tweaked in any way.

    (This is the main feature addition that led me down the path to the other PR and issue.)

    Reviewed by edemaine at 2021-03-31 23:59
  • 4. How to use selfRef with ag-grid ?

    How can i access selfRef and then pass it down to ag-grid component ?

    factory(node) {
        let component = node.getComponent();
        if (component === "AddCall") {
          return <AddCall />;
        } else if (component === 'CallsList') {
          return <CallsList /> // AG-Grid Component     <============
        }
        else if (component === 'Map') {
          return <Map />;
        }
      }
    Reviewed by mostafa-raafat at 2020-09-10 15:55
  • 5. How to delete/close a TabSet?

    I'm trying to programmatically delete a TabSet.

    Actions.deleteTab doesn't do anything (possibly because it's not a tab): https://github.com/caplin/FlexLayout/blob/d8cac2915e8968a26bf3592ffe70c248c0d9d2dc/src/model/Model.ts#L257

    model.doAction(Actions.deleteTab(tabSet.getId()));
    

    Deleting each tab separately closes all the tabSet's content, but the tabset itself is still there.

    tabSet
      .getChildren()
      .map((node) => Actions.deleteTab(node.getId()))
      .forEach((action) => model.doAction(action));
    

    I tried updating the parent's attributes too, eg.:

    const parent = tabSet.getParent();
    if (isNil(parent)) {
      return;
    }
    // Logic not exactly the same, but illustrates my point
    model.doAction(Actions.updateNodeAttributes(parent.getId(), { children: [] }));
    

    But this doesn't do anything either.

    Can you advise on how to kill a tabset?

    Reviewed by balazs-edes-lab49 at 2021-09-30 09:46
  • 6. Bug: Resize listeners get stripped when tab is popped out

    Window listeners appear to be removed when using the enableFloat feature on a tab. I am using xterm.js which relies on defining the width and height of a window explicitly in order to render the terminal correctly in the browser window. In order to do that, I've implemented a ResizeObserver and attached it to the div of my xterm.js component. It works great when the terminal is attached to a non-floating tab. However, when I pop the tab out, all resize observers are gone and resizing is no longer detected.

    Reviewed by allfro at 2020-10-19 17:07
  • 7. Allow custom dropping of tabs inside other tabs

    This allows consumers to use flexlayout's dnd infrastructure for the pretty outline and ability to drag it out to dock with other tabs, while also being able to interact more deeply with the components inside of tabs.

    Example: a list where each item can be dragged around in the same list, or dragged out to create a new tab. Tabs can also be dragged from outside, into the list! This kind of interaction is currently not possible with flexlayout.

    This new feature only requires one new prop passed to the layout. A user-provided function will tell flexlayout when it wants to override the default docking behavior. Currently, only tabs can have their drop behavior modified. I don't think tab strips need it.

    Closes #254

    I couldn't find a fitting demo other than overriding drag for something. Please advise if you need the behavior changed. Alternatively you have access to the branch.

    Reviewed by LoganDark at 2021-09-30 06:32
  • 8. React Leaflet Map is displayed in any other TAB popout

    Hi, I'm experimenting with FlexLaout. I has created a simple experimental app. I am using the original default layout. In one of the TAB, I'm using react-leaflet to display OpenStreetMap, in another TAB, I'm displaying a Data Grid with some data (but this is not relevant). Now the problem I'm struggling with is if I popout the MAP Tab, everything is fine, I see only the MAP with the popout window. But, hoping out any other TAB, displays the content of that TAB, but with the MAP as an overlay. Any hint, what do I need to check to prevent this? I have attached the image of the normal main window, and another depicting the problem. I have also attached a zip of my React Javascript project, which you can run from the client folder, first npm install then npm run dev.

    mainwindow problem

    sandbox.zip

    **.

    Reviewed by PatrickIl at 2020-07-21 14:44
  • 9. buttonFactory prop for customizing buttons.

    Hello,

    Im working on a PR to be able to customize full buttons and not just the button icons. Would you like me to implement a ButtonNode model similar to TabNode or would an interface be fine?

    The way I have it now is:

    export type ButtonType = 'overflow' | 'float' | 'max' | 'min' | 'close' 
    
    export interface IButton {
      name: ButtonType
      icon: React.ReactNode
      hidden?: { node: TabNode, index: number }[]
      props: {
        key: string
        ref: React.MutableRefObject<HTMLButtonElement | null>
        'data-layout-path': string
        title: string
        onClick: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void
        onMouseDown: (event: React.MouseEvent | React.TouchEvent) => void
        onTouchStart: (event: React.MouseEvent | React.TouchEvent) => void
     }
    }
    
    buttonFactory?: (button: IButton) => React.ReactNode | undefined;
    
    const buttonFactory = (button: IButton) => {
      switch(button.name) {
        case 'max':
          return <button {...button.props}>{button.icon}</button>
        default: 
          return undefined
      }
    }
    
    <Layout
      buttonFactory={buttonFactory}
      ...
    />
    

    I'd want to get this done the way you want it on the first go, so let me know if you would prefer a model over an interface and I can go down that route instead.

    Thanks!

    Reviewed by SupremeTechnopriest at 2021-11-27 00:24
  • 10. How do I get the tabset ID after the move?

    First of all, thank you so much for publishing this wonderful library.

    In onAction, when action.type is FlexLayout_MoveNode, I cannot get the tabset ID after the move.

    ■ Movement when tabs are added to another tab set If "location" is "center", the destination tabset ID can be get from toNode.

    Action: {
      data: {
        "fromNode": "tab_0",
        "toNode": "tabset_0",	<= Destination tabset ID
        "location": "center",
        "index": 1
      },
      type: "FlexLayout_MoveNode"
    }
    

    Movement when a new tab set is created If "location" is not "center", the newly added tabset ID cannot be get.

    Action: {
      data: {
        "fromNode": "tab_0",
        "toNode": "tabset_0",	<= Destination tabset ID
        "location": "right",
        "index": -1
      },
      type: "FlexLayout_MoveNode"
    }
    

    This is probably because FlexLayout_MoveNode in onAction is called at the start of tab movement. How do I get the tabset ID after the move is complete?

    Reviewed by kijitora2021 at 2022-05-03 02:40
  • 11. React context is not available inside onRenderTab when dragging the tab after updating to 0.6.0

    I use React's context and more specifically useSelector from react-redux in a component which I set in renderValues.content of the onRenderTab. When I drag the tab, I get the following error: Uncaught Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>.

    I get this error only in 0.6.0 version.

    You can see the problem in the CodeSandbox below.

    https://codesandbox.io/s/flexlayout-react-forked-l1shf

    Reviewed by MariosVisos at 2021-11-25 15:57
  • 12. Allow users to set activeTabset to undefined

    This PR creates a way to not have a selected tab. I needed this feature for a project I'm working on with popout windows and I couldn't find another way to do it.

    Reviewed by alexwaeseperlman at 2022-07-29 07:09
  • 13. Bump terser from 5.10.0 to 5.14.2

    Bumps terser from 5.10.0 to 5.14.2.

    Changelog

    Sourced from terser's changelog.

    v5.14.2

    • Security fix for RegExps that should not be evaluated (regexp DDOS)
    • Source maps improvements (#1211)
    • Performance improvements in long property access evaluation (#1213)

    v5.14.1

    • keep_numbers option added to TypeScript defs (#1208)
    • Fixed parsing of nested template strings (#1204)

    v5.14.0

    • Switched to @​jridgewell/source-map for sourcemap generation (#1190, #1181)
    • Fixed source maps with non-terminated segments (#1106)
    • Enabled typescript types to be imported from the package (#1194)
    • Extra DOM props have been added (#1191)
    • Delete the AST while generating code, as a means to save RAM

    v5.13.1

    • Removed self-assignments (varname=varname) (closes #1081)
    • Separated inlining code (for inlining things into references, or removing IIFEs)
    • Allow multiple identifiers with the same name in var destructuring (eg var { a, a } = x) (#1176)

    v5.13.0

    • All calls to eval() were removed (#1171, #1184)
    • source-map was updated to 0.8.0-beta.0 (#1164)
    • NavigatorUAData was added to domprops to avoid property mangling (#1166)

    v5.12.1

    • Fixed an issue with function definitions inside blocks (#1155)
    • Fixed parens of new in some situations (closes #1159)

    v5.12.0

    • TERSER_DEBUG_DIR environment variable
    • @​copyright comments are now preserved with the comments="some" option (#1153)

    v5.11.0

    • Unicode code point escapes (\u{abcde}) are not emitted inside RegExp literals anymore (#1147)
    • acorn is now a regular dependency
    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    Reviewed by dependabot[bot] at 2022-07-20 01:27
  • 14. Pop-outs not working in react 18 with React.StrictMode?

    Describe the bug

    WIth React 18 and </React.StrictMode> pop-outs are not visible. When I click on pop-out, it creates a new window and quickly dissapears. I can click "dock window" but it doesn't reappear. If I remove </React.StrictMode> pop-outs work as expected. Some googling seemed to suggest portals may not work the same in strict mode.

    Your Example Website or App

    No response

    Steps to Reproduce the Bug or Issue

    Apologies on not providing a working example but pop-out is not essential for my work so I can't investigate further as I'm happy to turn them off.

    Expected behavior

    Pop-outs pop out and display the tab.

    Operating System

    windows

    Browser Type?

    Edge / Chrome / Firefox.

    Browser Version

    Edge 103

    Screenshots or Videos

    .

    Additional context

    No response

    Reviewed by ryanhamilton at 2022-07-07 08:16
  • 15. Keep all tabs mounted

    Describe the bug

    Can we add support to either mount all tabs immediately, or lazy loading -- once mounted, don't unmount them (unless the tab is closed)?

    This is useful for persisting local states when switching tabs.

    Your Example Website or App

    No response

    Steps to Reproduce the Bug or Issue

    Expected behavior

    Operating System

    MacOS

    Browser Type?

    Chrome

    Browser Version

    Version 101.0.4951.67

    Screenshots or Videos

    Additional context

    No response

    Reviewed by riwu at 2022-05-17 16:55
  • 16. Bump ejs from 3.1.6 to 3.1.7

    Bumps ejs from 3.1.6 to 3.1.7.

    Release notes

    Sourced from ejs's releases.

    v3.1.7

    Version 3.1.7

    Commits
    • 820855a Version 3.1.7
    • 076dcb6 Don't use template literal
    • faf8b84 Skip test -- error message vary depending on JS runtime
    • c028c34 Update packages
    • e4180b4 Merge pull request #629 from markbrouwer96/main
    • d5404d6 Updated jsdoc to 3.6.7
    • 7b0845d Merge pull request #609 from mde/dependabot/npm_and_yarn/glob-parent-5.1.2
    • 32fb8ee Bump glob-parent from 5.1.1 to 5.1.2
    • f21a9e4 Merge pull request #603 from mde/mde-null-proto-where-possible
    • a50e46f Merge pull request #606 from akash-55/main
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    Reviewed by dependabot[bot] at 2022-04-27 17:54
  • 17. Bump node-forge from 1.2.1 to 1.3.1

    Bumps node-forge from 1.2.1 to 1.3.1.

    Changelog

    Sourced from node-forge's changelog.

    1.3.1 - 2022-03-29

    Fixes

    • RFC 3447 and RFC 8017 allow for optional DigestAlgorithm NULL parameters for sha* algorithms and require NULL paramters for md2 and md5 algorithms.

    1.3.0 - 2022-03-17

    Security

    • Three RSA PKCS#1 v1.5 signature verification issues were reported by Moosa Yahyazadeh ([email protected]).
    • HIGH: Leniency in checking digestAlgorithm structure can lead to signature forgery.
    • HIGH: Failing to check tailing garbage bytes can lead to signature forgery.
    • MEDIUM: Leniency in checking type octet.
      • DigestInfo is not properly checked for proper ASN.1 structure. This can lead to successful verification with signatures that contain invalid structures but a valid digest.
      • CVE ID: CVE-2022-24773
      • GHSA ID: GHSA-2r2c-g63r-vccr

    Fixed

    • [asn1] Add fallback to pretty print invalid UTF8 data.
    • [asn1] fromDer is now more strict and will default to ensuring all input bytes are parsed or throw an error. A new option parseAllBytes can disable this behavior.
      • NOTE: The previous behavior is being changed since it can lead to security issues with crafted inputs. It is possible that code doing custom DER parsing may need to adapt to this new behavior and optional flag.
    • [rsa] Add and use a validator to check for proper structure of parsed ASN.1

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    Reviewed by dependabot[bot] at 2022-04-17 07:46
💃 Make your react tab dance🕺
💃 Make your react tab dance🕺

A mobile support, draggable, editable and api based Tab for ReactJS. Support react >= v16.3 Note: Since v2, we don't support v15 and old styled-compon

Jul 18, 2022
Headless, simple, and highly flexible tab-like primitives built with react hooks

react-headless-tabs Headless and highly flexible tab-like primitives built with react hooks ?? Check out the documentation and interactive examples! F

Jul 28, 2022
A fully accessible, extravagantly flexible, React-powered Tab Panel component

react-aria-tabpanel SEEKING CO-MAINTAINERS! Continued development of this project is going to require the work of one or more dedicated co-maintainers

Aug 7, 2022
Atom like draggable tab react component
Atom like draggable tab react component

React-draggable-tab Atom like draggable tab react component. Demo View Demo Installation npm install --save react-draggable-tab React v0.14 is support

Apr 27, 2022
React reusable tab component
React reusable tab component

react-re-super-tabs React reusable tab component Demo Installing yarn: yarn add react-re-super-tabs npm: npm install react-re-super-tabs --save Usage

May 7, 2022
A simple tab scroll view for react native

react-native-tab-flatlist ReactNative 跨平台实现TabView嵌套ScrollView滚动吸顶效果,采用react-native-tab-view+flatlist实现效果丝滑 安装 使用前需要先安装react-native-tab-view 与 react-n

Feb 23, 2022
Add collapsible headers to your tab-view components.
Add collapsible headers to your tab-view components.

React Native Head Tab View ?? ?? ?? v4.0.0-rc.13 has been released, I hope you can help me test and collect questions. In this version, there is a big

Aug 8, 2022
Fast, open and free-to-use new tab page for modern browsers
Fast, open and free-to-use new tab page for modern browsers

Mue Mue is a fast, open and free-to-use browser extension that gives a new, fresh and customisable tab page to modern browsers. Table of contents Scre

Jul 12, 2022
UseTabs - Reusable way to create smooth tab highlights
UseTabs - Reusable way to create smooth tab highlights

useTabs Reusable way to create smooth tab highlights. Installation Install my-pr

Jul 31, 2022
A multi-tab layout manager
A multi-tab layout manager

FlexLayout FlexLayout is a layout manager that arranges React components in multiple tab sets, tabs can be resized and moved. Run the Demo Try it now

Aug 1, 2022
FlexLayout is a layout manager that arranges React components in multiple tab sets, tabs can be resized and moved.
FlexLayout is a layout manager that arranges React components in multiple tab sets, tabs can be resized and moved.

FlexLayout is a layout manager that arranges React components in multiple tab sets, tabs can be resized and moved.

Aug 5, 2022
A multi window layout manager for webapps

Golden Layout Version 2 Version 2.0 is now available from NPM. This version is a substantial change from the previous (1.5.9) version. The change can

Aug 7, 2022
Tabbed navigation that you can swipe between, each tab can have its own ScrollView and maintain its own scroll position between swipes. Pleasantly animated. Customizable tab bar

react-native-scrollable-tab-view This is probably my favorite navigation pattern on Android, I wish it were more common on iOS! This is a very simple

Jul 30, 2022
React Native platform-independent tabs. Could be used for bottom tab bars as well as sectioned views (with tab buttons)
React Native platform-independent tabs. Could be used for bottom tab bars as well as sectioned views (with tab buttons)

react-native-tabs React Native platform-independent tabs. Could be used for bottom tab bars as well as sectioned views (with tab buttons) Why I need t

Jul 12, 2022
❄️ Winterly Tab ❄️ Beautifully replace new tab screen with winter themed background.
❄️ Winterly Tab ❄️ Beautifully replace new tab screen with winter themed background.

Beautifully replace the new-tab page in your web browser with winter-themed background. It's free & open source!

Jan 23, 2022
React-layout - Layout component for React. Handling the overall layout of a page

Layout Handling the overall layout of a page. ⚠️ Note: Implemented with flex lay

Jul 10, 2022
Platform independent (Android / iOS) Selectbox | Picker | Multi-select | Multi-picker.
Platform independent (Android / iOS) Selectbox | Picker | Multi-select | Multi-picker.

react-native-multi-selectbox Platform independent (Android / iOS) Selectbox | Picker | Multi-select | Multi-picker. The idea is to bring out the commo

Aug 9, 2022
React-Grid-Layout is a grid layout system much like Packery or Gridster, for React.
React-Grid-Layout is a grid layout system much like Packery or Gridster, for React.

A draggable and resizable grid layout with responsive breakpoints, for React.

Aug 5, 2022
Atomic Layout is a physical representation of layout composition to create declarative responsive layouts in React.
Atomic Layout is a physical representation of layout composition to create declarative responsive layouts in React.

Atomic Layout is a spatial distribution library for React. It uses CSS Grid to define layout areas and render them as React components.

Jul 29, 2022
Layout-reactJS - Layout with React JS using NASA API
Layout-reactJS - Layout with React JS using NASA API

Layout with React JS using NASA API Website link on the web: Click Here Preview:

Feb 2, 2022