From 0e2efd668f53269e67aec41d1d123f49331beed3 Mon Sep 17 00:00:00 2001 From: x1unix Date: Sun, 10 Apr 2022 17:09:40 +0200 Subject: [PATCH 01/10] ui: add follow system theme setting prop --- web/src/components/settings/SettingsModal.tsx | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/web/src/components/settings/SettingsModal.tsx b/web/src/components/settings/SettingsModal.tsx index cee3825e..c166aaec 100644 --- a/web/src/components/settings/SettingsModal.tsx +++ b/web/src/components/settings/SettingsModal.tsx @@ -63,6 +63,7 @@ const FONT_OPTS: IDropdownOption[] = [ export interface SettingsChanges { monaco?: MonacoParamsChanges args?: BuildParamsArgs, + settings?: Partial } export interface SettingsProps { @@ -110,6 +111,18 @@ export default class SettingsModal extends React.Component) { + if (!this.changes.settings) { + this.changes.settings = changes; + return; + } + + this.changes.settings = { + ...this.changes.settings, + ...changes, + }; + } + render() { const theme = getTheme(); const contentStyles = getContentStyles(theme); @@ -147,6 +160,19 @@ export default class SettingsModal extends React.Component} /> + { + this.touchSettingsProperty({ + useSystemTheme: val + }); + }} + />} + /> Date: Sun, 10 Apr 2022 17:10:05 +0200 Subject: [PATCH 02/10] ui: map use system theme settings to state --- web/src/components/core/Header.tsx | 11 +++++++++-- web/src/services/config.ts | 15 +++++++++++++++ web/src/store/actions.ts | 10 +++++++++- web/src/store/dispatch.ts | 17 ++++++++++++++++- web/src/store/reducers.ts | 12 +++++++++++- web/src/store/state.ts | 1 + web/src/utils/theme.ts | 20 ++++++++++++++++++++ 7 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 web/src/utils/theme.ts diff --git a/web/src/components/core/Header.tsx b/web/src/components/core/Header.tsx index af486777..a12cdfda 100644 --- a/web/src/components/core/Header.tsx +++ b/web/src/components/core/Header.tsx @@ -13,9 +13,12 @@ import { Dispatcher, dispatchToggleTheme, formatFileDispatcher, - newBuildParamsChangeDispatcher, newCodeImportDispatcher, + newBuildParamsChangeDispatcher, + newCodeImportDispatcher, newImportFileDispatcher, - newMonacoParamsChangeDispatcher, newSnippetLoadDispatcher, + newMonacoParamsChangeDispatcher, + newSnippetLoadDispatcher, + newSettingsChangeDispatcher, runFileDispatcher, saveFileDispatcher, shareSnippetDispatcher @@ -229,6 +232,10 @@ export class Header extends React.Component { this.props.dispatch(newBuildParamsChangeDispatcher(runtime, autoFormat)); } + if (changes.settings) { + this.props.dispatch(newSettingsChangeDispatcher(changes.settings)); + } + this.setState({ showSettings: false }); } diff --git a/web/src/services/config.ts b/web/src/services/config.ts index 90a83986..2d1f8fb7 100644 --- a/web/src/services/config.ts +++ b/web/src/services/config.ts @@ -3,8 +3,10 @@ import { DEFAULT_FONT } from './fonts'; import { DarkTheme, LightTheme } from './colors'; import {PanelState} from '~/store'; import { defaultPanelProps } from '~/styles/layout'; +import {supportsPreferColorScheme} from "~/utils/theme"; const DARK_THEME_KEY = 'ui.darkTheme.enabled'; +const USE_SYSTEM_THEME_KEY = 'ui.darkTheme.useSystem'; const RUNTIME_TYPE_KEY = 'go.build.runtime'; const AUTOFORMAT_KEY = 'go.build.autoFormat'; const MONACO_SETTINGS = 'ms.monaco.settings'; @@ -88,6 +90,19 @@ const Config = { localStorage.setItem(DARK_THEME_KEY, enable.toString()); }, + get useSystemTheme() { + if (this._cache[DARK_THEME_KEY]) { + return this._cache[DARK_THEME_KEY]; + } + + return this.getBoolean(USE_SYSTEM_THEME_KEY, supportsPreferColorScheme()); + }, + + set useSystemTheme(val: boolean) { + this._cache[USE_SYSTEM_THEME_KEY] = val; + localStorage.setItem(USE_SYSTEM_THEME_KEY, val.toString()); + }, + get runtimeType(): RuntimeType { if (this._cache[RUNTIME_TYPE_KEY]) { return this._cache[RUNTIME_TYPE_KEY]; diff --git a/web/src/store/actions.ts b/web/src/store/actions.ts index 283238a5..3b9ca91a 100644 --- a/web/src/store/actions.ts +++ b/web/src/store/actions.ts @@ -1,5 +1,5 @@ import {editor} from "monaco-editor"; -import {PanelState, UIState} from './state'; +import {PanelState, SettingsState, UIState} from './state'; import { RunResponse, EvalEvent } from '~/services/api'; import { MonacoSettings, RuntimeType } from '~/services/config'; @@ -16,6 +16,7 @@ export enum ActionType { MARKER_CHANGE = 'MARKER_CHANGE', ENVIRONMENT_CHANGE = 'ENVIRONMENT_CHANGE', PANEL_STATE_CHANGE = 'PANEL_STATE_CHANGE', + SETTINGS_CHANGE = 'SETTINGS_CHANGE', // Special actions used by Go WASM bridge EVAL_START = 'EVAL_START', @@ -104,6 +105,13 @@ export const newMonacoParamsChangeAction = (changes: MonacoParamsChanges) payload: changes }); +export const newSettingsChangeAction = (changes: Partial) => ( + { + type: ActionType.SETTINGS_CHANGE, + payload: changes + } +); + export const newProgramWriteAction = (event: EvalEvent) => ({ type: ActionType.EVAL_EVENT, diff --git a/web/src/store/dispatch.ts b/web/src/store/dispatch.ts index 37fa8f25..92799005 100644 --- a/web/src/store/dispatch.ts +++ b/web/src/store/dispatch.ts @@ -11,6 +11,7 @@ import { newLoadingAction, newMonacoParamsChangeAction, newPanelStateChangeAction, + newSettingsChangeAction, newProgramWriteAction, newToggleThemeAction, newUIStateChangeAction @@ -23,7 +24,8 @@ import client, { import config, {RuntimeType} from '~/services/config'; import {DEMO_CODE} from '~/components/editor/props'; import {getImportObject, goRun} from '~/services/go'; -import {PanelState, State} from './state'; +import {PanelState, SettingsState, State} from './state'; +import {SettingsChanges} from "@components/settings/SettingsModal"; export type StateProvider = () => State export type DispatchFn = (a: Action | any) => any @@ -64,6 +66,19 @@ export function newMonacoParamsChangeDispatcher(changes: MonacoParamsChanges): D }; } +export const newSettingsChangeDispatcher = (changes: Partial): Dispatcher => ( + (dispatch: DispatchFn, _: StateProvider) => { + if ('useSystemTheme' in changes) { + config.useSystemTheme = !!changes.useSystemTheme; + } + + if ('darkMode' in changes) { + config.darkThemeEnabled = !!changes.darkMode; + } + + dispatch(newSettingsChangeAction(changes)); + } +); export function newBuildParamsChangeDispatcher(runtime: RuntimeType, autoFormat: boolean): Dispatcher { return (dispatch: DispatchFn, _: StateProvider) => { diff --git a/web/src/store/reducers.ts b/web/src/store/reducers.ts index e5105a49..e450b17c 100644 --- a/web/src/store/reducers.ts +++ b/web/src/store/reducers.ts @@ -16,6 +16,7 @@ import { PanelState, UIState, } from './state'; +import {supportsPreferColorScheme} from "~/utils/theme"; const reducers = { editor: mapByAction({ @@ -91,8 +92,16 @@ const reducers = { }, [ActionType.ENVIRONMENT_CHANGE]: (s: SettingsState, { payload }: Action) => ({ ...s, runtime: payload, + }), + [ActionType.SETTINGS_CHANGE]: (s: SettingsState, {payload}: Action>) => ({ + ...s, ...payload }) - }, { darkMode: localConfig.darkThemeEnabled, autoFormat: true, runtime: RuntimeType.GoPlayground }), + }, { + darkMode: localConfig.darkThemeEnabled, + autoFormat: true, + runtime: RuntimeType.GoPlayground, + useSystemTheme: localConfig.useSystemTheme, + }), monaco: mapByAction({ [ActionType.MONACO_SETTINGS_CHANGE]: (s: MonacoSettings, a: Action) => { return Object.assign({}, s, a.payload); @@ -136,6 +145,7 @@ export const getInitialState = (): State => ({ darkMode: localConfig.darkThemeEnabled, autoFormat: localConfig.autoFormat, runtime: localConfig.runtimeType, + useSystemTheme: supportsPreferColorScheme(), }, monaco: config.monacoSettings, panel: defaultPanelProps diff --git a/web/src/store/state.ts b/web/src/store/state.ts index e29b3033..3881f515 100644 --- a/web/src/store/state.ts +++ b/web/src/store/state.ts @@ -23,6 +23,7 @@ export interface StatusState { export interface SettingsState { darkMode: boolean + useSystemTheme: boolean autoFormat: boolean, runtime: RuntimeType, } diff --git a/web/src/utils/theme.ts b/web/src/utils/theme.ts new file mode 100644 index 00000000..04284970 --- /dev/null +++ b/web/src/utils/theme.ts @@ -0,0 +1,20 @@ +const query = '(prefers-color-scheme)'; + +export const supportsPreferColorScheme = () => { + if (!('matchMedia' in window)) { + return false; + } + + // See: https://kilianvalkhof.com/2021/web/detecting-media-query-support-in-css-and-javascript/ + const { media } = window.matchMedia(query); + return media === query; +}; + +export const isDarkModeEnabled = () => { + if (!supportsPreferColorScheme()) { + return false; + } + + const { matches } = window.matchMedia('(prefers-color-scheme: dark)'); + return matches; +} From 39aac8dce30ed98bdcd292510e33adea37a3d1c1 Mon Sep 17 00:00:00 2001 From: x1unix Date: Sun, 10 Apr 2022 17:15:25 +0200 Subject: [PATCH 03/10] ui: hide theme toggle if system theme used --- web/src/components/core/Header.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/src/components/core/Header.tsx b/web/src/components/core/Header.tsx index a12cdfda..cfba1ba8 100644 --- a/web/src/components/core/Header.tsx +++ b/web/src/components/core/Header.tsx @@ -42,12 +42,14 @@ interface Props { darkMode: boolean loading: boolean snippetName?: string + hideThemeToggle?: boolean, dispatch: (d: Dispatcher) => void } @Connect(({ settings, status, ui }) => ({ darkMode: settings.darkMode, loading: status?.loading, + hideThemeToggle: settings.useSystemTheme, snippetName: ui?.shareCreated && ui?.snippetId })) export class Header extends React.Component { @@ -176,6 +178,7 @@ export class Header extends React.Component { text: 'Toggle Dark Mode', ariaLabel: 'Toggle Dark Mode', iconOnly: true, + hidden: this.props.hideThemeToggle, iconProps: { iconName: this.props.darkMode ? 'Brightness' : 'ClearNight' }, onClick: () => { this.props.dispatch(dispatchToggleTheme) @@ -252,7 +255,7 @@ export class Header extends React.Component { !hidden)} overflowItems={this.overflowItems} ariaLabel='CodeEditor menu' /> From 75ca9d4c751301a9218b954ad2ade6e9483cbccc Mon Sep 17 00:00:00 2001 From: x1unix Date: Sun, 10 Apr 2022 22:21:29 +0200 Subject: [PATCH 04/10] ui: listen for system theme change --- .../utils/ConnectedThemeProvider.tsx | 42 +++++++++++++++---- web/src/store/dispatch.ts | 1 - web/src/utils/theme.ts | 35 ++++++++++++++++ 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/web/src/components/utils/ConnectedThemeProvider.tsx b/web/src/components/utils/ConnectedThemeProvider.tsx index abd63ce4..c8a4875e 100644 --- a/web/src/components/utils/ConnectedThemeProvider.tsx +++ b/web/src/components/utils/ConnectedThemeProvider.tsx @@ -1,16 +1,42 @@ -import React from 'react'; +import React, {useEffect} from 'react'; import {connect} from 'react-redux'; import {ThemeProvider, ThemeProviderProps} from '@fluentui/react/lib/Theme'; -import { LightTheme, DarkTheme } from '~/services/colors'; +import { + getThemeFromVariant, + isDarkModeEnabled, + supportsPreferColorScheme, + ThemeVariant, + usePrefersColorScheme +} from '~/utils/theme'; +import {newSettingsChangeAction} from "~/store"; interface Props extends ThemeProviderProps { darkMode?: boolean + useSystemTheme?: boolean + dispatch?: Function } -const ConnectedThemeProvider: React.FunctionComponent = ({darkMode, children, ...props}) => ( - - {children} - -); +const getInitialTheme = (userDarkModeEnabled?: boolean, useSystemTheme?: boolean) => { + if (useSystemTheme && supportsPreferColorScheme()) { + return { currentTheme: isDarkModeEnabled() ? ThemeVariant.dark : ThemeVariant.light, matchMedia: true}; + } -export default connect(({settings: {darkMode}}: any) => ({darkMode}))(ConnectedThemeProvider); + return { currentTheme: userDarkModeEnabled ? ThemeVariant.dark : ThemeVariant.light, matchMedia: false}; +}; + +const ConnectedThemeProvider: React.FunctionComponent = ({darkMode, useSystemTheme, children, dispatch, ...props}) => { + const { currentTheme, matchMedia } = getInitialTheme(darkMode, useSystemTheme); + const theme = usePrefersColorScheme(currentTheme, matchMedia); + + useEffect(() => { + dispatch?.(newSettingsChangeAction({ darkMode: theme === ThemeVariant.dark})); + }, [theme]) + return ( + + {children} + + ); +}; + +export default connect(({settings: {darkMode, useSystemTheme}, dispatch}: any) => + ({darkMode, useSystemTheme, dispatch}))(ConnectedThemeProvider); diff --git a/web/src/store/dispatch.ts b/web/src/store/dispatch.ts index 92799005..b5c9988c 100644 --- a/web/src/store/dispatch.ts +++ b/web/src/store/dispatch.ts @@ -25,7 +25,6 @@ import config, {RuntimeType} from '~/services/config'; import {DEMO_CODE} from '~/components/editor/props'; import {getImportObject, goRun} from '~/services/go'; import {PanelState, SettingsState, State} from './state'; -import {SettingsChanges} from "@components/settings/SettingsModal"; export type StateProvider = () => State export type DispatchFn = (a: Action | any) => any diff --git a/web/src/utils/theme.ts b/web/src/utils/theme.ts index 04284970..978736f3 100644 --- a/web/src/utils/theme.ts +++ b/web/src/utils/theme.ts @@ -1,5 +1,13 @@ +import {useState, useEffect} from "react"; +import {DarkTheme, LightTheme} from "@services/colors"; + const query = '(prefers-color-scheme)'; +export enum ThemeVariant { + dark = 'dark', + light = 'light' +} + export const supportsPreferColorScheme = () => { if (!('matchMedia' in window)) { return false; @@ -18,3 +26,30 @@ export const isDarkModeEnabled = () => { const { matches } = window.matchMedia('(prefers-color-scheme: dark)'); return matches; } + +export const usePrefersColorScheme = (defaultValue: ThemeVariant, enabled = true) : ThemeVariant => { + const [theme, setTheme] = useState(defaultValue); + useEffect(() => { + if (!enabled) { + return; + } + + const handler = ({matches}) => { + if (!enabled) { + return; + } + setTheme(matches ? ThemeVariant.dark : ThemeVariant.light); + }; + + const query = window.matchMedia('(prefers-color-scheme: dark)'); + query.addEventListener('change', handler); + return () => { + query.removeEventListener('change', handler); + } + }, [enabled]); + return theme; +} + +export const getThemeFromVariant = (variant: ThemeVariant) => ( + variant === ThemeVariant.dark ? DarkTheme : LightTheme +); From 591762f0b480c95367c50e2d13decaeb6e9bd072 Mon Sep 17 00:00:00 2001 From: x1unix Date: Sun, 10 Apr 2022 22:30:13 +0200 Subject: [PATCH 05/10] ui: listen for system theme change --- web/src/store/reducers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/store/reducers.ts b/web/src/store/reducers.ts index e450b17c..8bc46bbe 100644 --- a/web/src/store/reducers.ts +++ b/web/src/store/reducers.ts @@ -145,7 +145,7 @@ export const getInitialState = (): State => ({ darkMode: localConfig.darkThemeEnabled, autoFormat: localConfig.autoFormat, runtime: localConfig.runtimeType, - useSystemTheme: supportsPreferColorScheme(), + useSystemTheme: localConfig.useSystemTheme, }, monaco: config.monacoSettings, panel: defaultPanelProps From 70b0b1f541909806d8cf99841e2fbefa33240724 Mon Sep 17 00:00:00 2001 From: x1unix Date: Sun, 10 Apr 2022 22:47:38 +0200 Subject: [PATCH 06/10] ui: set proper theme depending on system theme settings --- .../utils/ConnectedThemeProvider.tsx | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/web/src/components/utils/ConnectedThemeProvider.tsx b/web/src/components/utils/ConnectedThemeProvider.tsx index c8a4875e..7e550537 100644 --- a/web/src/components/utils/ConnectedThemeProvider.tsx +++ b/web/src/components/utils/ConnectedThemeProvider.tsx @@ -8,35 +8,35 @@ import { ThemeVariant, usePrefersColorScheme } from '~/utils/theme'; -import {newSettingsChangeAction} from "~/store"; +import {newSettingsChangeAction, SettingsState} from '~/store'; interface Props extends ThemeProviderProps { - darkMode?: boolean - useSystemTheme?: boolean + settings?: SettingsState dispatch?: Function } -const getInitialTheme = (userDarkModeEnabled?: boolean, useSystemTheme?: boolean) => { +const getInitialTheme = ({darkMode, useSystemTheme}: SettingsState) => { if (useSystemTheme && supportsPreferColorScheme()) { return { currentTheme: isDarkModeEnabled() ? ThemeVariant.dark : ThemeVariant.light, matchMedia: true}; } - return { currentTheme: userDarkModeEnabled ? ThemeVariant.dark : ThemeVariant.light, matchMedia: false}; + return { currentTheme: darkMode ? ThemeVariant.dark : ThemeVariant.light, matchMedia: false}; }; -const ConnectedThemeProvider: React.FunctionComponent = ({darkMode, useSystemTheme, children, dispatch, ...props}) => { - const { currentTheme, matchMedia } = getInitialTheme(darkMode, useSystemTheme); - const theme = usePrefersColorScheme(currentTheme, matchMedia); +const ConnectedThemeProvider: React.FunctionComponent = ({settings, children, dispatch, ...props}) => { + const { currentTheme, matchMedia } = getInitialTheme(settings as SettingsState); + const systemTheme = usePrefersColorScheme(currentTheme, matchMedia); useEffect(() => { - dispatch?.(newSettingsChangeAction({ darkMode: theme === ThemeVariant.dark})); - }, [theme]) + dispatch?.(newSettingsChangeAction({ darkMode: systemTheme === ThemeVariant.dark})); + }, [systemTheme]); + return ( - + {children} ); }; -export default connect(({settings: {darkMode, useSystemTheme}, dispatch}: any) => - ({darkMode, useSystemTheme, dispatch}))(ConnectedThemeProvider); +export default connect(({settings, dispatch}: any) => + ({settings, dispatch}))(ConnectedThemeProvider); From c4cb00f6b1f65960e780d59c680f2e4e1b042594 Mon Sep 17 00:00:00 2001 From: x1unix Date: Sun, 10 Apr 2022 23:17:47 +0200 Subject: [PATCH 07/10] ui: fix cool start of userPrefersColorScheme hook --- web/src/utils/theme.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/src/utils/theme.ts b/web/src/utils/theme.ts index 978736f3..1deda9f1 100644 --- a/web/src/utils/theme.ts +++ b/web/src/utils/theme.ts @@ -42,6 +42,8 @@ export const usePrefersColorScheme = (defaultValue: ThemeVariant, enabled = true }; const query = window.matchMedia('(prefers-color-scheme: dark)'); + setTheme(query.matches ? ThemeVariant.dark : ThemeVariant.light); + query.addEventListener('change', handler); return () => { query.removeEventListener('change', handler); From 435e8a6c6edc9ab46dc0443a7a299f0cb1ee2b04 Mon Sep 17 00:00:00 2001 From: x1unix Date: Mon, 11 Apr 2022 00:00:02 +0200 Subject: [PATCH 08/10] ui: use theme from context --- web/src/components/core/Header.tsx | 80 ++++++++++--------- web/src/components/modals/AboutModal.tsx | 10 ++- web/src/components/modals/ChangeLogModal.tsx | 4 +- web/src/components/preview/Preview.tsx | 21 ++--- .../components/preview/ResizablePreview.tsx | 8 +- web/src/components/settings/SettingsModal.tsx | 9 +-- web/src/components/utils/SharePopup.tsx | 14 +++- .../components/utils/ThemeableComponent.tsx | 10 +++ web/src/styles/modal.ts | 14 ++-- 9 files changed, 99 insertions(+), 71 deletions(-) create mode 100644 web/src/components/utils/ThemeableComponent.tsx diff --git a/web/src/components/core/Header.tsx b/web/src/components/core/Header.tsx index cfba1ba8..424fdb54 100644 --- a/web/src/components/core/Header.tsx +++ b/web/src/components/core/Header.tsx @@ -1,8 +1,8 @@ import React from 'react'; -import { getTheme } from '@fluentui/react'; import { CommandBar, ICommandBarItemProps } from '@fluentui/react/lib/CommandBar'; import SettingsModal, { SettingsChanges } from '~/components/settings/SettingsModal'; +import ThemeableComponent from '@components/utils/ThemeableComponent'; import AboutModal from '~/components/modals/AboutModal'; import config from '~/services/config'; import { getSnippetsMenuItems, SnippetMenuItem } from '~/utils/headerutils'; @@ -52,7 +52,7 @@ interface Props { hideThemeToggle: settings.useSystemTheme, snippetName: ui?.shareCreated && ui?.snippetId })) -export class Header extends React.Component { +export class Header extends ThemeableComponent { private fileInput?: HTMLInputElement; private snippetMenuItems = getSnippetsMenuItems(i => this.onSnippetMenuItemClick(i)); @@ -85,10 +85,9 @@ export class Header extends React.Component { } onSnippetMenuItemClick(item: SnippetMenuItem) { - // if (item.snippet) { - // this.setState({ showShareMessage: true }); - // } - const dispatcher = item.snippet ? newSnippetLoadDispatcher(item.snippet) : newCodeImportDispatcher(item.label, item.text as string); + const dispatcher = item.snippet ? + newSnippetLoadDispatcher(item.snippet) : + newCodeImportDispatcher(item.label, item.text as string); this.props.dispatch(dispatcher); } @@ -215,14 +214,6 @@ export class Header extends React.Component { ] } - get styles() { - // Apply the same colors as rest of Fabric components - const theme = getTheme(); - return { - backgroundColor: theme.palette.white - } - } - private onSettingsClose(changes: SettingsChanges) { if (changes.monaco) { // Update monaco state if some of it's settings were changed @@ -245,30 +236,43 @@ export class Header extends React.Component { render() { const { showShareMessage } = this.state; const { snippetName } = this.props; - - return
- Golang Logo - !hidden)} - overflowItems={this.overflowItems} - ariaLabel='CodeEditor menu' - /> - this.setState({ showShareMessage: false })} - /> - this.onSettingsClose(args)} isOpen={this.state.showSettings} /> - this.setState({ showAbout: false })} isOpen={this.state.showAbout} /> - this.setState({ showChangelog: false })} isOpen={this.state.showChangelog} /> -
; + return ( +
+ Golang Logo + !hidden)} + overflowItems={this.overflowItems} + ariaLabel='CodeEditor menu' + /> + this.setState({ showShareMessage: false })} + /> + this.onSettingsClose(args)} + isOpen={this.state.showSettings} + /> + this.setState({ showAbout: false })} + isOpen={this.state.showAbout} + /> + this.setState({ showChangelog: false })} + isOpen={this.state.showChangelog} + /> +
+ ); } } diff --git a/web/src/components/modals/AboutModal.tsx b/web/src/components/modals/AboutModal.tsx index 7d16f028..3d70973d 100644 --- a/web/src/components/modals/AboutModal.tsx +++ b/web/src/components/modals/AboutModal.tsx @@ -1,5 +1,11 @@ import React from 'react'; -import { getTheme, IconButton, FontWeights, FontSizes, mergeStyleSets } from '@fluentui/react'; +import { + IconButton, + FontWeights, + FontSizes, + mergeStyleSets, + useTheme +} from '@fluentui/react'; import { Modal } from '@fluentui/react/lib/Modal'; import { Link } from '@fluentui/react/lib/Link'; @@ -29,7 +35,7 @@ const modalStyles = mergeStyleSets({ }); export default function AboutModal(props: AboutModalProps) { - const theme = getTheme(); + const theme = useTheme(); const contentStyles = getContentStyles(theme); const iconButtonStyles = getIconButtonStyles(theme); diff --git a/web/src/components/modals/ChangeLogModal.tsx b/web/src/components/modals/ChangeLogModal.tsx index 1b53ce04..8645aaa9 100644 --- a/web/src/components/modals/ChangeLogModal.tsx +++ b/web/src/components/modals/ChangeLogModal.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { getTheme, IconButton } from '@fluentui/react'; +import { useTheme, IconButton } from '@fluentui/react'; import { Modal } from '@fluentui/react/lib/Modal'; import { Link } from '@fluentui/react/lib/Link'; @@ -18,7 +18,7 @@ interface ChangeLogModalProps { } export default function ChangeLogModal(props: ChangeLogModalProps) { - const theme = getTheme(); + const theme = useTheme(); const contentStyles = getContentStyles(theme); const iconButtonStyles = getIconButtonStyles(theme); diff --git a/web/src/components/preview/Preview.tsx b/web/src/components/preview/Preview.tsx index 3dc50de6..3fbb9911 100644 --- a/web/src/components/preview/Preview.tsx +++ b/web/src/components/preview/Preview.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import { MessageBar, MessageBarType, getTheme } from '@fluentui/react'; +import { MessageBar, MessageBarType } from '@fluentui/react'; +import ThemeableComponent from '@components/utils/ThemeableComponent'; import { getDefaultFontFamily } from '~/services/fonts'; import { Connect } from '~/store'; import { RuntimeType } from '~/services/config'; @@ -16,9 +17,9 @@ export interface PreviewProps { } @Connect(s => ({ darkMode: s.settings.darkMode, runtime: s.settings.runtime, ...s.status })) -export default class Preview extends React.Component { +export default class Preview extends ThemeableComponent { get styles() { - const { palette } = getTheme(); + const { palette } = this.theme; return { backgroundColor: palette.neutralLight, color: palette.neutralDark, @@ -35,12 +36,14 @@ export default class Preview extends React.Component { const isWasm = this.props.runtime === RuntimeType.WebAssembly; let content; if (this.props.lastError) { - content = - Error -
-          {this.props.lastError}
-        
-
+ content = ( + + Error +
+            {this.props.lastError}
+          
+
+ ) } else if (this.props.events) { content = this.props.events.map(({Message, Delay, Kind}, k) => ( = ({ collapsed, onViewChange }) => { - const {palette: { accent }, semanticColors: { buttonBorder }} = getTheme(); + const {palette: { accent }, semanticColors: { buttonBorder }} = useTheme(); const onResize = useCallback((e, direction, ref, size) => { switch (layout) { case LayoutType.Vertical: diff --git a/web/src/components/settings/SettingsModal.tsx b/web/src/components/settings/SettingsModal.tsx index c166aaec..7fd9d4c9 100644 --- a/web/src/components/settings/SettingsModal.tsx +++ b/web/src/components/settings/SettingsModal.tsx @@ -2,7 +2,6 @@ import React from 'react'; import { Checkbox, Dropdown, - getTheme, IconButton, IDropdownOption, Modal @@ -11,6 +10,7 @@ import {Pivot, PivotItem} from '@fluentui/react/lib/Pivot'; import {MessageBar, MessageBarType} from '@fluentui/react/lib/MessageBar'; import {Link} from '@fluentui/react/lib/Link'; +import ThemeableComponent from '@components/utils/ThemeableComponent'; import {getContentStyles, getIconButtonStyles} from '~/styles/modal'; import SettingsProperty from './SettingsProperty'; import {MonacoSettings, RuntimeType} from '~/services/config'; @@ -84,7 +84,7 @@ interface SettingsModalState { settings: state.settings, monaco: state.monaco, })) -export default class SettingsModal extends React.Component { +export default class SettingsModal extends ThemeableComponent { private titleID = 'Settings'; private subtitleID = 'SettingsSubText'; private changes: SettingsChanges = {}; @@ -124,9 +124,8 @@ export default class SettingsModal extends React.Component = ({ visible, snippetId, originUrl, onDismiss, target }) => { + const { semanticColors: { bodyBackground } } = useTheme(); const primaryButtonProps: IButtonProps = useMemo( () => ({ children: 'Copy link', @@ -28,13 +35,12 @@ const SharePopup: FC = ({ visible, snippetId, originUrl, onDismiss, targe return <>; } - const { semanticColors: { bodyBackground } } = getTheme(); return ( extends React.Component { + static contextType = ThemeContext; + + get theme() { + return this.context as ITheme; + } +} diff --git a/web/src/styles/modal.ts b/web/src/styles/modal.ts index 79aab3c3..b8f3358f 100644 --- a/web/src/styles/modal.ts +++ b/web/src/styles/modal.ts @@ -5,19 +5,19 @@ import { ITheme } from '@fluentui/react'; -export const getIconButtonStyles = (theme: ITheme) => mergeStyleSets({ +export const getIconButtonStyles = (theme?: ITheme) => mergeStyleSets({ root: { - color: theme.palette.neutralPrimary, + color: theme?.palette.neutralPrimary, marginLeft: 'auto', marginTop: '4px', marginRight: '2px' }, rootHovered: { - color: theme.palette.neutralDark + color: theme?.palette.neutralDark } }); -export const getContentStyles = (theme: ITheme) => mergeStyleSets({ +export const getContentStyles = (theme?: ITheme) => mergeStyleSets({ container: { display: 'flex', flexFlow: 'column nowrap', @@ -26,11 +26,11 @@ export const getContentStyles = (theme: ITheme) => mergeStyleSets({ maxWidth: '480px' }, header: [ - theme.fonts.xLargePlus, + theme?.fonts.xLargePlus, { flex: '1 1 auto', - borderTop: `4px solid ${theme.palette.themePrimary}`, - color: theme.palette.neutralPrimary, + borderTop: `4px solid ${theme?.palette.themePrimary}`, + color: theme?.palette.neutralPrimary, display: 'flex', fontSize: FontSizes.xLarge, alignItems: 'center', From 1790ee8362ec5df9c2535f76a88ca1a240fbdab0 Mon Sep 17 00:00:00 2001 From: x1unix Date: Mon, 11 Apr 2022 00:07:34 +0200 Subject: [PATCH 09/10] ui: sync dark mode state when system theme is used --- web/src/components/utils/ConnectedThemeProvider.tsx | 3 +-- web/src/store/dispatch.ts | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/web/src/components/utils/ConnectedThemeProvider.tsx b/web/src/components/utils/ConnectedThemeProvider.tsx index 7e550537..118ddeeb 100644 --- a/web/src/components/utils/ConnectedThemeProvider.tsx +++ b/web/src/components/utils/ConnectedThemeProvider.tsx @@ -26,10 +26,9 @@ const getInitialTheme = ({darkMode, useSystemTheme}: SettingsState) => { const ConnectedThemeProvider: React.FunctionComponent = ({settings, children, dispatch, ...props}) => { const { currentTheme, matchMedia } = getInitialTheme(settings as SettingsState); const systemTheme = usePrefersColorScheme(currentTheme, matchMedia); - useEffect(() => { dispatch?.(newSettingsChangeAction({ darkMode: systemTheme === ThemeVariant.dark})); - }, [systemTheme]); + }, [systemTheme, dispatch]); return ( diff --git a/web/src/store/dispatch.ts b/web/src/store/dispatch.ts index b5c9988c..7963cc0f 100644 --- a/web/src/store/dispatch.ts +++ b/web/src/store/dispatch.ts @@ -25,6 +25,7 @@ import config, {RuntimeType} from '~/services/config'; import {DEMO_CODE} from '~/components/editor/props'; import {getImportObject, goRun} from '~/services/go'; import {PanelState, SettingsState, State} from './state'; +import {isDarkModeEnabled} from "~/utils/theme"; export type StateProvider = () => State export type DispatchFn = (a: Action | any) => any @@ -69,6 +70,7 @@ export const newSettingsChangeDispatcher = (changes: Partial): Di (dispatch: DispatchFn, _: StateProvider) => { if ('useSystemTheme' in changes) { config.useSystemTheme = !!changes.useSystemTheme; + changes.darkMode = isDarkModeEnabled(); } if ('darkMode' in changes) { From a2cd9223d39bcc1f834c53ccd8e2e54679114140 Mon Sep 17 00:00:00 2001 From: x1unix Date: Mon, 11 Apr 2022 00:09:34 +0200 Subject: [PATCH 10/10] ui: fix min width for resizable panel --- web/src/components/preview/ResizablePreview.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/src/components/preview/ResizablePreview.tsx b/web/src/components/preview/ResizablePreview.tsx index 318f04f2..fc3285ac 100644 --- a/web/src/components/preview/ResizablePreview.tsx +++ b/web/src/components/preview/ResizablePreview.tsx @@ -15,6 +15,7 @@ import {LayoutType, DEFAULT_PANEL_HEIGHT, DEFAULT_PANEL_WIDTH} from '~/styles/la import './ResizablePreview.css'; const MIN_HEIGHT = 36; +const MIN_WIDTH = 120; const handleClasses = { top: 'ResizablePreview__handle--top', left: 'ResizablePreview__handle--left', @@ -83,6 +84,7 @@ const ResizablePreview: React.FC = ({ enable={enabledCorners} onResizeStop={onResize} minHeight={MIN_HEIGHT} + minWidth={MIN_WIDTH} style={{ '--pg-handle-active-color': accent, '--pg-handle-default-color': buttonBorder,