Skip to content

Commit

Permalink
Merge branch 'Add-Tabset-delete-action'
Browse files Browse the repository at this point in the history
  • Loading branch information
nealus committed Oct 4, 2021
2 parents 6ec6a8f + 536fe91 commit e47e3e0
Show file tree
Hide file tree
Showing 18 changed files with 247 additions and 36 deletions.
6 changes: 6 additions & 0 deletions ChangeLog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
0.5.16
Added 'New Features' layout to demo.
New tab attribute, helpText, to show tooltip over tabs.
New model action, deleteTabset, to delete a tabset and all it's child tabs.
New tabset attribute, enableClose, to close the tabset

0.5.15
Added new Layout prop: onTabDrag that allows tab dragging to be intercepted.
Added example of onTabDrag in demo app, example shows a list where tabs can be dragged into,
Expand Down
47 changes: 30 additions & 17 deletions examples/demo/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import * as React from "react";
import * as React from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import * as ReactDOM from "react-dom";
import * as FlexLayout from "../../src/index";
import * as ReactDOM from "react-dom";
import * as FlexLayout from "../../src/index";
import { Action, Actions, BorderNode, DropInfo, IJsonTabNode, Node, Rect, TabNode, TabSetNode } from "../../src/index";
import { ILayoutProps, ITabRenderValues, ITabSetRenderValues } from "../../src/view/Layout";
import Utils from "./Utils";
import { ILayoutProps, ITabRenderValues, ITabSetRenderValues } from "../../src/view/Layout";
import Utils from "./Utils";

var fields = ["Name", "Field1", "Field2", "Field3", "Field4", "Field5"];

