Skip to content

Commit

Permalink
Border autoHide attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
nealus committed Oct 15, 2021
1 parent bd10853 commit ff21e96
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 30 deletions.
1 change: 1 addition & 0 deletions ChangeLog.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
0.5.18
Added onRenderDragRect callback prop for rendering the drag rectangles
New border attribute: enableAutoHide, to hide border if it has zero tabs

0.5.17
New global option, splitterExtra, to allow splitters to have an extended hit test areas.
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ Attributes allowed in the 'global' element
| 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 |
Expand Down Expand Up @@ -456,6 +457,7 @@ Inherited defaults will take their value from the associated global attributes (
| 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* | |
Expand Down
19 changes: 12 additions & 7 deletions examples/demo/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,17 @@ class App extends React.Component<any, { layoutFile: string | null, model: FlexL
}

onRenderDragRect = (text: String, node?: Node, json?: IJsonTabNode) => {
if (node !== undefined) {
return <div>{JSON.stringify(node?.toJson())}</div>
} else if (json !== undefined) {
return <div>{JSON.stringify(json)}</div>
if (this.state.layoutFile === "newfeatures") {
return (
<div style={{ whiteSpace: "pre" }}>{text}
<br /><br />
This is a customized<br />
drag rectangle
</div>
);
} else {
return undefined; // use default rendering
}
return undefined;
}

onExternalDrag = (e: React.DragEvent) => {
Expand Down Expand Up @@ -348,7 +353,7 @@ class App extends React.Component<any, { layoutFile: string | null, model: FlexL
iconFactory={this.iconFactory}
onRenderTab={this.onRenderTab}
onRenderTabSet={this.onRenderTabSet}
// onRenderDragRect={this.onRenderDragRect}
onRenderDragRect={this.onRenderDragRect}
onExternalDrag={this.onExternalDrag}
realtimeResize={this.state.realtimeResize}
onTabDrag={(dragging, over, x, y, location, refresh) => {
Expand Down Expand Up @@ -635,7 +640,7 @@ function TabStorage({ tab, layout }: { tab: TabNode, layout: FlexLayout.Layout }

if (absY - rootY < tabRect.height / 5) {
setScrollDown(false)
} else if (absY - rootY > tabRect.height * 4/5) {
} else if (absY - rootY > tabRect.height * 4 / 5) {
setScrollDown(true)
} else {
setScrollDown(null)
Expand Down
1 change: 1 addition & 0 deletions examples/demo/layouts/newfeatures.layout
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"tabSetMinHeight":100,
"tabSetMinWidth":100,
"borderMinSize":100,
"borderEnableAutoHide": true,
"tabSetEnableClose":true,
"splitterSize":1,
"splitterExtra":4
Expand Down
21 changes: 20 additions & 1 deletion examples/demo/newfeatures.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,20 @@
<meta http-equiv='Content-type' content='text/html; charset=utf-8'>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400&display=swap" rel="stylesheet">
<link id="page-stylesheet" rel="stylesheet" href="gray.css" />
<style>
body {
overflow: auto;
}
ul {
margin-right: 1em;
}
li {
margin-bottom: 1em;
}
</style>
</head>

<body style="overflow:auto">
<body>

<ul>
<li>
Expand All @@ -30,6 +41,14 @@
Tab attributes: borderWidth, borderHeight to allow tabs to have individual sizes in borders:<br>
<small>Try the 'With border sizes' tab</small>
</li>
<li>
Customize the drag rectangle using the callback property: onRenderDragRect <br>
<small>In this layout all drag rectangles are custom rendered</small>
</li>
<li>
New border attribute: enableAutoHide, to hide border if it has zero tabs:<br>
<small>Try moving all tabs from any of the borders</small>
</li>
</ul>
</body>

Expand Down
8 changes: 6 additions & 2 deletions src/Rect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ class Rect {
}

static fromElement(element: Element) {
let {x, y, width, height} = element.getBoundingClientRect();
return new Rect(x, y, width, height);
let { x, y, width, height } = element.getBoundingClientRect();
return new Rect(x, y, width, height);
}

clone() {
Expand All @@ -42,6 +42,10 @@ class Rect {
return this.x + this.width;
}

getCenter() {
return { x: this.x + this.width / 2, y: this.y + this.height / 2 };
}

positionElement(element: HTMLElement, position?: string) {
this.styleWithPosition(element.style, position);
}
Expand Down
15 changes: 14 additions & 1 deletion src/model/BorderNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class BorderNode extends Node implements IDropTarget {
attributeDefinitions.addInherited("autoSelectTabWhenClosed", "borderAutoSelectTabWhenClosed").setType(Attribute.BOOLEAN);
attributeDefinitions.addInherited("size", "borderSize").setType(Attribute.NUMBER);
attributeDefinitions.addInherited("minSize", "borderMinSize").setType(Attribute.NUMBER);
attributeDefinitions.addInherited("enableAutoHide", "borderEnableAutoHide").setType(Attribute.BOOLEAN);
return attributeDefinitions;
}

Expand Down Expand Up @@ -178,7 +179,19 @@ class BorderNode extends Node implements IDropTarget {
}

isShowing() {
return this._attributes.show as boolean;
const show = this._attributes.show as boolean;
if (show) {
if (this._model._getShowHiddenBorder() !== this._location && this.isAutoHide() && this._children.length === 0) {
return false;
}
return true;
} else {
return false;
}
}

isAutoHide() {
return this._getAttr("enableAutoHide") as boolean;
}

/** @hidden @internal */
Expand Down
32 changes: 15 additions & 17 deletions src/model/BorderSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,23 +58,21 @@ class BorderSet {

// sum size of borders to see they will fit
for (const border of showingBorders) {
if (border.isShowing()) {
border._setAdjustedSize(border.getSize());
const visible = border.getSelected() !== -1;
if (border.getLocation().getOrientation() === Orientation.HORZ) {
sumWidth += border.getBorderBarSize() ;
if (visible) {
width -= this._model.getSplitterSize();
sumWidth += border.getSize();
adjustableWidth += border.getSize();
}
} else {
sumHeight += border.getBorderBarSize();
if (visible) {
height -= this._model.getSplitterSize();
sumHeight += border.getSize();
adjustableHeight += border.getSize();
}
border._setAdjustedSize(border.getSize());
const visible = border.getSelected() !== -1;
if (border.getLocation().getOrientation() === Orientation.HORZ) {
sumWidth += border.getBorderBarSize() ;
if (visible) {
width -= this._model.getSplitterSize();
sumWidth += border.getSize();
adjustableWidth += border.getSize();
}
} else {
sumHeight += border.getBorderBarSize();
if (visible) {
height -= this._model.getSplitterSize();
sumHeight += border.getSize();
adjustableHeight += border.getSize();
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/model/IJsonModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export interface IGlobalAttributes {
borderAutoSelectTabWhenOpen?: boolean; // default: true
borderBarSize?: number; // default: 0
borderClassName?: string;
borderEnableAutoHide?: boolean; // default: false
borderEnableDrop?: boolean; // default: true
borderMinSize?: number; // default: 0
borderSize?: number; // default: 200
Expand Down Expand Up @@ -131,6 +132,7 @@ export interface IBorderAttributes {
barSize?: number; // default: 0 - inherited from global borderBarSize
className?: string; // - inherited from global borderClassName
config?: any;
enableAutoHide?: boolean; // default: false - inherited from global borderEnableAutoHide
enableDrop?: boolean; // default: true - inherited from global borderEnableDrop
minSize?: number; // default: 0 - inherited from global borderMinSize
selected?: number; // default: -1
Expand Down
16 changes: 16 additions & 0 deletions src/model/Model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ class Model {
attributeDefinitions.add("borderAutoSelectTabWhenOpen", true).setType(Attribute.BOOLEAN);
attributeDefinitions.add("borderAutoSelectTabWhenClosed", false).setType(Attribute.BOOLEAN);
attributeDefinitions.add("borderClassName", undefined).setType(Attribute.STRING);
attributeDefinitions.add("borderEnableAutoHide", false).setType(Attribute.BOOLEAN);

return attributeDefinitions;
}

Expand All @@ -127,6 +129,9 @@ class Model {
private _pointerFine: boolean;
/** @hidden @internal */
private _onCreateTabSet? : (tabNode?: TabNode) => ITabSetAttributes;
/** @hidden @internal */
private _showHiddenBorder: DockLocation;


/**
* 'private' constructor. Use the static method Model.fromJson(json) to create a model
Expand All @@ -138,6 +143,7 @@ class Model {
this._idMap = {};
this._borders = new BorderSet(this);
this._pointerFine = true;
this._showHiddenBorder = DockLocation.CENTER;
}

/** @hidden @internal */
Expand All @@ -156,6 +162,16 @@ class Model {
}
}

/** @hidden @internal */
_getShowHiddenBorder() {
return this._showHiddenBorder;
}

/** @hidden @internal */
_setShowHiddenBorder(location: DockLocation) {
this._showHiddenBorder = location;
}

/** @hidden @internal */
_setActiveTabset(tabsetNode: TabSetNode | undefined) {
this._activeTabSet = tabsetNode;
Expand Down
47 changes: 45 additions & 2 deletions src/view/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export interface ILayoutState {
calculatedTabBarSize: number;
calculatedBorderBarSize: number;
editingTab?: TabNode;
showHiddenBorder: DockLocation;
}

export interface IIcons {
Expand Down Expand Up @@ -174,6 +175,7 @@ const defaultSupportsPopout: boolean = isDesktop && !isIEorEdge;
* A React component that hosts a multi-tabbed layout
*/
export class Layout extends React.Component<ILayoutProps, ILayoutState> {

/** @hidden @internal */
private selfRef: React.RefObject<HTMLDivElement>;
/** @hidden @internal */
Expand Down Expand Up @@ -264,6 +266,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
calculatedTabBarSize: 26,
calculatedBorderBarSize: 30,
editingTab: undefined,
showHiddenBorder: DockLocation.CENTER,
};

this.onDragEnter = this.onDragEnter.bind(this);
Expand Down Expand Up @@ -450,6 +453,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
tabBarSize: this.state.calculatedTabBarSize,
borderBarSize: this.state.calculatedBorderBarSize,
};
this.props.model._setShowHiddenBorder(this.state.showHiddenBorder);

this.centerRect = this.props.model._layout(this.state.rect, metrics);

Expand Down Expand Up @@ -750,6 +754,8 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
this.newTabJson = undefined;
this.customDrop = undefined;
}
this.setState({ showHiddenBorder: DockLocation.CENTER });

};

/** @hidden @internal */
Expand Down Expand Up @@ -778,8 +784,8 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {

/** @hidden @internal */
dragRectRender = (text: String, node?: Node, json?: IJsonTabNode, onRendered?: () => void) => {
let content: React.ReactElement | undefined = <div style={{whiteSpace: "pre"}}>{text.replace("<br>", "\n")}</div>;
let content: React.ReactElement | undefined = <div style={{ whiteSpace: "pre" }}>{text.replace("<br>", "\n")}</div>;

if (this.props.onRenderDragRect !== undefined) {
const customContent = this.props.onRenderDragRect(text, node, json);
if (customContent !== undefined) {
Expand Down Expand Up @@ -842,6 +848,8 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
y: event.clientY - clientRect.top,
};

this.checkForBorderToShow(pos.x, pos.y);

// keep it between left & right
const dragRect = this.dragDiv!.getBoundingClientRect();
let newLeft = pos.x - dragRect.width / 2;
Expand Down Expand Up @@ -950,6 +958,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
this.doAction(Actions.moveNode(this.dragNode.getId(), this.dropInfo.node.getId(), this.dropInfo.location, this.dropInfo.index));
}
}
this.setState({ showHiddenBorder: DockLocation.CENTER });
};

/** @hidden @internal */
Expand All @@ -967,6 +976,40 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
}
}


/** @hidden @internal */
checkForBorderToShow(x: number, y: number) {
const r = this.props.model._getOuterInnerRects().outer;
const c = r.getCenter();
const margin = this.edgeRectWidth;
const offset = this.edgeRectLength / 2;

let overEdge = false;
if (this.props.model.isEnableEdgeDock() && this.state.showHiddenBorder === DockLocation.CENTER) {
if ((y > c.y - offset && y < c.y + offset) ||
(x > c.x - offset && x < c.x + offset)) {
overEdge = true;
}
}

let location = DockLocation.CENTER;
if (!overEdge) {
if (x <= r.x + margin) {
location = DockLocation.LEFT;
} else if (x >= r.getRight() - margin) {
location = DockLocation.RIGHT;
} else if (y <= r.y + margin) {
location = DockLocation.TOP;
} else if (y >= r.getBottom() - margin) {
location = DockLocation.BOTTOM;
}
}

if (location !== this.state.showHiddenBorder) {
this.setState({ showHiddenBorder: location });
}
}

/** @hidden @internal */
showEdges(rootdiv: HTMLElement) {
if (this.props.model.isEnableEdgeDock()) {
Expand Down

0 comments on commit ff21e96

Please sign in to comment.