From 4f22d308b3514ad034af7fac3d4cd91608af056f Mon Sep 17 00:00:00 2001 From: Jordi Bunster Date: Wed, 7 Apr 2021 09:15:00 -0700 Subject: [PATCH] Move tabs state out into React (#621) This PR is just about moving the tabs array. I'm hoping to let it bake for a bit before merging the rest of the tabs in react work. Preview here: https://ms.portal.azure.com/?dataExplorerSource=https%3A%2F%2Fcosmos-explorer-preview.azurewebsites.net%2Fcommit%2Fda809beb82bb54dc82da18eda41caaf7b9b6597f%2Fexplorer.html#@microsoft.onmicrosoft.com/resource/subscriptions/b9c77f10-b438-4c32-9819-eef8a654e478/resourceGroups/stfaul/providers/Microsoft.DocumentDb/databaseAccounts/stfaul-sql/dataExplorer --- src/Explorer/Explorer.tsx | 3 ++- src/Explorer/SplashScreen/SplashScreen.tsx | 13 +++++++------ src/Main.tsx | 10 +++++----- src/hooks/useObservableState.ts | 16 ++++++++++++++++ src/hooks/useTabs.ts | 16 ++++++++++++++++ 5 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 src/hooks/useObservableState.ts create mode 100644 src/hooks/useTabs.ts diff --git a/src/Explorer/Explorer.tsx b/src/Explorer/Explorer.tsx index 367693c99..971ac43a5 100644 --- a/src/Explorer/Explorer.tsx +++ b/src/Explorer/Explorer.tsx @@ -93,6 +93,7 @@ export interface ExplorerParams { closeSidePanel: () => void; closeDialog: () => void; openDialog: (props: DialogProps) => void; + tabsManager: TabsManager; } export default class Explorer { @@ -600,7 +601,7 @@ export default class Explorer { container: this, }); - this.tabsManager = new TabsManager(); + this.tabsManager = params?.tabsManager ?? new TabsManager(); this._panes = [ this.addDatabasePane, diff --git a/src/Explorer/SplashScreen/SplashScreen.tsx b/src/Explorer/SplashScreen/SplashScreen.tsx index 634ba3e44..e5debb883 100644 --- a/src/Explorer/SplashScreen/SplashScreen.tsx +++ b/src/Explorer/SplashScreen/SplashScreen.tsx @@ -50,10 +50,6 @@ export class SplashScreen extends React.Component { this.subscriptions = []; } - public shouldComponentUpdate() { - return this.container.tabsManager.openedTabs.length === 0; - } - public componentWillUnmount() { while (this.subscriptions.length) { this.subscriptions.pop().dispose(); @@ -62,7 +58,6 @@ export class SplashScreen extends React.Component { public componentDidMount() { this.subscriptions.push( - this.container.tabsManager.openedTabs.subscribe(() => this.setState({})), this.container.selectedNode.subscribe(() => this.setState({})), this.container.isNotebookEnabled.subscribe(() => this.setState({})) ); @@ -80,7 +75,13 @@ export class SplashScreen extends React.Component { const tipsItems = this.createTipsItems(); const onClearRecent = this.clearMostRecent; - return ( + const formContainer = (jsx: JSX.Element) => ( +
+
{jsx}
+
+ ); + + return formContainer(
diff --git a/src/Main.tsx b/src/Main.tsx index d829e897b..4137aa59f 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -53,6 +53,7 @@ import "./Explorer/Tabs/QueryTab.less"; import { useConfig } from "./hooks/useConfig"; import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer"; import { useSidePanel } from "./hooks/useSidePanel"; +import { useTabs } from "./hooks/useTabs"; import { KOCommentEnd, KOCommentIfStart } from "./koComment"; import "./Libs/jquery"; import "./Shared/appInsights"; @@ -78,6 +79,7 @@ const App: React.FunctionComponent = () => { }; const { isPanelOpen, panelContent, headerText, openSidePanel, closeSidePanel } = useSidePanel(); + const { tabs, tabsManager } = useTabs(); const explorerParams: ExplorerParams = { setIsNotificationConsoleExpanded, @@ -87,7 +89,9 @@ const App: React.FunctionComponent = () => { closeSidePanel, openDialog, closeDialog, + tabsManager, }; + const config = useConfig(); const explorer = useKnockoutExplorer(config?.platform, explorerParams); @@ -200,11 +204,7 @@ const App: React.FunctionComponent = () => { {/* Splitter - End */}
{/* Collections Tree - End */} -
-
- - -
+ {tabs.length === 0 && }
{/* Collections Tree and Tabs - End */} diff --git a/src/hooks/useObservableState.ts b/src/hooks/useObservableState.ts new file mode 100644 index 000000000..8894499b5 --- /dev/null +++ b/src/hooks/useObservableState.ts @@ -0,0 +1,16 @@ +import { isObservableArray, Observable, ObservableArray } from "knockout"; +import { useEffect, useState } from "react"; + +export function useObservableState(observable: Observable): [T, (s: T) => void]; +export function useObservableState(observable: ObservableArray): [T[], (s: T[]) => void]; +export function useObservableState(observable: ObservableArray | Observable): [T | T[], (s: T | T[]) => void] { + const [value, setValue] = useState(observable()); + + useEffect(() => { + isObservableArray(observable) + ? observable.subscribe((values) => setValue([...values])) + : observable.subscribe(setValue); + }, [observable]); + + return [value, observable]; +} diff --git a/src/hooks/useTabs.ts b/src/hooks/useTabs.ts new file mode 100644 index 000000000..b487accbd --- /dev/null +++ b/src/hooks/useTabs.ts @@ -0,0 +1,16 @@ +import { useState } from "react"; +import TabsBase from "../Explorer/Tabs/TabsBase"; +import { TabsManager } from "../Explorer/Tabs/TabsManager"; +import { useObservableState } from "./useObservableState"; + +export type UseTabs = { + tabs: readonly TabsBase[]; + tabsManager: TabsManager; +}; + +export function useTabs(): UseTabs { + const [tabsManager] = useState(() => new TabsManager()); + const [tabs] = useObservableState(tabsManager.openedTabs); + + return { tabs, tabsManager }; +}