From 3196dedb5305028853374ec0f07663583cbdc650 Mon Sep 17 00:00:00 2001 From: nmd Date: Sat, 12 Jun 2021 14:30:21 +0100 Subject: [PATCH] Added StickyButtons to onRenderTabSet render values to allow for implementation of Chrome style + button --- ChangeLog.txt | 4 ++++ examples/demo/App.tsx | 42 ++++++++++++++++++++++++++--------- examples/demo/images/add.png | Bin 0 -> 1225 bytes package.json | 2 +- src/Types.ts | 1 + src/view/BorderTabSet.tsx | 6 +++-- src/view/Layout.tsx | 14 +++++++++--- src/view/TabOverflowHook.tsx | 20 ++++++++++++++--- src/view/TabSet.tsx | 35 ++++++++++++++++++++++++----- style/_base.scss | 5 +++++ style/dark.css | 4 ++++ style/gray.css | 4 ++++ style/light.css | 4 ++++ 13 files changed, 116 insertions(+), 25 deletions(-) create mode 100644 examples/demo/images/add.png diff --git a/ChangeLog.txt b/ChangeLog.txt index d911d940..a1f06124 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,7 @@ +0.5.11 +Added StickyButtons to onRenderTabSet render values to allow for implementation of Chrome style + button +Added example of + button to default layout in demo app + 0.5.10 Adjust selected tab when tabs popped out to an external window diff --git a/examples/demo/App.tsx b/examples/demo/App.tsx index 92964d4d..e53eaf6a 100755 --- a/examples/demo/App.tsx +++ b/examples/demo/App.tsx @@ -106,6 +106,14 @@ class App extends React.Component { + // if (this.state.model!.getMaximizedTabset() == null) { + (this.refs.layout as FlexLayout.Layout).addTabToTabSet(node.getId(), { + component: "grid", + name: "Grid " + this.nextGridIndex++ + }); + // } + } onAddIndirectClick = (event: React.MouseEvent) => { if (this.state.model!.getMaximizedTabset() == null) { @@ -235,7 +243,7 @@ class App extends React.Component(Added by titleFactory) {node.getName()}, - name: "the name for custom tab" + name: "the name for custom tab" }; } return; @@ -273,17 +281,29 @@ class App extends React.Component); - }; + onRenderTab = (node: TabNode, renderValues: any) => { + // renderValues.content += " *"; + // renderValues.name = "tab " + node.getId(); // name used in overflow menu + // renderValues.buttons.push(); + } - var onRenderTabSet = function (node: (TabSetNode | BorderNode), renderValues: any) { + onRenderTabSet = (node: (TabSetNode | BorderNode), renderValues: any) => { + if (this.state.layoutFile === "default") { //renderValues.headerContent = "-- " + renderValues.headerContent + " --"; //renderValues.buttons.push(); - }; + renderValues.stickyButtons.push( + Add this.onAddFromTabSetButton(node)} + />); + } + } + + render() { + let contents: React.ReactNode = "loading ..."; let maximized = false; @@ -297,8 +317,8 @@ class App extends React.Component { diff --git a/examples/demo/images/add.png b/examples/demo/images/add.png new file mode 100644 index 0000000000000000000000000000000000000000..935adee5cc749dfeed45c08a2e3295131cddb810 GIT binary patch literal 1225 zcmV;)1UCDLP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=HIvfL;Ph2L34mVhLL#Bwk{RoTHTKL_mg?Y=iN zNowY0sv63G1wP37WV*xn>-!AfP^jb>lA7m|b3{oc6|NX~JWJUv#kAY=<}pXF_Ha)y zOoCp{TdUXl3cG$haO)he_TbkH`z1uT@Gi1;UgQ4kB{3Y zu3T68c)eZLeRwe#qSO@|3G)z=xZ8AkAnPKj=rdZv8u%-Uu?PipERR=0Kwe(&(Z~24 zpqC&&M&Wbx@6o5=bG_{HDVF()5e7eEeLZcm$wJ!nZH9_bq$BhOFV-c(+Hhh#(Cvt7dOjBo`Hhq>^Z)z9SFZBDs<<3u2Ci@-X;7PIJ-5-)O#Su8llL>W%%ViP(oVnCP%u}-?#eUSStZbALG zxbd&Zg@x|FAQyn{E4QCe>+5%}ZDQwFxMCUwUthDzUITv5;u`$h{w?$tdJDaU-a>Do zx6uDyNbuL8gda_OoE7%rI+_3g0VHWdLr_UWLm+T+Z)Rz1WdHzpoSl%dO2beThQCy) zBGpAv5Qhw|1s5}jOH~MhLW~uxPHB>2F^M5bDZYVkq7TzS5PSn)K*8DnMh6G8c;WDW z9M1XwdoQ%?BuvZs9Z0jPaQ)uka&ToYywRq^3LESNVVUP>5XmIp;px8_u>(FoojVR-xZ4EO{J@d~u zegO(*P { const toolbarRef = React.useRef(null); const overflowbuttonRef = React.useRef(null); + const stickyButtonsRef = React.useRef(null); - const { selfRef, position, userControlledLeft, hiddenTabs, onMouseWheel } = useTabOverflow(border, Orientation.flip(border.getOrientation()), toolbarRef); + const { selfRef, position, userControlledLeft, hiddenTabs, onMouseWheel } = useTabOverflow(border, Orientation.flip(border.getOrientation()), toolbarRef, stickyButtonsRef); const onInterceptMouseDown = (event: React.MouseEvent | React.MouseEvent | React.TouchEvent) => { event.stopPropagation(); @@ -84,7 +85,8 @@ export const BorderTabSet = (props: IBorderTabSetProps) => { // allow customization of tabset right/bottom buttons let buttons: any[] = []; - const renderState = { headerContent: {}, buttons }; + let stickyButtons: any[] = []; + const renderState = { headerContent: {}, buttons, stickyButtons }; layout.customizeTabSet(border, renderState); buttons = renderState.buttons; diff --git a/src/view/Layout.tsx b/src/view/Layout.tsx index 352c370f..1cf8965b 100755 --- a/src/view/Layout.tsx +++ b/src/view/Layout.tsx @@ -52,6 +52,7 @@ export interface ILayoutProps { tabSetNode: TabSetNode | BorderNode, renderValues: { headerContent?: React.ReactNode; + stickyButtons: React.ReactNode[]; buttons: React.ReactNode[]; } ) => void; @@ -120,6 +121,7 @@ export interface ILayoutCallbacks { renderValues: { headerContent?: React.ReactNode; buttons: React.ReactNode[]; + stickyButtons: React.ReactNode[]; } ): void; styleFont: (style: Record) => Record; @@ -195,7 +197,10 @@ export class Layout extends React.Component { private popoutURL: string; /** @hidden @internal */ private icons?: IIcons; + /** @hidden @internal */ private firstRender: boolean; + /** @hidden @internal */ + private resizeObserver? : ResizeObserver; constructor(props: ILayoutProps) { super(props); @@ -264,12 +269,14 @@ export class Layout extends React.Component { // need to re-render if size changes this.currentDocument = (this.selfRef.current as HTMLDivElement).ownerDocument; this.currentWindow = this.currentDocument.defaultView!; - this.currentWindow!.addEventListener("resize", this.updateRect); + this.resizeObserver = new ResizeObserver(entries => { + this.updateRect(); + }); + this.resizeObserver.observe(this.selfRef.current!); } /** @hidden @internal */ componentDidUpdate() { - this.updateRect(); this.updateLayoutMetrics(); if (this.props.model !== this.previousModel) { if (this.previousModel !== undefined) { @@ -348,7 +355,7 @@ export class Layout extends React.Component { /** @hidden @internal */ componentWillUnmount() { - this.currentWindow!.removeEventListener("resize", this.updateRect); + this.resizeObserver?.unobserve(this.selfRef.current!) } /** @hidden @internal */ @@ -857,6 +864,7 @@ export class Layout extends React.Component { renderValues: { headerContent?: React.ReactNode; buttons: React.ReactNode[]; + stickyButtons: React.ReactNode[]; } ) { if (this.props.onRenderTabSet) { diff --git a/src/view/TabOverflowHook.tsx b/src/view/TabOverflowHook.tsx index 10c5deca..df1d9d2b 100755 --- a/src/view/TabOverflowHook.tsx +++ b/src/view/TabOverflowHook.tsx @@ -6,8 +6,14 @@ import BorderNode from "../model/BorderNode"; import Orientation from "../Orientation"; /** @hidden @internal */ -export const useTabOverflow = (node: TabSetNode | BorderNode, orientation: Orientation, toolbarRef: React.MutableRefObject) => { +export const useTabOverflow = ( + node: TabSetNode | BorderNode, + orientation: Orientation, + toolbarRef: React.MutableRefObject, + stickyButtonsRef: React.MutableRefObject + ) => { const firstRender = React.useRef(true); + const tabsTruncated = React.useRef(false); const lastRect = React.useRef(new Rect(0, 0, 0, 0)); const selfRef = React.useRef(null); @@ -63,8 +69,12 @@ export const useTabOverflow = (node: TabSetNode | BorderNode, orientation: Orien const updateVisibleTabs = () => { const tabMargin = 2; + if (firstRender.current === true ) { + tabsTruncated.current = false; + } const nodeRect = node instanceof TabSetNode ? node.getRect() : (node as BorderNode).getTabHeaderRect()!; let lastChild = node.getChildren()[node.getChildren().length - 1] as TabNode; + const stickyButtonsSize = stickyButtonsRef.current === null ? 0 : getSize(stickyButtonsRef.current!.getBoundingClientRect()); if ( firstRender.current === true || @@ -73,7 +83,7 @@ export const useTabOverflow = (node: TabSetNode | BorderNode, orientation: Orien ) { lastRect.current = nodeRect; const enabled = node instanceof TabSetNode ? node.isEnableTabStrip() === true : true; - let endPos = getFar(nodeRect) - getSize(toolbarRef.current!.getBoundingClientRect()); + let endPos = getFar(nodeRect) - getSize(toolbarRef.current!.getBoundingClientRect()) - stickyButtonsSize; if (enabled && node.getChildren().length > 0) { if (hiddenTabs.length === 0 && position === 0 && getFar(lastChild.getTabRect()!) + tabMargin < endPos) { return; // nothing to do all tabs are shown in available space @@ -118,6 +128,10 @@ export const useTabOverflow = (node: TabSetNode | BorderNode, orientation: Orien } } + if (hidden.length > 0) { + tabsTruncated.current = true; + } + firstRender.current = false; // need to do a second render setHiddenTabs(hidden); setPosition(newPosition); @@ -143,5 +157,5 @@ export const useTabOverflow = (node: TabSetNode | BorderNode, orientation: Orien event.stopPropagation(); }; - return { selfRef, position, userControlledLeft, hiddenTabs, onMouseWheel }; + return { selfRef, position, userControlledLeft, hiddenTabs, onMouseWheel, tabsTruncated: tabsTruncated.current }; }; diff --git a/src/view/TabSet.tsx b/src/view/TabSet.tsx index 676cc322..b42bd3b7 100755 --- a/src/view/TabSet.tsx +++ b/src/view/TabSet.tsx @@ -26,8 +26,9 @@ export const TabSet = (props: ITabSetProps) => { const toolbarRef = React.useRef(null); const overflowbuttonRef = React.useRef(null); const tabbarInnerRef = React.useRef(null); + const stickyButtonsRef = React.useRef(null); - const { selfRef, position, userControlledLeft, hiddenTabs, onMouseWheel } = useTabOverflow(node, Orientation.HORZ, toolbarRef); + const { selfRef, position, userControlledLeft, hiddenTabs, onMouseWheel, tabsTruncated } = useTabOverflow(node, Orientation.HORZ, toolbarRef, stickyButtonsRef); const onOverflowClick = () => { const element = overflowbuttonRef.current!; @@ -51,7 +52,7 @@ export const TabSet = (props: ITabSetProps) => { layout.dragStart(event, message, node, node.isEnableDrag(), (event2: Event) => undefined, onDoubleClick); }; - const onInterceptMouseDown = (event: React.MouseEvent | React.MouseEvent | React.TouchEvent) => { + const onInterceptMouseDown = (event: React.MouseEvent | React.TouchEvent) => { event.stopPropagation(); }; @@ -109,14 +110,33 @@ export const TabSet = (props: ITabSetProps) => { } } - let buttons: any[] = []; + let stickyButtons: React.ReactNode[] = []; + let buttons: React.ReactNode[] = []; // allow customization of header contents and buttons - const renderState = { headerContent: node.getName(), buttons }; + const renderState = { headerContent: node.getName(), stickyButtons, buttons }; layout.customizeTabSet(node, renderState); const headerContent = renderState.headerContent; + stickyButtons = renderState.stickyButtons; buttons = renderState.buttons; + if (stickyButtons.length > 0) { + if (tabsTruncated) { + buttons = [...stickyButtons, ...buttons]; + } else { + tabs.push(
{ e.preventDefault() }} + className={cm(CLASSES.FLEXLAYOUT__TAB_TOOLBAR_STICKY_BUTTONS_CONTAINER)} + > + {stickyButtons} +
); + } + } + let toolbar; if (hiddenTabs.length > 0) { const overflowTitle = layout.i18nName(I18nLabel.Overflow_Menu_Tooltip); @@ -169,7 +189,12 @@ export const TabSet = (props: ITabSetProps) => { } toolbar = ( -
+
{ e.preventDefault() }} + > {buttons}
); diff --git a/style/_base.scss b/style/_base.scss index c531cb82..bba84e01 100755 --- a/style/_base.scss +++ b/style/_base.scss @@ -271,6 +271,11 @@ background: transparent url("../images/popout.png") no-repeat center; } } + + &_sticky_buttons_container { + display: flex; + align-items: center; + } } &_floating { diff --git a/style/dark.css b/style/dark.css index 7bbc2cc7..420fd037 100644 --- a/style/dark.css +++ b/style/dark.css @@ -232,6 +232,10 @@ .flexlayout__tab_toolbar_button-float { background: transparent url("../images/popout.png") no-repeat center; } +.flexlayout__tab_toolbar_sticky_buttons_container { + display: flex; + align-items: center; +} .flexlayout__tab_floating { overflow: auto; position: absolute; diff --git a/style/gray.css b/style/gray.css index 2e129280..67d2e196 100644 --- a/style/gray.css +++ b/style/gray.css @@ -232,6 +232,10 @@ .flexlayout__tab_toolbar_button-float { background: transparent url("../images/popout.png") no-repeat center; } +.flexlayout__tab_toolbar_sticky_buttons_container { + display: flex; + align-items: center; +} .flexlayout__tab_floating { overflow: auto; position: absolute; diff --git a/style/light.css b/style/light.css index bdb2f14b..d96e7c72 100755 --- a/style/light.css +++ b/style/light.css @@ -220,6 +220,10 @@ .flexlayout__tab_toolbar_button-float { background: transparent url("../images/popout.png") no-repeat center; } +.flexlayout__tab_toolbar_sticky_buttons_container { + display: flex; + align-items: center; +} .flexlayout__tab_floating { overflow: auto; position: absolute;