diff --git a/src/renderer/components/dock/__test__/dock-tabs.test.tsx b/src/renderer/components/dock/__test__/dock-tabs.test.tsx index 705680317f..47e9689fa3 100644 --- a/src/renderer/components/dock/__test__/dock-tabs.test.tsx +++ b/src/renderer/components/dock/__test__/dock-tabs.test.tsx @@ -49,6 +49,7 @@ const getComponent = (dockStore: DockStore) => ( selectedTab={dockStore.selectedTab} autoFocus={true} onChangeTab={noop} + dockStore={dockStore} /> ); diff --git a/src/renderer/components/dock/dock-store/dock.store.ts b/src/renderer/components/dock/dock-store/dock.store.ts index 6cca0e4d06..6997286042 100644 --- a/src/renderer/components/dock/dock-store/dock.store.ts +++ b/src/renderer/components/dock/dock-store/dock.store.ts @@ -97,6 +97,11 @@ export interface DockTabCloseEvent { tabId: TabId; // closed tab id } +export interface DockTabsNumberEvent { + currentTabsNumber: number; // number of open tabs + previousTabsNumber: number; +} + interface Dependencies { storage: StorageHelper } @@ -160,6 +165,10 @@ export class DockStore implements DockStorageState { this.dependencies.storage.merge({ selectedTabId: tabId }); } + @computed get tabsNumber() : number { + return this.tabs.length; + } + @computed get selectedTab() { return this.tabs.find(tab => tab.id === this.selectedTabId); } @@ -224,6 +233,15 @@ export class DockStore implements DockStorageState { }), reactionOpts); } + onTabsNumberChange(callback: (evt: DockTabsNumberEvent) => void) { + return reaction(() => this.tabsNumber, ((currentTabsNumber, previousTabsNumber) => { + callback({ + currentTabsNumber, + previousTabsNumber, + }); + })); + } + hasTabs() { return this.tabs.length > 0; } diff --git a/src/renderer/components/dock/dock-tabs.tsx b/src/renderer/components/dock/dock-tabs.tsx index 1b90766489..482f114e8c 100644 --- a/src/renderer/components/dock/dock-tabs.tsx +++ b/src/renderer/components/dock/dock-tabs.tsx @@ -8,17 +8,18 @@ import { Icon } from "../icon"; import { Tabs } from "../tabs/tabs"; import { DockTab } from "./dock-tab"; import type { DockTab as DockTabModel } from "./dock-store/dock.store"; -import { TabKind } from "./dock-store/dock.store"; +import { TabKind, DockStore } from "./dock-store/dock.store"; import { TerminalTab } from "./terminal-tab"; interface Props { tabs: DockTabModel[] + dockStore: DockStore; autoFocus: boolean selectedTab: DockTabModel onChangeTab: (tab: DockTabModel) => void } -export const DockTabs = ({ tabs, autoFocus, selectedTab, onChangeTab }: Props) => { +export const DockTabs = ({ tabs, autoFocus, selectedTab, onChangeTab, dockStore }: Props) => { const elem = useRef(null); const contentElem = useRef(null); const [contentWidth, setContentWidth] = useState(0); @@ -27,7 +28,7 @@ export const DockTabs = ({ tabs, autoFocus, selectedTab, onChangeTab }: Props) = const scrollStep = 200; const scrollToRight = (): void => { - if(!elem || scrollPosition + scrollStep > contentWidth) return; + if(!elem || scrollPosition === contentWidth) return; const scroll = scrollPosition + scrollStep; setScrollPosition(scroll); @@ -35,6 +36,14 @@ export const DockTabs = ({ tabs, autoFocus, selectedTab, onChangeTab }: Props) = elem.current.scrollLeft = scroll; }; + const updateStateValues = () => { + if(!elem || !contentElem) return; + + setContentWidth(contentElem.current.clientWidth); + setContainerWidth(elem.current.clientWidth); + setScrollPosition(elem.current.scrollLeft); + }; + const scrollToLeft = (): void => { if(!elem) return; const scroll = scrollPosition - scrollStep; @@ -47,13 +56,15 @@ export const DockTabs = ({ tabs, autoFocus, selectedTab, onChangeTab }: Props) = const isScrollableRight = (): boolean => { if(!elem || !contentElem) return false; - return contentWidth > containerWidth && scrollPosition < contentWidth - containerWidth; + // check if element with tabs is wider than the parent element + // check if scroll at the end of scrollable area. + return contentElem.current?.clientWidth > containerWidth && scrollPosition < contentElem.current?.clientWidth - elem.current?.clientWidth; }; const isScrollableLeft = (): boolean => { if(!elem || !contentElem) return false; - return scrollPosition > scrollStep; + return scrollPosition > 0; }; const updateScrollPosition = ( evt: UIEvent): void => { @@ -65,11 +76,7 @@ export const DockTabs = ({ tabs, autoFocus, selectedTab, onChangeTab }: Props) = }; const onWindowResize = (): void => { - if(!elem || !contentElem) return; - - setContentWidth(contentElem.current.clientWidth); - setContainerWidth(elem.current.clientWidth); - setScrollPosition(elem.current.scrollLeft); + updateStateValues(); }; const renderTab = (tab?: DockTabModel) => { @@ -93,17 +100,18 @@ export const DockTabs = ({ tabs, autoFocus, selectedTab, onChangeTab }: Props) = useEffect(() => { - if(!elem || !contentElem) return; - - setContentWidth(contentElem.current.clientWidth); - setContainerWidth(elem.current.clientWidth); - setScrollPosition(elem.current.scrollLeft); + updateStateValues(); // update values in store on scroll elem.current.addEventListener("scroll", updateScrollPosition); // update current values on resize to show/hide scroll window.addEventListener("resize", onWindowResize); + + // update scroll state if tabs numbers has changed + dockStore.onTabsNumberChange(() => { + updateStateValues(); + }); }, []); return ( diff --git a/src/renderer/components/dock/dock.scss b/src/renderer/components/dock/dock.scss index ba9be598a0..c3919fc78e 100644 --- a/src/renderer/components/dock/dock.scss +++ b/src/renderer/components/dock/dock.scss @@ -60,6 +60,10 @@ .tabs-wrapper { overflow: hidden; + width: 100%; + box-sizing: border-box; + position: relative; + padding: 0 $padding*2.5; } .tab-content { @@ -83,7 +87,7 @@ .tabs-control { overflow: auto; - position: relative; + width: 100%; &::-webkit-scrollbar { visibility: hidden; @@ -100,20 +104,6 @@ &:hover::-webkit-scrollbar-thumb { background: var(--scrollBarColor); } - - .tab-control { - position: absolute; - z-index: 9999; - } - - .scroll-left { - left: 0; - } - - .right { - right: 0; - } - } .DockTab.active { @@ -123,4 +113,18 @@ color: var(--terminalWhite); } } + + .tab-control { + position: absolute; + z-index: 9999; + } + + .scroll-left { + left: 0; + } + + .scroll-right { + right: 0; + } + } diff --git a/src/renderer/components/dock/dock.tsx b/src/renderer/components/dock/dock.tsx index de63a1e5ba..8f5c38a174 100644 --- a/src/renderer/components/dock/dock.tsx +++ b/src/renderer/components/dock/dock.tsx @@ -157,6 +157,7 @@ class NonInjectedDock extends React.Component { selectedTab={selectedTab} autoFocus={isOpen} onChangeTab={this.onChangeTab} + dockStore={dockStore} />