Expand All @@ -15,7 +15,7 @@ class App extends React.Component<any, { layoutFile: string | null, model: FlexL

constructor(props: any) {
super(props);
this.state = { layoutFile: null, model: null, adding: false, fontSize: "medium", realtimeResize: false};
this.state = { layoutFile: null, model: null, adding: false, fontSize: "medium", realtimeResize: false };

// save layout when unloading page
window.onbeforeunload = (event: Event) => {
Expand Down Expand Up @@ -137,7 +137,7 @@ class App extends React.Component<any, { layoutFile: string | null, model: FlexL

onRealtimeResize = (event: React.ChangeEvent<HTMLInputElement>) => {
this.setState({
realtimeResize: event.target.checked
realtimeResize: event.target.checked
});
}

Expand Down Expand Up @@ -198,9 +198,9 @@ class App extends React.Component<any, { layoutFile: string | null, model: FlexL

factory = (node: TabNode) => {
// log lifecycle events
//node.setEventListener("resize", function(p){console.log("resize");});
//node.setEventListener("visibility", function(p){console.log("visibility");});
//node.setEventListener("close", function(p){console.log("close");});
//node.setEventListener("resize", function(p){console.log("resize", node);});
//node.setEventListener("visibility", function(p){console.log("visibility", node);});
//node.setEventListener("close", function(p){console.log("close", node);});

var component = node.getComponent();

Expand Down Expand Up @@ -316,7 +316,7 @@ class App extends React.Component<any, { layoutFile: string | null, model: FlexL
alt="Add"
key="Add button"
title="Add Tab (using onRenderTabSet callback, see Demo)"
style={{ marginLeft: 5, width:24, height:24 }}
style={{ marginLeft: 5, width: 24, height: 24 }}
onClick={() => this.onAddFromTabSetButton(node)}
/>);
}
Expand Down Expand Up @@ -372,6 +372,7 @@ class App extends React.Component<any, { layoutFile: string | null, model: FlexL
<div className="toolbar">
<select onChange={this.onSelectLayout}>
<option value="default">Default</option>
<option value="newfeatures">New Features</option>
<option value="simple">Simple</option>
<option value="justsplitters">Just Splitters</option>
<option value="sub">SubLayout</option>
Expand Down Expand Up @@ -484,11 +485,11 @@ class SimpleTable extends React.Component<{ fields: any, data: any, onClick: any
}
}

function TabStorage({tab, layout}: {tab: TabNode, layout: FlexLayout.Layout}) {
function TabStorage({ tab, layout }: { tab: TabNode, layout: FlexLayout.Layout }) {
const [storedTabs, setStoredTabs] = useState<IJsonTabNode[]>(tab.getConfig()?.storedTabs ?? [])

useEffect(() => {
tab.getModel().doAction(Actions.updateNodeAttributes(tab.getId(), {config: {...(tab.getConfig() ?? {}), storedTabs}}))
tab.getModel().doAction(Actions.updateNodeAttributes(tab.getId(), { config: { ...(tab.getConfig() ?? {}), storedTabs } }))
}, [storedTabs])

const [contents, setContents] = useState<HTMLDivElement | null>(null)
Expand Down Expand Up @@ -578,10 +579,22 @@ function TabStorage({tab, layout}: {tab: TabNode, layout: FlexLayout.Layout}) {
</p>
<div ref={setList} className="tab-storage-tabs">
{storedTabs.length === 0 && <div ref={setEmptyElem} className="tab-storage-empty">Looks like there's nothing here! Try dragging a tab over this text.</div>}
{storedTabs.map((stored, i) => <div ref={ref => refs[i] = ref ?? undefined} className="tab-storage-entry" key={stored.id} onMouseDown={e => {
e.preventDefault()
layout.addTabWithDragAndDrop(stored.name ?? 'Unnamed', stored, (node) => node && setStoredTabs(tabs => tabs.filter(tab => tab !== stored)))
}}>{stored.name ?? 'Unnamed'}</div>)}
{storedTabs.map((stored, i) => (
<div
ref={ref => refs[i] = ref ?? undefined}
className="tab-storage-entry"
key={stored.id}
onMouseDown={e => {
e.preventDefault()
layout.addTabWithDragAndDrop(stored.name ?? 'Unnamed', stored, (node) => node && setStoredTabs(tabs => tabs.filter(tab => tab !== stored)))
}}
onTouchStart={e => {
layout.addTabWithDragAndDrop(stored.name ?? 'Unnamed', stored, (node) => node && setStoredTabs(tabs => tabs.filter(tab => tab !== stored)))
}}
>
{stored.name ?? 'Unnamed'}
</div>))
}
</div>
</div>
}
Expand Down
5 changes: 5 additions & 0 deletions examples/demo/gray.css
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,8 @@ html,body
background-color: #ddd;
border-radius: 0.5em;
}

small {
color: gray;
}

5 changes: 0 additions & 5 deletions examples/demo/layouts/default.layout
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@
"type":"url",
"data": "https://en.wikipedia.org/wiki/Main_Page"
}
},
{
"type": "tab",
"name": "Tab Storage",
"component": "tabstorage"
}
]
}
Expand Down
87 changes: 87 additions & 0 deletions examples/demo/layouts/newfeatures.layout
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
{
"global": {
"tabEnableFloat": true,
"tabSetEnableClose":true
},
"layout": {
"type": "row",
"children": [
{
"type": "tabset",
"weight": 12.5,
"active": true,
"children": [
{
"type": "tab",
"name": "One",
"helpText": "this tab has helpText defined",
"component": "text",
"config": {
"text":"New:<ul><li>Help text (tooltip) option on tabs: <br><small>Hover over this tab button</small></li><li>Action to close tabset:<br><small>See added x button in this tabset</small></li><li>Intercept drag drop to allow dropping tabs into custom areas:<br><small>See Tab Storage tab<small></li></ul>"
}
},
{
"type": "tab",
"name": "Two",
"component": "grid"
}
]
},
{
"type": "tabset",
"weight": 25,
"children": [
{
"type": "tab",
"name": "Tab Storage",
"component": "tabstorage"
}
]
}
]
},
"borders": [
{
"type": "border",
"location": "bottom",
"children": [
{
"type": "tab",
"enableClose":false,
"name": "Output",
"component": "grid"
},
{
"type": "tab",
"enableClose":false,
"name": "Terminal",
"component": "grid"
}
]
},
{
"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"
}
]
}
]
}
1 change: 1 addition & 0 deletions src/I18nLabel.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export enum I18nLabel {
Close_Tab = "Close",
Close_Tabset = "Close tabset",
Move_Tab = "Move: ",
Move_Tabset = "Move tabset",
Maximize = "Maximize tabset",
Expand Down
2 changes: 2 additions & 0 deletions src/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,6 @@ export enum CLASSES {
FLEXLAYOUT__TAB_TOOLBAR_BUTTON_ = "flexlayout__tab_toolbar_button-",
FLEXLAYOUT__TAB_TOOLBAR_BUTTON_FLOAT = "flexlayout__tab_toolbar_button-float",
FLEXLAYOUT__TAB_TOOLBAR_STICKY_BUTTONS_CONTAINER = "flexlayout__tab_toolbar_sticky_buttons_container",
FLEXLAYOUT__TAB_TOOLBAR_BUTTON_CLOSE = "flexlayout__tab_toolbar_button-close",

}
32 changes: 21 additions & 11 deletions src/model/Actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Actions {
static ADD_NODE = "FlexLayout_AddNode";
static MOVE_NODE = "FlexLayout_MoveNode";
static DELETE_TAB = "FlexLayout_DeleteTab";
static DELETE_TABSET = "FlexLayout_DeleteTabset";
static RENAME_TAB = "FlexLayout_RenameTab";
static SELECT_TAB = "FlexLayout_SelectTab";
static SET_ACTIVE_TABSET = "FlexLayout_SetActiveTabset";
Expand All @@ -26,7 +27,7 @@ class Actions {
* @param location the location where the new tab will be added, one of the DockLocation enum values.
* @param index for docking to the center this value is the index of the tab, use -1 to add to the end.
* @param select (optional) whether to select the new tab, overriding autoSelectTab
* @returns {{type: (string|string), json: *, toNode: *, location: (*|string), index: *, select?: boolean}}
* @returns {Action} the action
*/
static addNode(json: any, toNodeId: string, location: DockLocation, index: number, select?: boolean): Action {
return new Action(Actions.ADD_NODE, {
Expand All @@ -45,7 +46,7 @@ class Actions {
* @param location the location where the moved node will be added, one of the DockLocation enum values.
* @param index for docking to the center this value is the index of the tab, use -1 to add to the end.
* @param select (optional) whether to select the moved tab(s) in new tabset, overriding autoSelectTab
* @returns {{type: (string|string), fromNode: *, toNode: *, location: (*|string), index: *}}
* @returns {Action} the action
*/
static moveNode(fromNodeId: string, toNodeId: string, location: DockLocation, index: number, select?: boolean): Action {
return new Action(Actions.MOVE_NODE, {
Expand All @@ -59,18 +60,27 @@ class Actions {

/**
* Deletes a tab node from the layout
* @param tabNodeId the id of the node to delete
* @returns {{type: (string|string), node: *}}
* @param tabsetNodeId the id of the tab node to delete
* @returns {Action} the action
*/
static deleteTab(tabNodeId: string): Action {
return new Action(Actions.DELETE_TAB, { node: tabNodeId });
}

/**
* Deletes a tabset node and all it's child tab nodes from the layout
* @param tabsetNodeId the id of the tabset node to delete
* @returns {Action} the action
*/
static deleteTabset(tabsetNodeId: string): Action {
return new Action(Actions.DELETE_TABSET, { node: tabsetNodeId });
}

/**
* Change the given nodes tab text
* @param tabNodeId the id of the node to rename
* @param text the test of the tab
* @returns {{type: (string|string), node: *, text: *}}
* @returns {Action} the action
*/
static renameTab(tabNodeId: string, text: string): Action {
return new Action(Actions.RENAME_TAB, { node: tabNodeId, text });
Expand All @@ -79,7 +89,7 @@ class Actions {
/**
* Selects the given tab in its parent tabset
* @param tabNodeId the id of the node to set selected
* @returns {{type: (string|string), tabNode: *}}
* @returns {Action} the action
*/
static selectTab(tabNodeId: string): Action {
return new Action(Actions.SELECT_TAB, { tabNode: tabNodeId });
Expand All @@ -88,7 +98,7 @@ class Actions {
/**
* Set the given tabset node as the active tabset
* @param tabsetNodeId the id of the tabset node to set as active
* @returns {{type: (string|string), tabsetNode: *}}
* @returns {Action} the action
*/
static setActiveTabset(tabsetNodeId: string): Action {
return new Action(Actions.SET_ACTIVE_TABSET, { tabsetNode: tabsetNodeId });
Expand All @@ -100,7 +110,7 @@ class Actions {
* Actions.adjustSplit({node1: "1", weight1:30, pixelWidth1:300, node2: "2", weight2:70, pixelWidth2:700});
*
* @param splitSpec an object the defines the new split between two tabsets, see example below.
* @returns {{type: (string|string), node1: *, weight1: *, pixelWidth1: *, node2: *, weight2: *, pixelWidth2: *}}
* @returns {Action} the action
*/
static adjustSplit(splitSpec: { node1Id: string; weight1: number; pixelWidth1: number; node2Id: string; weight2: number; pixelWidth2: number }): Action {
const node1 = splitSpec.node1Id;
Expand All @@ -123,7 +133,7 @@ class Actions {
/**
* Maximizes the given tabset
* @param tabsetNodeId the id of the tabset to maximize
* @returns {{type: (string|string), node: *}}
* @returns {Action} the action
*/
static maximizeToggle(tabsetNodeId: string): Action {
return new Action(Actions.MAXIMIZE_TOGGLE, { node: tabsetNodeId });
Expand All @@ -132,7 +142,7 @@ class Actions {
/**
* Updates the global model jsone attributes
* @param attributes the json for the model attributes to update (merge into the existing attributes)
* @returns {{type: (string|string), json: *}}
* @returns {Action} the action
*/
static updateModelAttributes(attributes: any): Action {
return new Action(Actions.UPDATE_MODEL_ATTRIBUTES, { json: attributes });
Expand All @@ -142,7 +152,7 @@ class Actions {
* Updates the given nodes json attributes
* @param nodeId the id of the node to update
* @param attributes the json attributes to update (merge with the existing attributes)
* @returns {{type: (string|string), node: *, json: *}}
* @returns {Action} the action
*/
static updateNodeAttributes(nodeId: string, attributes: any): Action {
return new Action(Actions.UPDATE_NODE_ATTRIBUTES, { node: nodeId, json: attributes });
Expand Down
2 changes: 2 additions & 0 deletions src/model/IJsonModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export interface IGlobalAttributes {
tabSetBorderInsets?: IInsets; // default: {"top":0,"right":0,"bottom":0,"left":0}
tabSetClassNameHeader?: string;
tabSetClassNameTabStrip?: string;
tabSetEnableClose?: boolean; // default: false
tabSetEnableDeleteWhenEmpty?: boolean; // default: true
tabSetEnableDivide?: boolean; // default: true
tabSetEnableDrag?: boolean; // default: true
Expand All @@ -81,6 +82,7 @@ export interface ITabSetAttributes {
classNameHeader?: string; // - inherited from global tabSetClassNameHeader
classNameTabStrip?: string; // - inherited from global tabSetClassNameTabStrip
config?: any;
enableClose?: boolean; // default: false - inherited from global tabSetEnableClose
enableDeleteWhenEmpty?: boolean; // default: true - inherited from global tabSetEnableDeleteWhenEmpty
enableDivide?: boolean; // default: true - inherited from global tabSetEnableDivide
enableDrag?: boolean; // default: true - inherited from global tabSetEnableDrag
Expand Down
17 changes: 16 additions & 1 deletion src/model/Model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class Model {
attributeDefinitions.add("tabSetEnableDrag", true).setType(Attribute.BOOLEAN);
attributeDefinitions.add("tabSetEnableDivide", true).setType(Attribute.BOOLEAN);
attributeDefinitions.add("tabSetEnableMaximize", true).setType(Attribute.BOOLEAN);
attributeDefinitions.add("tabSetEnableClose", false).setType(Attribute.BOOLEAN);
attributeDefinitions.add("tabSetAutoSelectTab", true).setType(Attribute.BOOLEAN);
attributeDefinitions.add("tabSetClassNameTabStrip", undefined).setType(Attribute.STRING);
attributeDefinitions.add("tabSetClassNameHeader", undefined).setType(Attribute.STRING);
Expand Down Expand Up @@ -253,11 +254,25 @@ class Model {
case Actions.DELETE_TAB: {
const node = this._idMap[action.data.node];
if (node instanceof TabNode) {
delete this._idMap[action.data.node];
node._delete();
}
break;
}
case Actions.DELETE_TABSET: {
const node = this._idMap[action.data.node];

if (node instanceof TabSetNode) {
// first delete all child tabs
const children = [...node.getChildren()];
children.forEach((child, i) => {
(child as TabNode)._delete();
});

node._delete();
this._tidy();
}
break;
}
case Actions.FLOAT_TAB: {
const node = this._idMap[action.data.node];
if (node instanceof TabNode) {
Expand Down
Loading

0 comments on commit e47e3e0

Please sign in to comment.