Updated theme on sidebar

This commit is contained in:
Sakshi Gupta 2025-04-18 14:27:19 +05:30
parent f2d6bbf54e
commit b5976fb034
4 changed files with 103 additions and 33 deletions

View File

@ -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<SidebarProps> = ({ explorer }) => {
const [expandedSize, setExpandedSize] = React.useState(300);
const hasSidebar = userContext.apiType !== "Postgres" && userContext.apiType !== "VCoreMongo";
const allotment = useRef<AllotmentHandle>(null);
const { isDarkMode } = useTheme();
const expand = useCallback(() => {
if (!expanded) {
@ -304,7 +341,7 @@ export const SidebarContainer: React.FC<SidebarProps> = ({ explorer }) => {
{hasSidebar && (
// When collapsed, we force the pane to 24 pixels wide and make it non-resizable.
<Allotment.Pane minSize={24} preferredSize={250}>
<CosmosFluentProvider className={mergeClasses(styles.sidebar)}>
<CosmosFluentProvider>
<div className={styles.sidebarContainer}>
{loading && (
// The Fluent UI progress bar has some issues in reduced-motion environments so we use a simple CSS animation here.
@ -335,13 +372,12 @@ export const SidebarContainer: React.FC<SidebarProps> = ({ explorer }) => {
</button>
</div>
</div>
<div
className={styles.expandedContent}
style={!hasGlobalCommands ? { gridTemplateRows: "1fr" } : undefined}
>
<div className={styles.expandedContent} style={!hasGlobalCommands ? { gridTemplateRows: "1fr" } : undefined}>
{hasGlobalCommands && <GlobalCommands explorer={explorer} />}
<div className={styles.treeContainer}>
<ResourceTree explorer={explorer} />
</div>
</div>
</>
) : (
<button

View File

@ -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<CosmosFluentProviderProps> = ({ 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<CosmosFluentProviderProps> = ({ chil
return (
<FluentProviderContext.Provider value={{ isInFluentProvider: true }}>
<FluentProvider
theme={getPlatformTheme(configContext.platform)}
theme={getPlatformTheme(configContext.platform, isDarkMode)}
className={mergeClasses(styles.fluentProvider, className)}
{...props}
>
@ -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,

View File

@ -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 (
<ErrorBoundary>

View File

@ -20,15 +20,27 @@ export const CustomThemeProvider: FC<ThemeProviderProps> = ({ 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