From b5976fb034293b032b7d8cff1d7c96d63927a762 Mon Sep 17 00:00:00 2001 From: Sakshi Gupta Date: Fri, 18 Apr 2025 14:27:19 +0530 Subject: [PATCH] Updated theme on sidebar --- src/Explorer/Sidebar.tsx | 68 ++++++++++++++++++++++++-------- src/Explorer/Theme/ThemeUtil.tsx | 26 +++++++++--- src/Main.tsx | 24 +++++++---- src/hooks/useTheme.tsx | 18 +++++++-- 4 files changed, 103 insertions(+), 33 deletions(-) diff --git a/src/Explorer/Sidebar.tsx b/src/Explorer/Sidebar.tsx index 01fd8a6d1..c13619169 100644 --- a/src/Explorer/Sidebar.tsx +++ b/src/Explorer/Sidebar.tsx @@ -8,9 +8,8 @@ import { MenuList, MenuPopover, MenuTrigger, - mergeClasses, shorthands, - SplitButton, + SplitButton } from "@fluentui/react-components"; import { Add16Regular, ArrowSync12Regular, ChevronLeft12Regular, ChevronRight12Regular } from "@fluentui/react-icons"; import { configContext, Platform } from "ConfigContext"; @@ -18,7 +17,6 @@ import Explorer from "Explorer/Explorer"; import { AddDatabasePanel } from "Explorer/Panes/AddDatabasePanel/AddDatabasePanel"; import { Tabs } from "Explorer/Tabs/Tabs"; import { CosmosFluentProvider, cosmosShorthands, tokens } from "Explorer/Theme/ThemeUtil"; -import { ResourceTree } from "Explorer/Tree/ResourceTree"; import { useDatabases } from "Explorer/useDatabases"; import { KeyboardAction, KeyboardActionGroup, KeyboardActionHandler, useKeyboardActionGroup } from "KeyboardShortcuts"; import { isFabric, isFabricMirrored, isFabricNative } from "Platform/Fabric/FabricUtil"; @@ -26,8 +24,10 @@ import { userContext } from "UserContext"; import { getCollectionName, getDatabaseName } from "Utils/APITypeUtils"; import { Allotment, AllotmentHandle } from "allotment"; import { useSidePanel } from "hooks/useSidePanel"; +import { useTheme } from "hooks/useTheme"; import { debounce } from "lodash"; import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; +import { ResourceTree } from "./Tree/ResourceTree"; const useSidebarStyles = makeStyles({ sidebar: { @@ -35,38 +35,67 @@ const useSidebarStyles = makeStyles({ }, sidebarContainer: { height: "100%", + width: "100%", + borderRight: `1px solid ${tokens.colorNeutralStroke1}`, + transition: "all 0.2s ease-in-out", + display: "flex", + flexDirection: "column", backgroundColor: tokens.colorNeutralBackground1, + position: "relative", }, expandedContent: { display: "grid", height: "100%", + width: "100%", gridTemplateRows: `calc(${tokens.layoutRowHeight} * 2) 1fr`, }, floatingControlsContainer: { - position: "relative", + position: "absolute", + top: 0, + right: 0, zIndex: 1000, - width: "100%", + width: "auto", + padding: tokens.spacingHorizontalS, }, floatingControls: { display: "flex", flexDirection: "row", - position: "absolute", - right: 0, + gap: tokens.spacingHorizontalXS, }, floatingControlButton: { ...shorthands.border("none"), backgroundColor: "transparent", + color: tokens.colorNeutralForeground1, + cursor: "pointer", + padding: tokens.spacingHorizontalXS, + borderRadius: tokens.borderRadiusMedium, + display: "flex", + alignItems: "center", + justifyContent: "center", + ":hover": { + backgroundColor: tokens.colorNeutralBackground1Hover, + color: tokens.colorNeutralForeground1, + }, + ":active": { + backgroundColor: tokens.colorNeutralBackground1Pressed, + color: tokens.colorNeutralForeground1, + }, + ":disabled": { + color: tokens.colorNeutralForegroundDisabled, + cursor: "not-allowed", + }, }, globalCommandsContainer: { display: "grid", alignItems: "center", justifyItems: "center", width: "100%", - containerType: "size", // Use this container for "@container" queries below this. + containerType: "size", + padding: tokens.spacingHorizontalS, ...cosmosShorthands.borderBottom(), + backgroundColor: tokens.colorNeutralBackground1, }, loadingProgressBar: { - // Float above the content position: "absolute", width: "100%", height: "2px", @@ -76,7 +105,7 @@ const useSidebarStyles = makeStyles({ animationDuration: "3s", animationName: { "0%": { - opacity: ".2", // matches indeterminate bar width + opacity: ".2", }, "50%": { opacity: "1", @@ -98,6 +127,13 @@ const useSidebarStyles = makeStyles({ display: "flex", }, }, + treeContainer: { + flex: 1, + overflow: "auto", + paddingLeft: tokens.spacingHorizontalM, + backgroundColor: tokens.colorNeutralBackground1, + color: tokens.colorNeutralForeground1, + }, }); interface GlobalCommandsProps { @@ -250,6 +286,7 @@ export const SidebarContainer: React.FC = ({ explorer }) => { const [expandedSize, setExpandedSize] = React.useState(300); const hasSidebar = userContext.apiType !== "Postgres" && userContext.apiType !== "VCoreMongo"; const allotment = useRef(null); + const { isDarkMode } = useTheme(); const expand = useCallback(() => { if (!expanded) { @@ -304,7 +341,7 @@ export const SidebarContainer: React.FC = ({ explorer }) => { {hasSidebar && ( // When collapsed, we force the pane to 24 pixels wide and make it non-resizable. - +
{loading && ( // The Fluent UI progress bar has some issues in reduced-motion environments so we use a simple CSS animation here. @@ -335,12 +372,11 @@ export const SidebarContainer: React.FC = ({ explorer }) => {
-
+
{hasGlobalCommands && } - +
+ +
) : ( diff --git a/src/Explorer/Theme/ThemeUtil.tsx b/src/Explorer/Theme/ThemeUtil.tsx index 0d6295557..5a7548438 100644 --- a/src/Explorer/Theme/ThemeUtil.tsx +++ b/src/Explorer/Theme/ThemeUtil.tsx @@ -4,16 +4,19 @@ import { FluentProvider, FluentProviderSlots, Theme, + createDarkTheme, createLightTheme, makeStyles, mergeClasses, shorthands, themeToTokensObject, - webLightTheme, + webDarkTheme, + webLightTheme } from "@fluentui/react-components"; import { Platform, configContext } from "ConfigContext"; import React from "react"; import { appThemeFabricTealBrandRamp } from "../../Platform/Fabric/FabricTheme"; +import { useTheme } from "../../hooks/useTheme"; export const LayoutConstants = { rowHeight: 32, @@ -47,6 +50,7 @@ export const CosmosFluentProvider: React.FC = ({ chil // As we convert components to Fluent UI 9, if we end up with nested FluentProviders, the inner FluentProvider will be a no-op. const { isInFluentProvider } = React.useContext(FluentProviderContext); const styles = useDefaultRootStyles(); + const { isDarkMode } = useTheme(); if (isInFluentProvider) { // We're already in a fluent context, don't create another. @@ -61,7 +65,7 @@ export const CosmosFluentProvider: React.FC = ({ chil return ( @@ -114,7 +118,16 @@ const cosmosTheme = { sidebarInitialWidth: "300px", }; -export const tokens = themeToTokensObject({ ...webLightTheme, ...cosmosTheme, ...sizeMappings[LayoutSize.Compact] }); +// Get the current theme tokens based on the root theme +export const getThemeTokens = (isDarkMode: boolean) => + themeToTokensObject({ + ...(isDarkMode ? webDarkTheme : webLightTheme), + ...cosmosTheme, + ...sizeMappings[LayoutSize.Compact] + }); + +// Initialize with light theme, will be updated by the provider +export const tokens = getThemeTokens(false); export const cosmosShorthands = { border: () => shorthands.border("1px", "solid", tokens.colorNeutralStroke2), @@ -124,11 +137,12 @@ export const cosmosShorthands = { borderLeft: () => shorthands.borderLeft("1px", "solid", tokens.colorNeutralStroke2), }; -export function getPlatformTheme(platform: Platform): CosmosTheme { +export function getPlatformTheme(platform: Platform, isDarkMode: boolean = false): CosmosTheme { + const createTheme = isDarkMode ? createDarkTheme : createLightTheme; const baseTheme = platform === Platform.Fabric - ? createLightTheme(appThemeFabricTealBrandRamp) - : createLightTheme(appThemePortalBrandRamp); + ? createTheme(appThemeFabricTealBrandRamp) + : createTheme(appThemePortalBrandRamp); return { ...baseTheme, diff --git a/src/Main.tsx b/src/Main.tsx index 93c9fc507..3496bd2a7 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -11,7 +11,7 @@ import { SQLQuickstartTutorial } from "Explorer/Quickstart/Tutorials/SQLQuicksta import "allotment/dist/style.css"; import "bootstrap/dist/css/bootstrap.css"; import { useCarousel } from "hooks/useCarousel"; -import React from "react"; +import React, { useEffect } from "react"; import ReactDOM from "react-dom"; import "../externals/jquery-ui.min.css"; import "../externals/jquery-ui.min.js"; @@ -82,7 +82,6 @@ const App = (): JSX.Element => { const isCopilotCarouselOpen = useCarousel((state) => state.showCopilotCarousel); const styles = useStyles(); - console.log("App - Current theme: Dark"); if (config?.platform === Platform.Fabric) { loadTheme(appThemeFabric); @@ -140,13 +139,22 @@ 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 - }); + // Apply theme to body for Fluent UI v8 components + useEffect(() => { + if (isDarkMode) { + document.body.classList.add("isDarkMode"); + loadTheme(appThemeFabric); + } else { + document.body.classList.remove("isDarkMode"); + loadTheme(appThemeFabric); + } + }, [isDarkMode]); + + // console.log("Root component - Theme state:", { + // isDarkMode, + // currentTheme + // }); return ( diff --git a/src/hooks/useTheme.tsx b/src/hooks/useTheme.tsx index 7abd4de8a..075c83cf3 100644 --- a/src/hooks/useTheme.tsx +++ b/src/hooks/useTheme.tsx @@ -20,15 +20,27 @@ export const CustomThemeProvider: FC = ({ children, theme }) export const useTheme = () => { const { targetDocument } = useFluent(); + const context = React.useContext(ThemeContext); + const [isDarkMode, setIsDarkMode] = useState(() => { - const hasDarkMode = targetDocument?.body.classList.contains("isDarkMode") ?? true; - return hasDarkMode; + // First check if we're in a theme context + if (context) { + return context.isDarkMode; + } + // Fallback to checking body class + return targetDocument?.body.classList.contains("isDarkMode") ?? true; }); useEffect(() => { if (!targetDocument) return; const checkTheme = () => { + // First check if we're in a theme context + if (context) { + setIsDarkMode(context.isDarkMode); + return; + } + // Fallback to checking body class const hasDarkMode = targetDocument.body.classList.contains("isDarkMode"); setIsDarkMode(hasDarkMode); }; @@ -43,7 +55,7 @@ export const useTheme = () => { observer.observe(targetDocument.body, { attributes: true, attributeFilter: ["class"] }); return () => observer.disconnect(); - }, [targetDocument]); + }, [targetDocument, context]); return { isDarkMode