From 097afcb1cff8ac31663ca2a7e85c9337e71d7246 Mon Sep 17 00:00:00 2001 From: David Watrous <509299+dpwatrous@users.noreply.github.com> Date: Fri, 8 Sep 2023 12:18:14 -0400 Subject: [PATCH] Add Cycle themes --- .../react-container.component.ts | 3 +- .../src/components/layout/root-pane.tsx | 4 +- packages/bonito-ui/src/theme/azure-theme.ts | 29 +++++++ packages/bonito-ui/src/theme/theme-util.ts | 83 ++++++++++++++++--- web/src/components/application.tsx | 5 +- web/src/components/layout/app-root.tsx | 4 +- 6 files changed, 110 insertions(+), 18 deletions(-) create mode 100644 packages/bonito-ui/src/theme/azure-theme.ts diff --git a/desktop/src/app/components/common/react-container/react-container.component.ts b/desktop/src/app/components/common/react-container/react-container.component.ts index 477f7738fe..ebd95c7829 100644 --- a/desktop/src/app/components/common/react-container/react-container.component.ts +++ b/desktop/src/app/components/common/react-container/react-container.component.ts @@ -12,6 +12,7 @@ import * as ReactDOM from "react-dom"; import {RootPane} from "@azure/bonito-ui/lib/components/layout"; import { Subscription } from "rxjs"; import { Theme, ThemeService } from "app/services"; +import { ThemeName } from "@azure/bonito-ui/lib/theme"; export const ReactWrapper: React.FC = props => { return React.createElement(RootPane, {theme: "explorerDark"}, props.children); @@ -37,7 +38,7 @@ export class ReactContainerComponent

implements OnChanges, OnDestroy, AfterVi private _subs: Subscription[] = []; - private _themeName: string = "explorerLight"; + private _themeName: ThemeName = "explorerLight"; private _themeService: ThemeService; diff --git a/packages/bonito-ui/src/components/layout/root-pane.tsx b/packages/bonito-ui/src/components/layout/root-pane.tsx index b1776f90b5..2d508d0876 100644 --- a/packages/bonito-ui/src/components/layout/root-pane.tsx +++ b/packages/bonito-ui/src/components/layout/root-pane.tsx @@ -1,10 +1,10 @@ import * as React from "react"; -import { getTheme } from "../../theme"; +import { ThemeName, getTheme } from "../../theme"; import { ThemeProvider } from "@fluentui/react-theme-provider"; import { loadTheme } from "@fluentui/react/lib/Styling"; export interface RootPaneProps { - theme?: string; + theme?: ThemeName; } const themeProviderStyles: React.CSSProperties = { diff --git a/packages/bonito-ui/src/theme/azure-theme.ts b/packages/bonito-ui/src/theme/azure-theme.ts new file mode 100644 index 0000000000..7e0409515b --- /dev/null +++ b/packages/bonito-ui/src/theme/azure-theme.ts @@ -0,0 +1,29 @@ +import { PartialAppTheme } from "./app-theme"; + +export const CycleThemeLight: PartialAppTheme = { + semanticColors: { + appHeaderBackground: "#0062b3", + appHeaderText: "#ffffff", + }, +}; + +export const CycleThemeDark: PartialAppTheme = { + semanticColors: { + appHeaderBackground: "#333842", + appHeaderText: "#ffffff", + }, +}; + +export const CycleThemeHighContrastLight: PartialAppTheme = { + semanticColors: { + appHeaderBackground: "#333333", + appHeaderText: "#000000", + }, +}; + +export const CycleThemeHighContrastDark: PartialAppTheme = { + semanticColors: { + appHeaderBackground: "#000000", + appHeaderText: "#ffffff", + }, +}; diff --git a/packages/bonito-ui/src/theme/theme-util.ts b/packages/bonito-ui/src/theme/theme-util.ts index b047bf51cd..bcb6f556e8 100644 --- a/packages/bonito-ui/src/theme/theme-util.ts +++ b/packages/bonito-ui/src/theme/theme-util.ts @@ -14,6 +14,12 @@ import { import { AppTheme } from "./app-theme"; import { useTheme } from "@fluentui/react-theme-provider"; import { getLogger } from "@azure/bonito-core"; +import { + CycleThemeDark, + CycleThemeHighContrastDark, + CycleThemeHighContrastLight, + CycleThemeLight, +} from "./azure-theme"; export const BaseThemeLight = AzureThemeLight; export const BaseThemeDark = AzureThemeDark; @@ -49,7 +55,7 @@ export interface ThemeInfo extends ThemeMapEntry { * @returns A list of objects which contain metadata on each theme */ export function listThemes(): ThemeInfo[] { - const sortedNames = Object.keys(_themeMap).sort(); + const sortedNames = Object.keys(_themeMap).sort() as ThemeName[]; const themes: ThemeInfo[] = []; for (const n of sortedNames) { const entry = _themeMap[n]; @@ -62,18 +68,25 @@ export function listThemes(): ThemeInfo[] { return themes; } +export type ThemeName = keyof typeof _themeMap; + /** * Gets a theme by name * * @returns A FluentUI theme */ -export function getTheme(themeName: string): ThemeInfo { - let mapInfo = _themeMap[themeName]; - if (!mapInfo) { - getLogger("getTheme").error( - `Unable to load theme ${themeName}: Falling back to default` - ); +export function getTheme(themeName: ThemeName | "default"): ThemeInfo { + let mapInfo; + if (themeName === "default") { mapInfo = _themeMap[defaultTheme]; + } else { + mapInfo = _themeMap[themeName]; + if (!mapInfo) { + getLogger("getTheme").error( + `Unable to load theme ${themeName}: Falling back to default` + ); + mapInfo = _themeMap[defaultTheme]; + } } return { name: themeName, @@ -87,9 +100,55 @@ interface ThemeMapEntry { get: () => AppTheme; } -const _themeMap: Record = { +const _themeMap = { + cycleLight: { + label: "Cycle Light", + get: () => { + return _getThemeLazy("cycleLight", BaseThemeLight, (baseTheme) => { + return mergeThemes(baseTheme, CycleThemeLight) as AppTheme; + }); + }, + }, + cycleDark: { + label: "Cycle Dark", + get: () => { + return _getThemeLazy("cycleDark", BaseThemeDark, (baseTheme) => { + return mergeThemes(baseTheme, CycleThemeDark) as AppTheme; + }); + }, + }, + cycleHighContrastLight: { + label: "Cycle High Contrast (Light)", + get: () => { + return _getThemeLazy( + "cycleHighContrastLight", + BaseThemeHighContrastLight, + (baseTheme) => { + return mergeThemes( + baseTheme, + CycleThemeHighContrastLight + ) as AppTheme; + } + ); + }, + }, + cycleHighContrastDark: { + label: "Cycle High Contrast (Dark)", + get: () => { + return _getThemeLazy( + "cycleHighContrastDark", + BaseThemeHighContrastDark, + (baseTheme) => { + return mergeThemes( + baseTheme, + CycleThemeHighContrastDark + ) as AppTheme; + } + ); + }, + }, explorerLight: { - label: "Light", + label: "Explorer Light", get: () => { return _getThemeLazy( "explorerLight", @@ -104,7 +163,7 @@ const _themeMap: Record = { }, }, explorerDark: { - label: "Dark", + label: "Explorer Dark", get: () => { return _getThemeLazy("explorerDark", BaseThemeDark, (baseTheme) => { return mergeThemes(baseTheme, ExplorerThemeDark) as AppTheme; @@ -112,7 +171,7 @@ const _themeMap: Record = { }, }, explorerHighContrastLight: { - label: "High Contrast (Light)", + label: "Explorer High Contrast (Light)", get: () => { return _getThemeLazy( "explorerHighContrastLight", @@ -127,7 +186,7 @@ const _themeMap: Record = { }, }, explorerHighContrastDark: { - label: "High Contrast (Dark)", + label: "Explorer High Contrast (Dark)", get: () => { return _getThemeLazy( "explorerHighContrastDark", diff --git a/web/src/components/application.tsx b/web/src/components/application.tsx index f398d574d8..4fef53723e 100644 --- a/web/src/components/application.tsx +++ b/web/src/components/application.tsx @@ -16,6 +16,7 @@ import { Main } from "./layout/main"; import { Stack, IStackTokens } from "@fluentui/react/lib/Stack"; import { PrimaryButton } from "@fluentui/react/lib/Button"; import { translate } from "@azure/bonito-core"; +import { ThemeName } from "@azure/bonito-ui/lib/theme"; //DefaultButton const dropdownStyles: Partial = { @@ -26,7 +27,7 @@ const dropdownStyles: Partial = { * Represents the entire application */ export const Application: React.FC = () => { - const [theme, setTheme] = React.useState(defaultTheme); + const [theme, setTheme] = React.useState(defaultTheme); const themeOptions = React.useMemo(() => { const options: IDropdownOption[] = []; @@ -74,7 +75,7 @@ export const Application: React.FC = () => { onRenderLabel={() => <>} onChange={(_, option) => { if (option) { - setTheme(String(option.key)); + setTheme(option.key as ThemeName); } }} /> diff --git a/web/src/components/layout/app-root.tsx b/web/src/components/layout/app-root.tsx index 941519fabf..18b96115eb 100644 --- a/web/src/components/layout/app-root.tsx +++ b/web/src/components/layout/app-root.tsx @@ -1,7 +1,9 @@ import * as React from "react"; import { RootPane } from "@azure/bonito-ui/lib/components"; +import { ThemeName } from "@azure/bonito-ui/lib/theme"; + export interface RootProps { - theme?: string; + theme?: ThemeName; } export const AppRoot: React.FC = (props) => {