From f2d6bbf54e2f15814922672812ac123bf2ed4d61 Mon Sep 17 00:00:00 2001 From: Sakshi Gupta Date: Thu, 17 Apr 2025 19:26:25 +0530 Subject: [PATCH] First dark theme commit for command bar --- src/Contracts/ViewModels.ts | 5 +- src/Explorer/DataExplorer.tsx | 29 +++++ src/Explorer/ErrorBoundary.tsx | 37 ++++++ .../Menus/CommandBar/CommandBarComponent.less | 3 +- .../CommandBar/CommandBarComponentAdapter.tsx | 41 ++++--- .../Menus/CommandBar/CommandBarUtil.tsx | 54 ++++++--- src/Explorer/Tabs/Tabs.tsx | 11 +- src/Main.tsx | 105 +++++++++++++----- src/hooks/useTheme.tsx | 51 +++++++++ 9 files changed, 273 insertions(+), 63 deletions(-) create mode 100644 src/Explorer/DataExplorer.tsx create mode 100644 src/Explorer/ErrorBoundary.tsx create mode 100644 src/hooks/useTheme.tsx diff --git a/src/Contracts/ViewModels.ts b/src/Contracts/ViewModels.ts index 83afa9ddb..14c2fee34 100644 --- a/src/Contracts/ViewModels.ts +++ b/src/Contracts/ViewModels.ts @@ -5,7 +5,7 @@ import { TriggerDefinition, UserDefinedFunctionDefinition, } from "@azure/cosmos"; -import Explorer from "../Explorer/Explorer"; +import type Explorer from "../Explorer/Explorer"; import { ConsoleData } from "../Explorer/Menus/NotificationConsole/ConsoleData"; import { CassandraTableKey, CassandraTableKeys } from "../Explorer/Tables/TableDataClient"; import ConflictId from "../Explorer/Tree/ConflictId"; @@ -462,3 +462,6 @@ export interface DropdownOption { value: T; disable?: boolean; } + +// Remove the duplicate Explorer interface and export the type +export type { Explorer }; diff --git a/src/Explorer/DataExplorer.tsx b/src/Explorer/DataExplorer.tsx new file mode 100644 index 000000000..18c112377 --- /dev/null +++ b/src/Explorer/DataExplorer.tsx @@ -0,0 +1,29 @@ +import { makeStyles } from "@fluentui/react-components"; +import React from "react"; +import type { Explorer } from "../Contracts/ViewModels"; +import { useTheme } from "../hooks/useTheme"; + +interface DataExplorerProps { + dataExplorer: Explorer; +} + +const useStyles = makeStyles({ + root: { + backgroundColor: "var(--colorNeutralBackground1)", + color: "var(--colorNeutralForeground1)", + height: "100%", + width: "100%" + } +}); + +export const DataExplorer: React.FC = ({ dataExplorer }) => { + const { isDarkMode } = useTheme(); + const styles = useStyles(); + + + return ( +
+
Data Explorer Content
+
+ ); +}; diff --git a/src/Explorer/ErrorBoundary.tsx b/src/Explorer/ErrorBoundary.tsx new file mode 100644 index 000000000..68bd62b00 --- /dev/null +++ b/src/Explorer/ErrorBoundary.tsx @@ -0,0 +1,37 @@ +import React, { Component, ErrorInfo, ReactNode } from "react"; + +interface Props { + children: ReactNode; +} + +interface State { + hasError: boolean; + error: Error | null; +} + +export class ErrorBoundary extends Component { + public state: State = { + hasError: false, + error: null, + }; + + public static getDerivedStateFromError(error: Error): State { + return { hasError: true, error }; + } + + public componentDidCatch(error: Error, errorInfo: ErrorInfo) { + } + + public render() { + if (this.state.hasError) { + return ( +
+

Something went wrong.

+
{this.state.error && this.state.error.toString()}
+
+ ); + } + + return this.props.children; + } +} diff --git a/src/Explorer/Menus/CommandBar/CommandBarComponent.less b/src/Explorer/Menus/CommandBar/CommandBarComponent.less index b94c0b265..8c83d4e87 100644 --- a/src/Explorer/Menus/CommandBar/CommandBarComponent.less +++ b/src/Explorer/Menus/CommandBar/CommandBarComponent.less @@ -4,11 +4,10 @@ padding: @SmallSpace 0px @SmallSpace 0px; .flex-display(); span { - border-left: @ButtonBorderWidth solid @BaseMediumHigh; margin: 0 10px 0 10px; } } .commandBarContainer { - border-bottom: 1px solid @BaseMedium; + border-bottom: 1px solid var(--colorNeutralStroke1); } diff --git a/src/Explorer/Menus/CommandBar/CommandBarComponentAdapter.tsx b/src/Explorer/Menus/CommandBar/CommandBarComponentAdapter.tsx index eb596c0f7..66b480723 100644 --- a/src/Explorer/Menus/CommandBar/CommandBarComponentAdapter.tsx +++ b/src/Explorer/Menus/CommandBar/CommandBarComponentAdapter.tsx @@ -4,6 +4,7 @@ * and update any knockout observables passed from the parent. */ import { CommandBar as FluentCommandBar, ICommandBarItemProps } from "@fluentui/react"; +import { makeStyles, useFluent } from "@fluentui/react-components"; import { useNotebook } from "Explorer/Notebook/useNotebook"; import { KeyboardActionGroup, useKeyboardActionGroup } from "KeyboardShortcuts"; import { isFabric } from "Platform/Fabric/FabricUtil"; @@ -11,7 +12,6 @@ import { userContext } from "UserContext"; import * as React from "react"; import create, { UseStore } from "zustand"; import { ConnectionStatusType, PoolIdType } from "../../../Common/Constants"; -import { StyleConstants } from "../../../Common/StyleConstants"; import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent"; import Explorer from "../../Explorer"; import { useSelectedNode } from "../../useSelectedNode"; @@ -30,18 +30,26 @@ export interface CommandBarStore { } export const useCommandBar: UseStore = create((set) => ({ - contextButtons: [], + contextButtons: [] as CommandButtonComponentProps[], setContextButtons: (contextButtons: CommandButtonComponentProps[]) => set((state) => ({ ...state, contextButtons })), isHidden: false, setIsHidden: (isHidden: boolean) => set((state) => ({ ...state, isHidden })), })); +const useStyles = makeStyles({ + commandBarContainer: { + borderBottom: "1px solid var(--colorNeutralStroke1)" + } +}); + export const CommandBar: React.FC = ({ container }: Props) => { const selectedNodeState = useSelectedNode(); const buttons = useCommandBar((state) => state.contextButtons); const isHidden = useCommandBar((state) => state.isHidden); - const backgroundColor = StyleConstants.BaseLight; + const { targetDocument } = useFluent(); + // const isDarkMode = targetDocument?.body.classList.contains("isDarkMode"); const setKeyboardHandlers = useKeyboardActionGroup(KeyboardActionGroup.COMMAND_BAR); + const styles = useStyles(); if (userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo") { const buttons = @@ -49,12 +57,15 @@ export const CommandBar: React.FC = ({ container }: Props) => { ? CommandBarComponentButtonFactory.createPostgreButtons(container) : CommandBarComponentButtonFactory.createVCoreMongoButtons(container); return ( -
+
@@ -68,18 +79,18 @@ export const CommandBar: React.FC = ({ container }: Props) => { ); const controlButtons = CommandBarComponentButtonFactory.createControlCommandBarButtons(container); - const uiFabricStaticButtons = CommandBarUtil.convertButton(staticButtons, backgroundColor); + const uiFabricStaticButtons = CommandBarUtil.convertButton(staticButtons, "var(--colorNeutralBackground1)"); if (buttons && buttons.length > 0) { uiFabricStaticButtons.forEach((btn: ICommandBarItemProps) => (btn.iconOnly = true)); } - const uiFabricTabsButtons: ICommandBarItemProps[] = CommandBarUtil.convertButton(contextButtons, backgroundColor); + const uiFabricTabsButtons: ICommandBarItemProps[] = CommandBarUtil.convertButton(contextButtons, "var(--colorNeutralBackground1)"); if (uiFabricTabsButtons.length > 0) { uiFabricStaticButtons.push(CommandBarUtil.createDivider("commandBarDivider")); } - const uiFabricControlButtons = CommandBarUtil.convertButton(controlButtons, backgroundColor); + const uiFabricControlButtons = CommandBarUtil.convertButton(controlButtons, "var(--colorNeutralBackground1)"); uiFabricControlButtons.forEach((btn: ICommandBarItemProps) => (btn.iconOnly = true)); const connectionInfo = useNotebook((state) => state.connectionInfo); @@ -96,14 +107,16 @@ export const CommandBar: React.FC = ({ container }: Props) => { const rootStyle = isFabric() ? { root: { - backgroundColor: "transparent", + backgroundColor: "var(--colorNeutralBackground1)", padding: "2px 8px 0px 8px", - }, + color: "var(--colorNeutralForeground1)" + } } : { root: { - backgroundColor: backgroundColor, - }, + backgroundColor: "var(--colorNeutralBackground1)", + color: "var(--colorNeutralForeground1)" + } }; const allButtons = staticButtons.concat(contextButtons).concat(controlButtons); @@ -111,7 +124,7 @@ export const CommandBar: React.FC = ({ container }: Props) => { setKeyboardHandlers(keyboardHandlers); return ( -
+
{ }); }, [setKeyboardHandlers]); + // Add useEffect to handle context buttons + useEffect(() => { + if (activeReactTab !== undefined) { + // React tabs have no context buttons + useCommandBar.getState().setContextButtons([]); + } + }, [activeReactTab]); + return (
@@ -259,9 +267,6 @@ const isQueryErrorThrown = (tab?: Tab, tabKind?: ReactTabKind): boolean => { }; const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): JSX.Element => { - // React tabs have no context buttons. - useCommandBar.getState().setContextButtons([]); - // eslint-disable-next-line no-console switch (activeReactTab) { case ReactTabKind.Connect: diff --git a/src/Main.tsx b/src/Main.tsx index 52972b462..93c9fc507 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -3,6 +3,8 @@ import "./ReactDevTools"; // CSS Dependencies import { initializeIcons, loadTheme } from "@fluentui/react"; +import { FluentProvider, makeStyles, webDarkTheme, webLightTheme } from "@fluentui/react-components"; +import { Platform } from "ConfigContext"; import { QuickstartCarousel } from "Explorer/Quickstart/QuickstartCarousel"; import { MongoQuickstartTutorial } from "Explorer/Quickstart/Tutorials/MongoQuickstartTutorial"; import { SQLQuickstartTutorial } from "Explorer/Quickstart/Tutorials/SQLQuickstartTutorial"; @@ -19,7 +21,7 @@ import "../externals/jquery.dataTables.min.css"; import "../externals/jquery.typeahead.min.css"; import "../externals/jquery.typeahead.min.js"; // Image Dependencies -import { Platform } from "ConfigContext"; +import { SidePanel } from "Explorer/Panes/PanelContainerComponent"; import { QueryCopilotCarousel } from "Explorer/QueryCopilot/CopilotCarousel"; import { SidebarContainer } from "Explorer/Sidebar"; import { KeyboardShortcutRoot } from "KeyboardShortcuts"; @@ -46,6 +48,7 @@ import "./Explorer/Controls/ErrorDisplayComponent/ErrorDisplayComponent.less"; import "./Explorer/Controls/JsonEditor/JsonEditorComponent.less"; import "./Explorer/Controls/Notebook/NotebookTerminalComponent.less"; import "./Explorer/Controls/TreeComponent/treeComponent.less"; +import { ErrorBoundary } from "./Explorer/ErrorBoundary"; import "./Explorer/Graph/GraphExplorerComponent/graphExplorer.less"; import "./Explorer/Menus/CommandBar/CommandBarComponent.less"; import { CommandBar } from "./Explorer/Menus/CommandBar/CommandBarComponentAdapter"; @@ -54,7 +57,6 @@ import "./Explorer/Menus/CommandBar/MemoryTrackerComponent.less"; import "./Explorer/Menus/NotificationConsole/NotificationConsole.less"; import { NotificationConsole } from "./Explorer/Menus/NotificationConsole/NotificationConsoleComponent"; import "./Explorer/Panes/PanelComponent.less"; -import { SidePanel } from "./Explorer/Panes/PanelContainerComponent"; import "./Explorer/SplashScreen/SplashScreen.less"; import "./Libs/jquery"; import { appThemeFabric } from "./Platform/Fabric/FabricTheme"; @@ -62,13 +64,26 @@ import "./Shared/appInsights"; import { useConfig } from "./hooks/useConfig"; import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer"; -initializeIcons(); +// Initialize icons before React is loaded +initializeIcons(undefined, { disableWarnings: true }); -const App: React.FunctionComponent = () => { +const useStyles = makeStyles({ + root: { + height: "100vh", + width: "100vw", + backgroundColor: "var(--colorNeutralBackground1)", + color: "var(--colorNeutralForeground1)" + } +}); + +const App = (): JSX.Element => { + const config = useConfig(); const isCarouselOpen = useCarousel((state) => state.shouldOpen); const isCopilotCarouselOpen = useCarousel((state) => state.showCopilotCarousel); + const styles = useStyles(); + + console.log("App - Current theme: Dark"); - const config = useConfig(); if (config?.platform === Platform.Fabric) { loadTheme(appThemeFabric); import("../less/documentDBFabric.less"); @@ -81,37 +96,71 @@ const App: React.FunctionComponent = () => { } return ( - -
-
-
- {/* Main Command Bar - Start */} - - {/* Collections Tree and Tabs - Begin */} - - {/* Collections Tree and Tabs - End */} +
+ +
+ + + {} + {} + {} + {}
- - - {} - {} - {} - {} -
- + +
+ ); +}; + +const Root: React.FC = () => { + // Force dark theme + const isDarkMode = true; + const currentTheme = isDarkMode ? webDarkTheme : webLightTheme; + const theme = "Dark"; + + console.log("Root component - Theme state:", { + isDarkMode, + currentTheme, + theme + }); + + return ( + + + + + ); }; const mainElement = document.getElementById("Main"); -ReactDOM.render(, mainElement); +if (mainElement) { + ReactDOM.render(, mainElement); +} function LoadingExplorer(): JSX.Element { return ( diff --git a/src/hooks/useTheme.tsx b/src/hooks/useTheme.tsx new file mode 100644 index 000000000..7abd4de8a --- /dev/null +++ b/src/hooks/useTheme.tsx @@ -0,0 +1,51 @@ +import { useFluent } from "@fluentui/react-components"; +import React, { createContext, FC, ReactNode, useEffect, useState } from "react"; + +interface ThemeContextType { + theme: "Light" | "Dark"; + isDarkMode: boolean; +} + +const ThemeContext = createContext(undefined); + +interface ThemeProviderProps { + children: ReactNode; + theme: "Light" | "Dark"; +} + +export const CustomThemeProvider: FC = ({ children, theme }) => { + const isDarkMode = theme === "Dark"; + return {children}; +}; + +export const useTheme = () => { + const { targetDocument } = useFluent(); + const [isDarkMode, setIsDarkMode] = useState(() => { + const hasDarkMode = targetDocument?.body.classList.contains("isDarkMode") ?? true; + return hasDarkMode; + }); + + useEffect(() => { + if (!targetDocument) return; + + const checkTheme = () => { + const hasDarkMode = targetDocument.body.classList.contains("isDarkMode"); + setIsDarkMode(hasDarkMode); + }; + + // Initial check + checkTheme(); + + // Create a MutationObserver to watch for class changes + const observer = new MutationObserver((mutations) => { + checkTheme(); + }); + observer.observe(targetDocument.body, { attributes: true, attributeFilter: ["class"] }); + + return () => observer.disconnect(); + }, [targetDocument]); + + return { + isDarkMode + }; +};