addons/isl/src/theme.tsxblame
View source
b69ab311/**
b69ab312 * Copyright (c) Meta Platforms, Inc. and affiliates.
b69ab313 *
b69ab314 * This source code is licensed under the MIT license found in the
b69ab315 * LICENSE file in the root directory of this source tree.
b69ab316 */
b69ab317
b69ab318import {atom} from 'jotai';
b69ab319import {useCommand} from './ISLShortcuts';
b69ab3110import {localStorageBackedAtom, writeAtom} from './jotaiUtils';
b69ab3111import platform from './platform';
b69ab3112import {registerDisposable} from './utils';
b69ab3113
b69ab3114import 'isl-components/theme/themeDark.css';
b69ab3115import 'isl-components/theme/themeLight.css';
b69ab3116
b69ab3117const THEME_LOCAL_STORAGE_KEY = 'isl-color-theme';
b69ab3118
b69ab3119export type ThemeColor = 'dark' | 'light';
b69ab3120
b69ab3121// local override. `null` means prefer platform theme.
b69ab3122const localThemeState = localStorageBackedAtom<ThemeColor | null>(THEME_LOCAL_STORAGE_KEY, null);
b69ab3123
b69ab3124// platform theme. `null` means not supported.
b69ab3125const theme = platform.theme;
b69ab3126const platformThemeState = atom<ThemeColor | undefined>(theme?.getTheme());
b69ab3127registerDisposable(
b69ab3128 platform,
b69ab3129 theme?.onDidChangeTheme(themeColor => {
b69ab3130 writeAtom(platformThemeState, themeColor);
b69ab3131 // reset local theme state so the user can notice the theme change
b69ab3132 writeAtom(localThemeState, null);
b69ab3133 theme.getThemeName && writeAtom(themeNameState, theme.getThemeName());
b69ab3134 }) ?? {dispose: () => null},
b69ab3135 import.meta.hot,
b69ab3136);
b69ab3137
b69ab3138// combined state
b69ab3139// - read: nullable local theme -> platform theme -> 'dark'
b69ab3140// - write: update local theme
b69ab3141export const themeState = atom<ThemeColor, [ThemeColor], void>(
b69ab3142 get => get(localThemeState) ?? get(platformThemeState) ?? 'dark',
b69ab3143 (_get, set, themeColor) => set(localThemeState, themeColor),
b69ab3144);
b69ab3145
b69ab3146/**
b69ab3147 * The specific theme name, like "Default Light Modern".
b69ab3148 * Typically, you'd rather use `themeState` to get simply "light" / "dark".
b69ab3149 * Theme name is useful for dynamically updating stylex styles for specific themes.
b69ab3150 */
b69ab3151export const themeNameState = atom<string | undefined>(theme?.getThemeName?.());
b69ab3152
b69ab3153export function useThemeShortcut() {
b69ab3154 useCommand('ToggleTheme', () => {
b69ab3155 if (platform.theme == null) {
b69ab3156 writeAtom(localThemeState, theme => (theme === 'dark' ? 'light' : 'dark'));
b69ab3157 }
b69ab3158 });
b69ab3159}