mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-10-13 15:28:05 +01:00
First dark theme commit for command bar
This commit is contained in:
parent
32576f50d3
commit
f2d6bbf54e
@ -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<T> {
|
||||
value: T;
|
||||
disable?: boolean;
|
||||
}
|
||||
|
||||
// Remove the duplicate Explorer interface and export the type
|
||||
export type { Explorer };
|
||||
|
29
src/Explorer/DataExplorer.tsx
Normal file
29
src/Explorer/DataExplorer.tsx
Normal file
@ -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<DataExplorerProps> = ({ dataExplorer }) => {
|
||||
const { isDarkMode } = useTheme();
|
||||
const styles = useStyles();
|
||||
|
||||
|
||||
return (
|
||||
<div className={`dataExplorerContainer ${styles.root}`}>
|
||||
<div>Data Explorer Content</div>
|
||||
</div>
|
||||
);
|
||||
};
|
37
src/Explorer/ErrorBoundary.tsx
Normal file
37
src/Explorer/ErrorBoundary.tsx
Normal file
@ -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<Props, State> {
|
||||
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 (
|
||||
<div style={{ padding: "20px", color: "red" }}>
|
||||
<h2>Something went wrong.</h2>
|
||||
<details style={{ whiteSpace: "pre-wrap" }}>{this.state.error && this.state.error.toString()}</details>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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<CommandBarStore> = 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<Props> = ({ 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<Props> = ({ container }: Props) => {
|
||||
? CommandBarComponentButtonFactory.createPostgreButtons(container)
|
||||
: CommandBarComponentButtonFactory.createVCoreMongoButtons(container);
|
||||
return (
|
||||
<div className="commandBarContainer" style={{ display: isHidden ? "none" : "initial" }}>
|
||||
<div className={styles.commandBarContainer} style={{ display: isHidden ? "none" : "initial" }}>
|
||||
<FluentCommandBar
|
||||
ariaLabel="Use left and right arrow keys to navigate between commands"
|
||||
items={CommandBarUtil.convertButton(buttons, backgroundColor)}
|
||||
items={CommandBarUtil.convertButton(buttons, "var(--colorNeutralBackground1)")}
|
||||
styles={{
|
||||
root: { backgroundColor: backgroundColor },
|
||||
root: {
|
||||
backgroundColor: "var(--colorNeutralBackground1)",
|
||||
color: "var(--colorNeutralForeground1)"
|
||||
}
|
||||
}}
|
||||
overflowButtonProps={{ ariaLabel: "More commands" }}
|
||||
/>
|
||||
@ -68,18 +79,18 @@ export const CommandBar: React.FC<Props> = ({ 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<Props> = ({ 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<Props> = ({ container }: Props) => {
|
||||
setKeyboardHandlers(keyboardHandlers);
|
||||
|
||||
return (
|
||||
<div className="commandBarContainer" style={{ display: isHidden ? "none" : "initial" }}>
|
||||
<div className={styles.commandBarContainer} style={{ display: isHidden ? "none" : "initial" }}>
|
||||
<FluentCommandBar
|
||||
ariaLabel="Use left and right arrow keys to navigate between commands"
|
||||
items={uiFabricStaticButtons.concat(uiFabricTabsButtons)}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import {
|
||||
Dropdown,
|
||||
ICommandBarItemProps,
|
||||
IComponentAsProps,
|
||||
IconType,
|
||||
IDropdownOption,
|
||||
IDropdownStyles,
|
||||
Dropdown,
|
||||
ICommandBarItemProps,
|
||||
IComponentAsProps,
|
||||
IconType,
|
||||
IDropdownOption,
|
||||
IDropdownStyles,
|
||||
} from "@fluentui/react";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import { KeyboardHandlerMap } from "KeyboardShortcuts";
|
||||
@ -53,7 +53,7 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
|
||||
const result: ICommandBarItemProps = {
|
||||
iconProps: {
|
||||
style: {
|
||||
width: StyleConstants.CommandBarIconWidth, // 16
|
||||
width: StyleConstants.CommandBarIconWidth,
|
||||
alignSelf: btn.iconName ? "baseline" : undefined,
|
||||
filter: getFilter(btn.disabled),
|
||||
},
|
||||
@ -79,7 +79,7 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
|
||||
"data-test": `CommandBar/Button:${label}`,
|
||||
buttonStyles: {
|
||||
root: {
|
||||
backgroundColor: backgroundColor,
|
||||
backgroundColor: "var(--colorNeutralBackground1)",
|
||||
height: buttonHeightPx,
|
||||
paddingRight: 0,
|
||||
paddingLeft: 0,
|
||||
@ -87,15 +87,29 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
|
||||
minWidth: 24,
|
||||
marginLeft: isSplit ? 0 : 5,
|
||||
marginRight: isSplit ? 0 : 5,
|
||||
color: "var(--colorNeutralForeground1)",
|
||||
selectors: {
|
||||
"&:hover": {
|
||||
backgroundColor: "var(--colorNeutralBackground1Hover)",
|
||||
color: "var(--colorNeutralForeground1)"
|
||||
},
|
||||
"&:active": {
|
||||
backgroundColor: "var(--colorNeutralBackground1Pressed)",
|
||||
color: "var(--colorNeutralForeground1)"
|
||||
}
|
||||
}
|
||||
},
|
||||
rootDisabled: {
|
||||
backgroundColor: backgroundColor,
|
||||
backgroundColor: "var(--colorNeutralBackground1)",
|
||||
pointerEvents: "auto",
|
||||
color: "var(--colorNeutralForegroundDisabled)"
|
||||
},
|
||||
splitButtonMenuButton: {
|
||||
backgroundColor: backgroundColor,
|
||||
backgroundColor: "var(--colorNeutralBackground1)",
|
||||
selectors: {
|
||||
":hover": { backgroundColor: hoverColor },
|
||||
":hover": {
|
||||
backgroundColor: "var(--colorNeutralBackground1Hover)"
|
||||
},
|
||||
},
|
||||
width: 16,
|
||||
},
|
||||
@ -104,13 +118,22 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
|
||||
configContext.platform == Platform.Fabric
|
||||
? StyleConstants.DefaultFontSize
|
||||
: StyleConstants.mediumFontSize,
|
||||
color: "var(--colorNeutralForeground1)"
|
||||
},
|
||||
rootHovered: {
|
||||
backgroundColor: "var(--colorNeutralBackground1Hover)",
|
||||
color: "var(--colorNeutralForeground1)"
|
||||
},
|
||||
rootPressed: {
|
||||
backgroundColor: "var(--colorNeutralBackground1Pressed)",
|
||||
color: "var(--colorNeutralForeground1)"
|
||||
},
|
||||
rootHovered: { backgroundColor: hoverColor },
|
||||
rootPressed: { backgroundColor: hoverColor },
|
||||
splitButtonMenuButtonExpanded: {
|
||||
backgroundColor: StyleConstants.AccentExtra,
|
||||
backgroundColor: "var(--colorNeutralBackground1Pressed)",
|
||||
selectors: {
|
||||
":hover": { backgroundColor: hoverColor },
|
||||
":hover": {
|
||||
backgroundColor: "var(--colorNeutralBackground1Hover)"
|
||||
},
|
||||
},
|
||||
},
|
||||
splitButtonDivider: {
|
||||
@ -119,6 +142,7 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
|
||||
icon: {
|
||||
paddingLeft: 0,
|
||||
paddingRight: 0,
|
||||
color: "var(--colorNeutralForeground1)"
|
||||
},
|
||||
splitButtonContainer: {
|
||||
marginLeft: 5,
|
||||
|
@ -40,6 +40,14 @@ export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
|
||||
});
|
||||
}, [setKeyboardHandlers]);
|
||||
|
||||
// Add useEffect to handle context buttons
|
||||
useEffect(() => {
|
||||
if (activeReactTab !== undefined) {
|
||||
// React tabs have no context buttons
|
||||
useCommandBar.getState().setContextButtons([]);
|
||||
}
|
||||
}, [activeReactTab]);
|
||||
|
||||
return (
|
||||
<div className="tabsManagerContainer">
|
||||
<div className="nav-tabs-margin">
|
||||
@ -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:
|
||||
|
105
src/Main.tsx
105
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 (
|
||||
<KeyboardShortcutRoot>
|
||||
<div className="flexContainer" aria-hidden="false" data-test="DataExplorerRoot">
|
||||
<div id="divExplorer" className="flexContainer hideOverflows">
|
||||
<div id="freeTierTeachingBubble"> </div>
|
||||
{/* Main Command Bar - Start */}
|
||||
<CommandBar container={explorer} />
|
||||
{/* Collections Tree and Tabs - Begin */}
|
||||
<SidebarContainer explorer={explorer} />
|
||||
{/* Collections Tree and Tabs - End */}
|
||||
<div id="Main" className={styles.root}>
|
||||
<KeyboardShortcutRoot>
|
||||
<div
|
||||
className="flexContainer"
|
||||
style={{ flex: 1, display: "flex", flexDirection: "column" }}
|
||||
aria-hidden="false"
|
||||
data-test="DataExplorerRoot"
|
||||
>
|
||||
<div
|
||||
className="dataExplorerErrorConsoleContainer"
|
||||
role="contentinfo"
|
||||
aria-label="Notification console"
|
||||
id="explorerNotificationConsole"
|
||||
id="divExplorer"
|
||||
className="flexContainer hideOverflows"
|
||||
style={{ flex: 1, display: "flex", flexDirection: "column" }}
|
||||
>
|
||||
<NotificationConsole />
|
||||
<div id="freeTierTeachingBubble"> </div>
|
||||
{/* Main Command Bar - Start */}
|
||||
<CommandBar container={explorer} />
|
||||
{/* Collections Tree and Tabs - Begin */}
|
||||
<SidebarContainer explorer={explorer} />
|
||||
{/* Collections Tree and Tabs - End */}
|
||||
<div
|
||||
className="dataExplorerErrorConsoleContainer"
|
||||
role="contentinfo"
|
||||
aria-label="Notification console"
|
||||
id="explorerNotificationConsole"
|
||||
>
|
||||
<NotificationConsole />
|
||||
</div>
|
||||
</div>
|
||||
<SidePanel />
|
||||
<Dialog />
|
||||
{<QuickstartCarousel isOpen={isCarouselOpen} />}
|
||||
{<SQLQuickstartTutorial />}
|
||||
{<MongoQuickstartTutorial />}
|
||||
{<QueryCopilotCarousel isOpen={isCopilotCarouselOpen} explorer={explorer} />}
|
||||
</div>
|
||||
<SidePanel />
|
||||
<Dialog />
|
||||
{<QuickstartCarousel isOpen={isCarouselOpen} />}
|
||||
{<SQLQuickstartTutorial />}
|
||||
{<MongoQuickstartTutorial />}
|
||||
{<QueryCopilotCarousel isOpen={isCopilotCarouselOpen} explorer={explorer} />}
|
||||
</div>
|
||||
</KeyboardShortcutRoot>
|
||||
</KeyboardShortcutRoot>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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 (
|
||||
<ErrorBoundary>
|
||||
<FluentProvider theme={currentTheme}>
|
||||
<App />
|
||||
</FluentProvider>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
const mainElement = document.getElementById("Main");
|
||||
ReactDOM.render(<App />, mainElement);
|
||||
if (mainElement) {
|
||||
ReactDOM.render(<Root />, mainElement);
|
||||
}
|
||||
|
||||
function LoadingExplorer(): JSX.Element {
|
||||
return (
|
||||
|
51
src/hooks/useTheme.tsx
Normal file
51
src/hooks/useTheme.tsx
Normal file
@ -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<ThemeContextType | undefined>(undefined);
|
||||
|
||||
interface ThemeProviderProps {
|
||||
children: ReactNode;
|
||||
theme: "Light" | "Dark";
|
||||
}
|
||||
|
||||
export const CustomThemeProvider: FC<ThemeProviderProps> = ({ children, theme }) => {
|
||||
const isDarkMode = theme === "Dark";
|
||||
return <ThemeContext.Provider value={{ theme, isDarkMode }}>{children}</ThemeContext.Provider>;
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user