diff --git a/packages/compass-workspaces/src/components/workspace-tab-context-provider.tsx b/packages/compass-workspaces/src/components/workspace-tab-context-provider.tsx new file mode 100644 index 00000000000..6a3a1d8dade --- /dev/null +++ b/packages/compass-workspaces/src/components/workspace-tab-context-provider.tsx @@ -0,0 +1,138 @@ +import React, { useCallback, useEffect, useRef } from 'react'; +import { + getLocalAppRegistryForTab, + type WorkspaceTab, +} from '../stores/workspaces'; +import { NamespaceProvider } from '@mongodb-js/compass-app-stores/provider'; +import { ConnectionInfoProvider } from '@mongodb-js/compass-connections/provider'; +import { rafraf } from '@mongodb-js/compass-components'; +import { useOnTabReplace } from './workspace-close-handler'; +import { + useTabState, + WorkspaceTabStateProvider, +} from './workspace-tab-state-provider'; +import { AppRegistryProvider } from 'hadron-app-registry'; + +function getInitialPropsForWorkspace(tab: WorkspaceTab) { + switch (tab.type) { + case 'Welcome': + case 'My Queries': + case 'Data Modeling': + case 'Performance': + case 'Databases': + return null; + case 'Shell': + return { + initialEvaluate: tab.initialEvaluate, + initialInput: tab.initialInput, + }; + case 'Collections': + return { namespace: tab.namespace }; + case 'Collection': { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { id, type, connectionId, ...collectionMetadata } = tab; + return { tabId: id, ...collectionMetadata }; + } + } +} + +const TabCloseHandler: React.FunctionComponent = ({ children }) => { + const mountedRef = useRef(false); + const [hasInteractedOnce, setHasInteractedOnce] = useTabState( + 'hasInteractedOnce', + false + ); + + useEffect(() => { + mountedRef.current = true; + return () => { + mountedRef.current = false; + }; + }); + + const markAsInteracted = useCallback(() => { + // Make sure we don't count clicking on buttons that actually cause the + // workspace to change, like using breadcrumbs or clicking on an item in the + // Databases / Collections list. There are certain corner-cases this doesn't + // handle, but it's good enough to prevent most cases where users can lose + // content by accident + rafraf(() => { + if (mountedRef.current) { + setHasInteractedOnce(true); + } + }); + }, [setHasInteractedOnce]); + + useOnTabReplace(() => { + return !hasInteractedOnce; + }); + + return ( + // We're not using these for actual user interactions, just to capture the + // interacted state + // eslint-disable-next-line jsx-a11y/no-static-element-interactions +
+ {children} +
+ ); +}; + +const WorkspaceTabContextProvider: React.FunctionComponent<{ + tab: WorkspaceTab; + sectionType: 'tab-content' | 'tab-title'; + onNamespaceNotFound?: ( + tab: Extract, + fallbackNamespace: string | null + ) => void; + children: React.JSX.Element; +}> = ({ tab, onNamespaceNotFound, sectionType: type, children }) => { + const initialProps = getInitialPropsForWorkspace(tab); + + if (initialProps) { + children = React.cloneElement(children, initialProps); + } + + if ('namespace' in tab) { + children = ( + { + onNamespaceNotFound?.(tab, ns); + }} + > + {children} + + ); + } + + if ('connectionId' in tab) { + children = ( + + {children} + + ); + } + + if (type === 'tab-content') { + children = {children}; + } + + return ( + + + {children} + + + ); +}; + +export { WorkspaceTabContextProvider }; diff --git a/packages/compass-workspaces/src/components/workspaces-provider.tsx b/packages/compass-workspaces/src/components/workspaces-provider.tsx index 38b3236bad4..7063bf06f65 100644 --- a/packages/compass-workspaces/src/components/workspaces-provider.tsx +++ b/packages/compass-workspaces/src/components/workspaces-provider.tsx @@ -30,7 +30,10 @@ export const useWorkspacePlugins = () => { hasWorkspacePlugin: (name: T) => { return workspaces.some((ws) => ws.name === name); }, - getWorkspacePluginByName: (name: T) => { + getWorkspacePluginByName: (name?: T) => { + if (!name) { + return null; + } const plugin = workspaces.find((ws) => ws.name === name); if (!plugin) { throw new Error( diff --git a/packages/compass-workspaces/src/components/workspaces.tsx b/packages/compass-workspaces/src/components/workspaces.tsx index b894b0fb6e0..ad873735703 100644 --- a/packages/compass-workspaces/src/components/workspaces.tsx +++ b/packages/compass-workspaces/src/components/workspaces.tsx @@ -1,12 +1,10 @@ -import React, { useCallback, useEffect, useMemo, useRef } from 'react'; -import { AppRegistryProvider } from 'hadron-app-registry'; +import React, { useCallback, useMemo } from 'react'; import { ErrorBoundary, MongoDBLogoMark, WorkspaceTabs, css, palette, - rafraf, spacing, useDarkMode, } from '@mongodb-js/compass-components'; @@ -20,7 +18,6 @@ import type { import { closeTab, getActiveTab, - getLocalAppRegistryForTab, moveTab, openFallbackWorkspace, openTabFromCurrent, @@ -32,17 +29,9 @@ import { useWorkspacePlugins } from './workspaces-provider'; import toNS from 'mongodb-ns'; import { useLogger } from '@mongodb-js/compass-logging/provider'; import { connect } from '../stores/context'; -import { - WorkspaceTabStateProvider, - useTabState, -} from './workspace-tab-state-provider'; -import { useOnTabReplace } from './workspace-close-handler'; -import { NamespaceProvider } from '@mongodb-js/compass-app-stores/provider'; -import { - ConnectionInfoProvider, - useTabConnectionTheme, -} from '@mongodb-js/compass-connections/provider'; +import { useTabConnectionTheme } from '@mongodb-js/compass-connections/provider'; import { useConnectionsListRef } from '@mongodb-js/compass-connections/provider'; +import { WorkspaceTabContextProvider } from './workspace-tab-context-provider'; type Tooltip = [string, string][]; @@ -64,51 +53,6 @@ const EmptyWorkspaceContent = () => { ); }; -const ActiveTabCloseHandler: React.FunctionComponent = ({ children }) => { - const mountedRef = useRef(false); - const [hasInteractedOnce, setHasInteractedOnce] = useTabState( - 'hasInteractedOnce', - false - ); - - useEffect(() => { - mountedRef.current = true; - return () => { - mountedRef.current = false; - }; - }); - - const markAsInteracted = useCallback(() => { - // Make sure we don't count clicking on buttons that actually cause the - // workspace to change, like using breadcrumbs or clicking on an item in the - // Databases / Collections list. There are certain corner-cases this doesn't - // handle, but it's good enough to prevent most cases where users can lose - // content by accident - rafraf(() => { - if (mountedRef.current) { - setHasInteractedOnce(true); - } - }); - }, [setHasInteractedOnce]); - - useOnTabReplace(() => { - return !hasInteractedOnce; - }); - - return ( - // We're not using these for actual user interactions, just to capture the - // interacted state - // eslint-disable-next-line jsx-a11y/no-static-element-interactions -
- {children} -
- ); -}; - const workspacesContainerStyles = css({ display: 'grid', width: '100%', @@ -309,71 +253,7 @@ const CompassWorkspaces: React.FunctionComponent = ({ }, [tabs, collectionInfo, databaseInfo, getThemeOf, getConnectionById]); const activeTabIndex = tabs.findIndex((tab) => tab === activeTab); - - const activeWorkspaceElement = useMemo(() => { - switch (activeTab?.type) { - case 'Welcome': - case 'My Queries': - case 'Data Modeling': { - const Component = getWorkspacePluginByName(activeTab.type); - return ; - } - case 'Shell': { - const Component = getWorkspacePluginByName(activeTab.type); - return ( - - - - ); - } - case 'Performance': - case 'Databases': { - const Component = getWorkspacePluginByName(activeTab.type); - return ( - - - - ); - } - case 'Collections': { - const Component = getWorkspacePluginByName(activeTab.type); - return ( - - { - onNamespaceNotFound(activeTab, ns); - }} - > - - - - ); - } - case 'Collection': { - const Component = getWorkspacePluginByName(activeTab.type); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { id, type, connectionId, ...collectionMetadata } = activeTab; - return ( - - { - onNamespaceNotFound(activeTab, ns); - }} - > - - - - ); - } - default: - return null; - } - }, [activeTab, getWorkspacePluginByName, onNamespaceNotFound]); + const WorkspaceComponent = getWorkspacePluginByName(activeTab?.type); const onCreateNewTab = useCallback(() => { onCreateTab(openOnEmptyWorkspace); @@ -397,31 +277,26 @@ const CompassWorkspaces: React.FunctionComponent = ({ >
- {activeTab && activeWorkspaceElement ? ( - - { + log.error( + mongoLogId(1_001_000_277), + 'Workspace', + 'Rendering workspace tab failed', + { name: activeTab.type, error: error.message, errorInfo } + ); + }} + > + - - { - log.error( - mongoLogId(1_001_000_277), - 'Workspace', - 'Rendering workspace tab failed', - { name: activeTab.type, error: error.message, errorInfo } - ); - }} - > - {activeWorkspaceElement} - - - - + + + ) : ( )}