flexlayout-react 0.7.11 → 0.7.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ChangeLog.txt +12 -0
- package/README.md +21 -90
- package/declarations/Rect.d.ts +1 -1
- package/declarations/Types.d.ts +1 -0
- package/declarations/model/Actions.d.ts +1 -1
- package/declarations/model/IJsonModel.d.ts +5 -1
- package/declarations/model/TabNode.d.ts +1 -0
- package/declarations/model/TabSetNode.d.ts +1 -0
- package/declarations/view/Layout.d.ts +4 -2
- package/dist/flexlayout.js +15 -15
- package/dist/flexlayout_min.js +1 -1
- package/lib/DragDrop.js +1 -1
- package/lib/DragDrop.js.map +1 -1
- package/lib/PopupMenu.js +9 -4
- package/lib/PopupMenu.js.map +1 -1
- package/lib/Rect.js +1 -6
- package/lib/Rect.js.map +1 -1
- package/lib/Types.js +1 -0
- package/lib/Types.js.map +1 -1
- package/lib/model/Actions.js.map +1 -1
- package/lib/model/Model.js +10 -3
- package/lib/model/Model.js.map +1 -1
- package/lib/model/TabNode.js +4 -0
- package/lib/model/TabNode.js.map +1 -1
- package/lib/model/TabSetNode.js +4 -0
- package/lib/model/TabSetNode.js.map +1 -1
- package/lib/view/BorderButton.js +7 -5
- package/lib/view/BorderButton.js.map +1 -1
- package/lib/view/FloatingWindow.js +3 -2
- package/lib/view/FloatingWindow.js.map +1 -1
- package/lib/view/Layout.js +117 -57
- package/lib/view/Layout.js.map +1 -1
- package/lib/view/Splitter.js +18 -6
- package/lib/view/Splitter.js.map +1 -1
- package/lib/view/Tab.js +3 -0
- package/lib/view/Tab.js.map +1 -1
- package/lib/view/TabButton.js +17 -12
- package/lib/view/TabButton.js.map +1 -1
- package/lib/view/TabOverflowHook.js +7 -4
- package/lib/view/TabOverflowHook.js.map +1 -1
- package/lib/view/TabSet.js +11 -5
- package/lib/view/TabSet.js.map +1 -1
- package/package.json +1 -1
- package/src/DragDrop.ts +2 -2
- package/src/PopupMenu.tsx +8 -4
- package/src/Rect.ts +2 -6
- package/src/Types.ts +1 -0
- package/src/model/Actions.ts +1 -1
- package/src/model/IJsonModel.ts +6 -2
- package/src/model/Model.ts +9 -3
- package/src/model/TabNode.ts +5 -0
- package/src/model/TabSetNode.ts +6 -0
- package/src/view/BorderButton.tsx +13 -5
- package/src/view/FloatingWindow.tsx +3 -3
- package/src/view/Layout.tsx +145 -69
- package/src/view/Splitter.tsx +36 -7
- package/src/view/Tab.tsx +4 -0
- package/src/view/TabButton.tsx +22 -11
- package/src/view/TabOverflowHook.tsx +8 -5
- package/src/view/TabSet.tsx +13 -5
- package/style/_base.scss +21 -0
- package/style/dark.css +17 -0
- package/style/dark.css.map +1 -1
- package/style/dark.scss +6 -0
- package/style/gray.css +17 -0
- package/style/gray.css.map +1 -1
- package/style/gray.scss +6 -0
- package/style/light.css +561 -544
- package/style/light.css.map +1 -1
- package/style/light.scss +6 -0
- package/style/underline.css +17 -0
- package/style/underline.css.map +1 -1
- package/style/underline.scss +6 -0
package/src/view/Layout.tsx
CHANGED
|
@@ -166,11 +166,11 @@ export interface ILayoutCallbacks {
|
|
|
166
166
|
getPopoutURL(): string;
|
|
167
167
|
isSupportsPopout(): boolean;
|
|
168
168
|
isRealtimeResize(): boolean;
|
|
169
|
-
getCurrentDocument():
|
|
169
|
+
getCurrentDocument(): Document | undefined;
|
|
170
170
|
getClassName(defaultClassName: string): string;
|
|
171
171
|
doAction(action: Action): Node | undefined;
|
|
172
|
-
getDomRect():
|
|
173
|
-
getRootDiv(): HTMLDivElement;
|
|
172
|
+
getDomRect(): DOMRect | undefined;
|
|
173
|
+
getRootDiv(): HTMLDivElement | null;
|
|
174
174
|
dragStart(
|
|
175
175
|
event: Event | React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement> | React.DragEvent<HTMLDivElement> | undefined,
|
|
176
176
|
dragDivText: string | undefined,
|
|
@@ -260,7 +260,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
260
260
|
/** @internal */
|
|
261
261
|
private fnNewNodeDropped?: (node?: Node, event?: Event) => void;
|
|
262
262
|
/** @internal */
|
|
263
|
-
private currentDocument?:
|
|
263
|
+
private currentDocument?: Document;
|
|
264
264
|
/** @internal */
|
|
265
265
|
private currentWindow?: Window;
|
|
266
266
|
/** @internal */
|
|
@@ -350,7 +350,10 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
350
350
|
this.resizeObserver = new ResizeObserver(entries => {
|
|
351
351
|
this.updateRect(entries[0].contentRect);
|
|
352
352
|
});
|
|
353
|
-
this.
|
|
353
|
+
const selfRefCurr = this.selfRef.current;
|
|
354
|
+
if (selfRefCurr) {
|
|
355
|
+
this.resizeObserver.observe(selfRefCurr);
|
|
356
|
+
}
|
|
354
357
|
}
|
|
355
358
|
|
|
356
359
|
/** @internal */
|
|
@@ -367,7 +370,14 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
367
370
|
}
|
|
368
371
|
|
|
369
372
|
/** @internal */
|
|
370
|
-
updateRect = (domRect
|
|
373
|
+
updateRect = (domRect?: DOMRectReadOnly) => {
|
|
374
|
+
if (!domRect) {
|
|
375
|
+
domRect = this.getDomRect();
|
|
376
|
+
}
|
|
377
|
+
if (!domRect) {
|
|
378
|
+
// no dom rect available, return.
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
371
381
|
const rect = new Rect(0, 0, domRect.width, domRect.height);
|
|
372
382
|
if (!rect.equals(this.state.rect) && rect.width !== 0 && rect.height !== 0) {
|
|
373
383
|
this.setState({ rect });
|
|
@@ -412,12 +422,12 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
412
422
|
|
|
413
423
|
/** @internal */
|
|
414
424
|
getDomRect() {
|
|
415
|
-
return this.selfRef.current
|
|
425
|
+
return this.selfRef.current?.getBoundingClientRect();
|
|
416
426
|
}
|
|
417
427
|
|
|
418
428
|
/** @internal */
|
|
419
429
|
getRootDiv() {
|
|
420
|
-
return this.selfRef.current
|
|
430
|
+
return this.selfRef.current;
|
|
421
431
|
}
|
|
422
432
|
|
|
423
433
|
/** @internal */
|
|
@@ -442,7 +452,10 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
442
452
|
|
|
443
453
|
/** @internal */
|
|
444
454
|
componentWillUnmount() {
|
|
445
|
-
this.
|
|
455
|
+
const selfRefCurr = this.selfRef.current;
|
|
456
|
+
if (selfRefCurr) {
|
|
457
|
+
this.resizeObserver?.unobserve(selfRefCurr);
|
|
458
|
+
}
|
|
446
459
|
}
|
|
447
460
|
|
|
448
461
|
/** @internal */
|
|
@@ -601,10 +614,12 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
601
614
|
|
|
602
615
|
const tabBorderWidth = child._getAttr("borderWidth");
|
|
603
616
|
const tabBorderHeight = child._getAttr("borderHeight");
|
|
604
|
-
if (
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
617
|
+
if (rect) {
|
|
618
|
+
if (tabBorderWidth !== -1 && border.getLocation().getOrientation() === Orientation.HORZ) {
|
|
619
|
+
rect.width = tabBorderWidth;
|
|
620
|
+
} else if (tabBorderHeight !== -1 && border.getLocation().getOrientation() === Orientation.VERT) {
|
|
621
|
+
rect.height = tabBorderHeight;
|
|
622
|
+
}
|
|
608
623
|
}
|
|
609
624
|
|
|
610
625
|
floatingWindows.push(
|
|
@@ -693,7 +708,11 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
693
708
|
/** @internal */
|
|
694
709
|
_getScreenRect(node: TabNode) {
|
|
695
710
|
const rect = node!.getRect()!.clone();
|
|
696
|
-
const bodyRect: DOMRect =
|
|
711
|
+
const bodyRect: DOMRect | undefined =
|
|
712
|
+
this.selfRef.current?.getBoundingClientRect();
|
|
713
|
+
if (!bodyRect) {
|
|
714
|
+
return null;
|
|
715
|
+
}
|
|
697
716
|
const navHeight = Math.min(80, this.currentWindow!.outerHeight - this.currentWindow!.innerHeight);
|
|
698
717
|
const navWidth = Math.min(80, this.currentWindow!.outerWidth - this.currentWindow!.innerWidth);
|
|
699
718
|
rect.x = rect.x + bodyRect.x + this.currentWindow!.screenX + navWidth;
|
|
@@ -705,23 +724,29 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
705
724
|
* Adds a new tab to the given tabset
|
|
706
725
|
* @param tabsetId the id of the tabset where the new tab will be added
|
|
707
726
|
* @param json the json for the new tab node
|
|
727
|
+
* @returns the added tab node or undefined
|
|
708
728
|
*/
|
|
709
|
-
addTabToTabSet(tabsetId: string, json: IJsonTabNode) {
|
|
729
|
+
addTabToTabSet(tabsetId: string, json: IJsonTabNode) : TabNode | undefined {
|
|
710
730
|
const tabsetNode = this.props.model.getNodeById(tabsetId);
|
|
711
731
|
if (tabsetNode !== undefined) {
|
|
712
|
-
this.doAction(Actions.addNode(json, tabsetId, DockLocation.CENTER, -1));
|
|
732
|
+
const node = this.doAction(Actions.addNode(json, tabsetId, DockLocation.CENTER, -1));
|
|
733
|
+
return node as TabNode;
|
|
713
734
|
}
|
|
735
|
+
return undefined;
|
|
714
736
|
}
|
|
715
737
|
|
|
716
738
|
/**
|
|
717
739
|
* Adds a new tab to the active tabset (if there is one)
|
|
718
740
|
* @param json the json for the new tab node
|
|
741
|
+
* @returns the added tab node or undefined
|
|
719
742
|
*/
|
|
720
|
-
addTabToActiveTabSet(json: IJsonTabNode) {
|
|
743
|
+
addTabToActiveTabSet(json: IJsonTabNode) : TabNode | undefined {
|
|
721
744
|
const tabsetNode = this.props.model.getActiveTabset();
|
|
722
745
|
if (tabsetNode !== undefined) {
|
|
723
|
-
this.doAction(Actions.addNode(json, tabsetNode.getId(), DockLocation.CENTER, -1));
|
|
746
|
+
const node = this.doAction(Actions.addNode(json, tabsetNode.getId(), DockLocation.CENTER, -1));
|
|
747
|
+
return node as TabNode;
|
|
724
748
|
}
|
|
749
|
+
return undefined;
|
|
725
750
|
}
|
|
726
751
|
|
|
727
752
|
/**
|
|
@@ -785,7 +810,9 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
785
810
|
/** @internal */
|
|
786
811
|
onCancelAdd = () => {
|
|
787
812
|
const rootdiv = this.selfRef.current;
|
|
788
|
-
rootdiv
|
|
813
|
+
if (rootdiv && this.dragDiv) {
|
|
814
|
+
rootdiv.removeChild(this.dragDiv);
|
|
815
|
+
}
|
|
789
816
|
this.dragDiv = undefined;
|
|
790
817
|
this.hidePortal();
|
|
791
818
|
if (this.fnNewNodeDropped != null) {
|
|
@@ -807,15 +834,21 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
807
834
|
/** @internal */
|
|
808
835
|
onCancelDrag = (wasDragging: boolean) => {
|
|
809
836
|
if (wasDragging) {
|
|
810
|
-
const rootdiv = this.selfRef.current
|
|
837
|
+
const rootdiv = this.selfRef.current;
|
|
811
838
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
839
|
+
const outlineDiv = this.outlineDiv;
|
|
840
|
+
if (rootdiv && outlineDiv) {
|
|
841
|
+
try {
|
|
842
|
+
rootdiv.removeChild(outlineDiv);
|
|
843
|
+
} catch (e) {}
|
|
844
|
+
}
|
|
815
845
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
846
|
+
const dragDiv = this.dragDiv;
|
|
847
|
+
if (rootdiv && dragDiv) {
|
|
848
|
+
try {
|
|
849
|
+
rootdiv.removeChild(dragDiv);
|
|
850
|
+
} catch (e) {}
|
|
851
|
+
}
|
|
819
852
|
|
|
820
853
|
this.dragDiv = undefined;
|
|
821
854
|
this.hidePortal();
|
|
@@ -855,11 +888,31 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
855
888
|
onDoubleClick?: (event: Event) => void
|
|
856
889
|
) => {
|
|
857
890
|
if (!allowDrag) {
|
|
858
|
-
DragDrop.instance.startDrag(
|
|
891
|
+
DragDrop.instance.startDrag(
|
|
892
|
+
event,
|
|
893
|
+
undefined,
|
|
894
|
+
undefined,
|
|
895
|
+
undefined,
|
|
896
|
+
undefined,
|
|
897
|
+
onClick,
|
|
898
|
+
onDoubleClick,
|
|
899
|
+
this.currentDocument,
|
|
900
|
+
this.selfRef.current ?? undefined
|
|
901
|
+
);
|
|
859
902
|
} else {
|
|
860
903
|
this.dragNode = node;
|
|
861
904
|
this.dragDivText = dragDivText;
|
|
862
|
-
DragDrop.instance.startDrag(
|
|
905
|
+
DragDrop.instance.startDrag(
|
|
906
|
+
event,
|
|
907
|
+
this.onDragStart,
|
|
908
|
+
this.onDragMove,
|
|
909
|
+
this.onDragEnd,
|
|
910
|
+
this.onCancelDrag,
|
|
911
|
+
onClick,
|
|
912
|
+
onDoubleClick,
|
|
913
|
+
this.currentDocument,
|
|
914
|
+
this.selfRef.current ?? undefined
|
|
915
|
+
);
|
|
863
916
|
}
|
|
864
917
|
};
|
|
865
918
|
|
|
@@ -888,18 +941,22 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
888
941
|
}
|
|
889
942
|
|
|
890
943
|
// hide div until the render is complete
|
|
891
|
-
this.dragDiv!.style.visibility = "hidden";
|
|
892
944
|
this.dragRectRendered = false;
|
|
893
|
-
this.
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
945
|
+
const dragDiv = this.dragDiv;
|
|
946
|
+
if (dragDiv) {
|
|
947
|
+
dragDiv.style.visibility = "hidden";
|
|
948
|
+
this.showPortal(
|
|
949
|
+
<DragRectRenderWrapper
|
|
950
|
+
// wait for it to be rendered
|
|
951
|
+
onRendered={() => {
|
|
952
|
+
this.dragRectRendered = true;
|
|
953
|
+
onRendered?.();
|
|
954
|
+
}}>
|
|
955
|
+
{content}
|
|
956
|
+
</DragRectRenderWrapper>,
|
|
957
|
+
dragDiv,
|
|
958
|
+
);
|
|
959
|
+
}
|
|
903
960
|
};
|
|
904
961
|
|
|
905
962
|
/** @internal */
|
|
@@ -917,11 +974,13 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
917
974
|
onDragStart = () => {
|
|
918
975
|
this.dropInfo = undefined;
|
|
919
976
|
this.customDrop = undefined;
|
|
920
|
-
const rootdiv = this.selfRef.current
|
|
977
|
+
const rootdiv = this.selfRef.current;
|
|
921
978
|
this.outlineDiv = this.currentDocument!.createElement("div");
|
|
922
979
|
this.outlineDiv.className = this.getClassName(CLASSES.FLEXLAYOUT__OUTLINE_RECT);
|
|
923
980
|
this.outlineDiv.style.visibility = "hidden";
|
|
924
|
-
rootdiv
|
|
981
|
+
if (rootdiv) {
|
|
982
|
+
rootdiv.appendChild(this.outlineDiv);
|
|
983
|
+
}
|
|
925
984
|
|
|
926
985
|
if (this.dragDiv == null) {
|
|
927
986
|
this.dragDiv = this.currentDocument!.createElement("div");
|
|
@@ -929,15 +988,17 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
929
988
|
this.dragDiv.setAttribute("data-layout-path", "/drag-rectangle");
|
|
930
989
|
this.dragRectRender(this.dragDivText, this.dragNode, this.newTabJson);
|
|
931
990
|
|
|
932
|
-
rootdiv
|
|
991
|
+
if (rootdiv) {
|
|
992
|
+
rootdiv.appendChild(this.dragDiv);
|
|
993
|
+
}
|
|
933
994
|
}
|
|
934
995
|
// add edge indicators
|
|
935
996
|
if (this.props.model.getMaximizedTabset() === undefined) {
|
|
936
997
|
this.setState({ showEdges: this.props.model.isEnableEdgeDock() });
|
|
937
998
|
}
|
|
938
999
|
|
|
939
|
-
if (this.dragNode
|
|
940
|
-
this.dragNode.getTabRect()
|
|
1000
|
+
if (this.dragNode && this.outlineDiv && this.dragNode instanceof TabNode && this.dragNode.getTabRect() !== undefined) {
|
|
1001
|
+
this.dragNode.getTabRect()?.positionElement(this.outlineDiv);
|
|
941
1002
|
}
|
|
942
1003
|
this.firstMove = true;
|
|
943
1004
|
|
|
@@ -948,30 +1009,34 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
948
1009
|
onDragMove = (event: React.MouseEvent<Element>) => {
|
|
949
1010
|
if (this.firstMove === false) {
|
|
950
1011
|
const speed = this.props.model._getAttribute("tabDragSpeed") as number;
|
|
951
|
-
this.outlineDiv
|
|
1012
|
+
if (this.outlineDiv) {
|
|
1013
|
+
this.outlineDiv.style.transition = `top ${speed}s, left ${speed}s, width ${speed}s, height ${speed}s`;
|
|
1014
|
+
}
|
|
952
1015
|
}
|
|
953
1016
|
this.firstMove = false;
|
|
954
|
-
const clientRect = this.selfRef.current
|
|
1017
|
+
const clientRect = this.selfRef.current?.getBoundingClientRect();
|
|
955
1018
|
const pos = {
|
|
956
|
-
x: event.clientX - clientRect
|
|
957
|
-
y: event.clientY - clientRect
|
|
1019
|
+
x: event.clientX - (clientRect?.left ?? 0),
|
|
1020
|
+
y: event.clientY - (clientRect?.top ?? 0),
|
|
958
1021
|
};
|
|
959
1022
|
|
|
960
1023
|
this.checkForBorderToShow(pos.x, pos.y);
|
|
961
1024
|
|
|
962
1025
|
// keep it between left & right
|
|
963
|
-
const dragRect = this.dragDiv
|
|
1026
|
+
const dragRect = this.dragDiv?.getBoundingClientRect() ?? new DOMRect(0, 0, 100, 100);
|
|
964
1027
|
let newLeft = pos.x - dragRect.width / 2;
|
|
965
|
-
if (newLeft + dragRect.width > clientRect
|
|
966
|
-
newLeft = clientRect
|
|
1028
|
+
if (newLeft + dragRect.width > (clientRect?.width ?? 0)) {
|
|
1029
|
+
newLeft = (clientRect?.width ?? 0) - dragRect.width;
|
|
967
1030
|
}
|
|
968
1031
|
newLeft = Math.max(0, newLeft);
|
|
969
1032
|
|
|
970
|
-
this.dragDiv
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1033
|
+
if (this.dragDiv) {
|
|
1034
|
+
this.dragDiv.style.left = newLeft + "px";
|
|
1035
|
+
this.dragDiv.style.top = pos.y + 5 + "px";
|
|
1036
|
+
if (this.dragRectRendered && this.dragDiv.style.visibility === "hidden") {
|
|
1037
|
+
// make visible once the drag rect has been rendered
|
|
1038
|
+
this.dragDiv.style.visibility = "visible";
|
|
1039
|
+
}
|
|
975
1040
|
}
|
|
976
1041
|
|
|
977
1042
|
let dropInfo = this.props.model._findDropTargetNode(this.dragNode!, pos.x, pos.y);
|
|
@@ -980,18 +1045,26 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
980
1045
|
this.handleCustomTabDrag(dropInfo, pos, event);
|
|
981
1046
|
} else {
|
|
982
1047
|
this.dropInfo = dropInfo;
|
|
983
|
-
this.outlineDiv
|
|
984
|
-
|
|
985
|
-
|
|
1048
|
+
if (this.outlineDiv) {
|
|
1049
|
+
this.outlineDiv.className = this.getClassName(dropInfo.className);
|
|
1050
|
+
dropInfo.rect.positionElement(this.outlineDiv);
|
|
1051
|
+
this.outlineDiv.style.visibility = "visible";
|
|
1052
|
+
}
|
|
986
1053
|
}
|
|
987
1054
|
}
|
|
988
1055
|
};
|
|
989
1056
|
|
|
990
1057
|
/** @internal */
|
|
991
1058
|
onDragEnd = (event: Event) => {
|
|
992
|
-
const rootdiv = this.selfRef.current
|
|
993
|
-
rootdiv
|
|
994
|
-
|
|
1059
|
+
const rootdiv = this.selfRef.current;
|
|
1060
|
+
if (rootdiv) {
|
|
1061
|
+
if (this.outlineDiv) {
|
|
1062
|
+
rootdiv.removeChild(this.outlineDiv);
|
|
1063
|
+
}
|
|
1064
|
+
if (this.dragDiv) {
|
|
1065
|
+
rootdiv.removeChild(this.dragDiv);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
995
1068
|
this.dragDiv = undefined;
|
|
996
1069
|
this.hidePortal();
|
|
997
1070
|
|
|
@@ -1070,16 +1143,19 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
1070
1143
|
}
|
|
1071
1144
|
|
|
1072
1145
|
this.dropInfo = dropInfo;
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1146
|
+
if (this.outlineDiv) {
|
|
1147
|
+
this.outlineDiv.className = this.getClassName(this.customDrop ? CLASSES.FLEXLAYOUT__OUTLINE_RECT : dropInfo.className);
|
|
1148
|
+
if (this.customDrop) {
|
|
1149
|
+
this.customDrop.rect.positionElement(this.outlineDiv);
|
|
1150
|
+
} else {
|
|
1151
|
+
dropInfo.rect.positionElement(this.outlineDiv);
|
|
1152
|
+
}
|
|
1079
1153
|
}
|
|
1080
1154
|
|
|
1081
1155
|
DragDrop.instance.setGlassCursorOverride(this.customDrop?.cursor);
|
|
1082
|
-
this.outlineDiv
|
|
1156
|
+
if (this.outlineDiv) {
|
|
1157
|
+
this.outlineDiv.style.visibility = "visible";
|
|
1158
|
+
}
|
|
1083
1159
|
|
|
1084
1160
|
try {
|
|
1085
1161
|
invalidated?.();
|
package/src/view/Splitter.tsx
CHANGED
|
@@ -24,9 +24,28 @@ export const Splitter = (props: ISplitterProps) => {
|
|
|
24
24
|
const outlineDiv = React.useRef<HTMLDivElement | undefined>(undefined);
|
|
25
25
|
const parentNode = node.getParent() as RowNode | BorderNode;
|
|
26
26
|
|
|
27
|
-
const onMouseDown = (
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
const onMouseDown = (
|
|
28
|
+
event:
|
|
29
|
+
| Event
|
|
30
|
+
| React.MouseEvent<HTMLDivElement, MouseEvent>
|
|
31
|
+
| React.TouchEvent<HTMLDivElement>
|
|
32
|
+
) => {
|
|
33
|
+
DragDrop.instance.setGlassCursorOverride(
|
|
34
|
+
node.getOrientation() === Orientation.HORZ
|
|
35
|
+
? "ns-resize"
|
|
36
|
+
: "ew-resize"
|
|
37
|
+
);
|
|
38
|
+
DragDrop.instance.startDrag(
|
|
39
|
+
event,
|
|
40
|
+
onDragStart,
|
|
41
|
+
onDragMove,
|
|
42
|
+
onDragEnd,
|
|
43
|
+
onDragCancel,
|
|
44
|
+
undefined,
|
|
45
|
+
undefined,
|
|
46
|
+
layout.getCurrentDocument(),
|
|
47
|
+
layout.getRootDiv() ?? undefined
|
|
48
|
+
);
|
|
30
49
|
pBounds.current = parentNode._getSplitterBounds(node, true);
|
|
31
50
|
const rootdiv = layout.getRootDiv();
|
|
32
51
|
outlineDiv.current = layout.getCurrentDocument()!.createElement("div");
|
|
@@ -41,12 +60,16 @@ export const Splitter = (props: ISplitterProps) => {
|
|
|
41
60
|
}
|
|
42
61
|
|
|
43
62
|
r.positionElement(outlineDiv.current);
|
|
44
|
-
rootdiv
|
|
63
|
+
if (rootdiv) {
|
|
64
|
+
rootdiv.appendChild(outlineDiv.current);
|
|
65
|
+
}
|
|
45
66
|
};
|
|
46
67
|
|
|
47
|
-
const onDragCancel = (
|
|
68
|
+
const onDragCancel = (_wasDragging: boolean) => {
|
|
48
69
|
const rootdiv = layout.getRootDiv();
|
|
49
|
-
rootdiv
|
|
70
|
+
if (rootdiv) {
|
|
71
|
+
rootdiv.removeChild(outlineDiv.current as Element);
|
|
72
|
+
}
|
|
50
73
|
};
|
|
51
74
|
|
|
52
75
|
const onDragStart = () => {
|
|
@@ -55,6 +78,10 @@ export const Splitter = (props: ISplitterProps) => {
|
|
|
55
78
|
|
|
56
79
|
const onDragMove = (event: React.MouseEvent<Element, MouseEvent>) => {
|
|
57
80
|
const clientRect = layout.getDomRect();
|
|
81
|
+
if (!clientRect) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
58
85
|
const pos = {
|
|
59
86
|
x: event.clientX - clientRect.left,
|
|
60
87
|
y: event.clientY - clientRect.top,
|
|
@@ -98,7 +125,9 @@ export const Splitter = (props: ISplitterProps) => {
|
|
|
98
125
|
updateLayout();
|
|
99
126
|
|
|
100
127
|
const rootdiv = layout.getRootDiv();
|
|
101
|
-
rootdiv
|
|
128
|
+
if (rootdiv) {
|
|
129
|
+
rootdiv.removeChild(outlineDiv.current as HTMLDivElement);
|
|
130
|
+
}
|
|
102
131
|
};
|
|
103
132
|
|
|
104
133
|
const getBoundPosition = (p: number) => {
|
package/src/view/Tab.tsx
CHANGED
|
@@ -66,6 +66,10 @@ export const Tab = (props: ITabProps) => {
|
|
|
66
66
|
className += " " + cm(CLASSES.FLEXLAYOUT__TAB_BORDER);
|
|
67
67
|
className += " " + cm(CLASSES.FLEXLAYOUT__TAB_BORDER_ + parentNode.getLocation().getName());
|
|
68
68
|
}
|
|
69
|
+
|
|
70
|
+
if (node.getContentClassName() !== undefined) {
|
|
71
|
+
className += " " + node.getContentClassName();
|
|
72
|
+
}
|
|
69
73
|
|
|
70
74
|
return (
|
|
71
75
|
<div
|
package/src/view/TabButton.tsx
CHANGED
|
@@ -103,8 +103,17 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
103
103
|
const updateRect = () => {
|
|
104
104
|
// record position of tab in node
|
|
105
105
|
const layoutRect = layout.getDomRect();
|
|
106
|
-
const r = selfRef.current
|
|
107
|
-
|
|
106
|
+
const r = selfRef.current?.getBoundingClientRect();
|
|
107
|
+
if (r && layoutRect) {
|
|
108
|
+
node._setTabRect(
|
|
109
|
+
new Rect(
|
|
110
|
+
r.left - layoutRect.left,
|
|
111
|
+
r.top - layoutRect.top,
|
|
112
|
+
r.width,
|
|
113
|
+
r.height
|
|
114
|
+
)
|
|
115
|
+
);
|
|
116
|
+
}
|
|
108
117
|
};
|
|
109
118
|
|
|
110
119
|
const onTextBoxMouseDown = (event: React.MouseEvent<HTMLInputElement> | React.TouchEvent<HTMLInputElement>) => {
|
|
@@ -113,11 +122,10 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
113
122
|
};
|
|
114
123
|
|
|
115
124
|
const onTextBoxKeyPress = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
116
|
-
|
|
117
|
-
if (event.keyCode === 27) {
|
|
125
|
+
if (event.code === 'Escape') {
|
|
118
126
|
// esc
|
|
119
127
|
layout.setEditingTab(undefined);
|
|
120
|
-
} else if (event.
|
|
128
|
+
} else if (event.code === 'Enter') {
|
|
121
129
|
// enter
|
|
122
130
|
layout.setEditingTab(undefined);
|
|
123
131
|
layout.doAction(Actions.renameTab(node.getId(), (event.target as HTMLInputElement).value));
|
|
@@ -127,14 +135,17 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
127
135
|
const cm = layout.getClassName;
|
|
128
136
|
const parentNode = node.getParent() as TabSetNode;
|
|
129
137
|
|
|
130
|
-
|
|
138
|
+
const isStretch = parentNode.isEnableSingleTabStretch() && parentNode.getChildren().length === 1;
|
|
139
|
+
let baseClassName = isStretch ? CLASSES.FLEXLAYOUT__TAB_BUTTON_STRETCH : CLASSES.FLEXLAYOUT__TAB_BUTTON;
|
|
131
140
|
let classNames = cm(baseClassName);
|
|
132
141
|
classNames += " " + cm(baseClassName + "_" + parentNode.getTabLocation());
|
|
133
142
|
|
|
134
|
-
if (
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
143
|
+
if (!isStretch) {
|
|
144
|
+
if (selected) {
|
|
145
|
+
classNames += " " + cm(baseClassName + "--selected");
|
|
146
|
+
} else {
|
|
147
|
+
classNames += " " + cm(baseClassName + "--unselected");
|
|
148
|
+
}
|
|
138
149
|
}
|
|
139
150
|
|
|
140
151
|
if (node.getClassName() !== undefined) {
|
|
@@ -169,7 +180,7 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
169
180
|
);
|
|
170
181
|
}
|
|
171
182
|
|
|
172
|
-
if (node.isEnableClose()) {
|
|
183
|
+
if (node.isEnableClose() && !isStretch) {
|
|
173
184
|
const closeTitle = layout.i18nName(I18nLabel.Close_Tab);
|
|
174
185
|
renderState.buttons.push(
|
|
175
186
|
<div
|
|
@@ -31,13 +31,16 @@ export const useTabOverflow = (
|
|
|
31
31
|
updateVisibleTabs();
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
+
const instance = selfRef.current;
|
|
34
35
|
React.useEffect(() => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return () => {
|
|
38
|
-
instance.removeEventListener('wheel', onWheel);
|
|
36
|
+
if (!instance) {
|
|
37
|
+
return;
|
|
39
38
|
}
|
|
40
|
-
|
|
39
|
+
instance.addEventListener("wheel", onWheel, { passive: false });
|
|
40
|
+
return () => {
|
|
41
|
+
instance.removeEventListener("wheel", onWheel);
|
|
42
|
+
};
|
|
43
|
+
}, [instance]);
|
|
41
44
|
|
|
42
45
|
// needed to prevent default mouse wheel over tabset/border (cannot do with react event?)
|
|
43
46
|
const onWheel = (event: Event) => {
|
package/src/view/TabSet.tsx
CHANGED
|
@@ -102,6 +102,11 @@ export const TabSet = (props: ITabSetProps) => {
|
|
|
102
102
|
event.stopPropagation();
|
|
103
103
|
};
|
|
104
104
|
|
|
105
|
+
const onCloseTab = (event: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
|
106
|
+
layout.doAction(Actions.deleteTab(node.getChildren()[0].getId()));
|
|
107
|
+
event.stopPropagation();
|
|
108
|
+
};
|
|
109
|
+
|
|
105
110
|
const onFloatTab = (event: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
|
106
111
|
if (selectedTabNode !== undefined) {
|
|
107
112
|
layout.doAction(Actions.floatTab(selectedTabNode.getId()));
|
|
@@ -167,12 +172,15 @@ export const TabSet = (props: ITabSetProps) => {
|
|
|
167
172
|
buttons = renderState.buttons;
|
|
168
173
|
headerButtons = renderState.headerButtons;
|
|
169
174
|
|
|
175
|
+
const isTabStretch = node.isEnableSingleTabStretch() && node.getChildren().length === 1;
|
|
176
|
+
const showClose = (isTabStretch && ((node.getChildren()[0] as TabNode).isEnableClose())) || node.isEnableClose();
|
|
177
|
+
|
|
170
178
|
if (renderState.overflowPosition === undefined) {
|
|
171
179
|
renderState.overflowPosition = stickyButtons.length;
|
|
172
180
|
}
|
|
173
181
|
|
|
174
182
|
if (stickyButtons.length > 0) {
|
|
175
|
-
if (tabsTruncated) {
|
|
183
|
+
if (tabsTruncated || isTabStretch) {
|
|
176
184
|
buttons = [...stickyButtons, ...buttons];
|
|
177
185
|
} else {
|
|
178
186
|
tabs.push(<div
|
|
@@ -253,8 +261,8 @@ export const TabSet = (props: ITabSetProps) => {
|
|
|
253
261
|
);
|
|
254
262
|
}
|
|
255
263
|
|
|
256
|
-
if (!node.isMaximized() &&
|
|
257
|
-
const title = layout.i18nName(I18nLabel.Close_Tabset);
|
|
264
|
+
if (!node.isMaximized() && showClose) {
|
|
265
|
+
const title = isTabStretch ? layout.i18nName(I18nLabel.Close_Tab) : layout.i18nName(I18nLabel.Close_Tabset);
|
|
258
266
|
const btns = showHeader ? headerButtons : buttons;
|
|
259
267
|
btns.push(
|
|
260
268
|
<button
|
|
@@ -262,7 +270,7 @@ export const TabSet = (props: ITabSetProps) => {
|
|
|
262
270
|
data-layout-path={path + "/button/close"}
|
|
263
271
|
title={title}
|
|
264
272
|
className={cm(CLASSES.FLEXLAYOUT__TAB_TOOLBAR_BUTTON) + " " + cm(CLASSES.FLEXLAYOUT__TAB_TOOLBAR_BUTTON_CLOSE)}
|
|
265
|
-
onClick={onClose}
|
|
273
|
+
onClick={isTabStretch ? onCloseTab : onClose}
|
|
266
274
|
onMouseDown={onInterceptMouseDown}
|
|
267
275
|
onTouchStart={onInterceptMouseDown}
|
|
268
276
|
>
|
|
@@ -348,7 +356,7 @@ export const TabSet = (props: ITabSetProps) => {
|
|
|
348
356
|
onTouchStart={onMouseDown}>
|
|
349
357
|
<div ref={tabbarInnerRef} className={cm(CLASSES.FLEXLAYOUT__TABSET_TABBAR_INNER) + " " + cm(CLASSES.FLEXLAYOUT__TABSET_TABBAR_INNER_ + node.getTabLocation())}>
|
|
350
358
|
<div
|
|
351
|
-
style={{ left: position }}
|
|
359
|
+
style={{ left: position, width: (isTabStretch? "100%": "10000px")}}
|
|
352
360
|
className={cm(CLASSES.FLEXLAYOUT__TABSET_TABBAR_INNER_TAB_CONTAINER) + " " + cm(CLASSES.FLEXLAYOUT__TABSET_TABBAR_INNER_TAB_CONTAINER_ + node.getTabLocation())}
|
|
353
361
|
>
|
|
354
362
|
{tabs}
|
package/style/_base.scss
CHANGED
|
@@ -208,6 +208,27 @@
|
|
|
208
208
|
cursor: pointer;
|
|
209
209
|
@include tab_button_mixin;
|
|
210
210
|
|
|
211
|
+
&_stretch {
|
|
212
|
+
background-color: transparent;
|
|
213
|
+
color:var(--color-tab-selected);
|
|
214
|
+
width: 100%;
|
|
215
|
+
padding: 3px 0em;
|
|
216
|
+
text-wrap: nowrap;
|
|
217
|
+
display: flex;
|
|
218
|
+
gap: 0.3em;
|
|
219
|
+
align-items: center;
|
|
220
|
+
box-sizing: border-box;
|
|
221
|
+
cursor: pointer;
|
|
222
|
+
@include tab_button_stretch_mixin;
|
|
223
|
+
|
|
224
|
+
@media (hover: hover) {
|
|
225
|
+
&:hover {
|
|
226
|
+
color:var(--color-tab-selected);
|
|
227
|
+
@include tab_button_stretch_hovered_mixin;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
211
232
|
&--selected {
|
|
212
233
|
background-color:var(--color-tab-selected-background);
|
|
213
234
|
color:var(--color-tab-selected);
|