diff --git a/.gitignore b/.gitignore index 707050daa..fdac2c6aa 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ tsconfig.tsbuildinfo # compiled **/tailwind-output.css + +.pnpm-store diff --git a/packages/storage/lib/base/base.ts b/packages/storage/lib/base/base.ts index 8b2610faa..59e441469 100644 --- a/packages/storage/lib/base/base.ts +++ b/packages/storage/lib/base/base.ts @@ -149,10 +149,15 @@ export const createStorage = (key: string, fallback: D, config?: Sto chrome?.storage[storageEnum].onChanged.addListener(_updateFromStorageOnChanged); } + const onChanged = (callback: (changes: { oldValue?: D; newValue?: D }) => void) => { + chrome.storage[storageEnum].onChanged.addListener(storageChange => callback(storageChange[key])); + }; + return { get, set, getSnapshot, subscribe, + onChanged, }; }; diff --git a/packages/storage/lib/types.ts b/packages/storage/lib/types.ts index 32a84049b..ebfd80b9f 100644 --- a/packages/storage/lib/types.ts +++ b/packages/storage/lib/types.ts @@ -7,6 +7,7 @@ export type BaseStorage = { set: (value: ValueOrUpdate) => Promise; getSnapshot: () => D | null; subscribe: (listener: () => void) => () => void; + onChanged: (callback: (changes: { oldValue?: D; newValue?: D }) => void) => void; }; export type StorageConfig = { diff --git a/packages/tailwind-config/tailwind.config.ts b/packages/tailwind-config/tailwind.config.ts index 865587972..62e9376b2 100644 --- a/packages/tailwind-config/tailwind.config.ts +++ b/packages/tailwind-config/tailwind.config.ts @@ -5,4 +5,5 @@ export default { extend: {}, }, plugins: [], + darkMode: 'selector', } as Omit; diff --git a/packages/ui/lib/components/ToggleButton.tsx b/packages/ui/lib/components/ToggleButton.tsx index 4ec66cc52..7fd9a4ee7 100644 --- a/packages/ui/lib/components/ToggleButton.tsx +++ b/packages/ui/lib/components/ToggleButton.tsx @@ -1,18 +1,15 @@ import { cn } from '@/lib/utils'; -import { useStorage } from '@extension/shared'; -import { exampleThemeStorage } from '@extension/storage'; +import { exampleThemeStorage } from '@extension/storage/lib'; import type { ComponentPropsWithoutRef } from 'react'; type ToggleButtonProps = ComponentPropsWithoutRef<'button'>; export const ToggleButton = ({ className, children, ...props }: ToggleButtonProps) => { - const { isLight } = useStorage(exampleThemeStorage); - return (

Edit pages/devtools-panel/src/Panel.tsx

- {t('toggleTheme')} + {t('toggleTheme')} ); }; const ToggleButton = (props: ComponentPropsWithoutRef<'button'>) => { - const { isLight } = useStorage(exampleThemeStorage); - return (

Edit pages/new-tab/src/NewTab.tsx

The color of this paragraph is defined using SASS.
- {t('toggleTheme')} + {t('toggleTheme')} ); diff --git a/pages/new-tab/src/index.tsx b/pages/new-tab/src/index.tsx index 49b8150e6..4b4ed70b4 100644 --- a/pages/new-tab/src/index.tsx +++ b/pages/new-tab/src/index.tsx @@ -1,8 +1,24 @@ import '@src/index.css'; +import { exampleThemeStorage } from '@extension/storage/lib'; import NewTab from '@src/NewTab'; import { createRoot } from 'react-dom/client'; +import type { ThemeState } from '@extension/storage/lib/types'; const init = () => { + const toggleTheme = (theme?: ThemeState['theme']) => { + if (!theme) { + return; + } + + document.querySelector('html')?.classList.toggle('dark', theme === 'dark'); + }; + + exampleThemeStorage.get().then(themeState => toggleTheme(themeState.theme)); + + exampleThemeStorage.onChanged(changes => { + toggleTheme(changes.newValue?.theme); + }); + const appContainer = document.querySelector('#app-container'); if (!appContainer) { throw new Error('Can not find #app-container'); diff --git a/pages/options/src/Options.tsx b/pages/options/src/Options.tsx index 834091db4..b2dbb87ec 100644 --- a/pages/options/src/Options.tsx +++ b/pages/options/src/Options.tsx @@ -1,25 +1,26 @@ import '@src/Options.css'; import { t } from '@extension/i18n'; -import { useStorage, withErrorBoundary, withSuspense } from '@extension/shared'; -import { exampleThemeStorage } from '@extension/storage'; -import { cn, ErrorDisplay, LoadingSpinner, ToggleButton } from '@extension/ui'; +import { withErrorBoundary, withSuspense } from '@extension/shared'; +import { ErrorDisplay, LoadingSpinner, ToggleButton } from '@extension/ui'; const Options = () => { - const { isLight } = useStorage(exampleThemeStorage); - const logo = isLight ? 'options/logo_horizontal.svg' : 'options/logo_horizontal_dark.svg'; - const goGithubSite = () => chrome.tabs.create({ url: 'https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite' }); return ( -
+

Edit pages/options/src/Options.tsx

- {t('toggleTheme')} + {t('toggleTheme')}
); }; diff --git a/pages/options/src/index.tsx b/pages/options/src/index.tsx index a0b576cd1..eab919656 100644 --- a/pages/options/src/index.tsx +++ b/pages/options/src/index.tsx @@ -1,13 +1,30 @@ import '@src/index.css'; +import { exampleThemeStorage } from '@extension/storage/lib'; import Options from '@src/Options'; import { createRoot } from 'react-dom/client'; +import type { ThemeState } from '@extension/storage/lib/types'; const init = () => { + const toggleTheme = (theme?: ThemeState['theme']) => { + if (!theme) { + return; + } + + document.querySelector('html')?.classList.toggle('dark', theme === 'dark'); + }; + + exampleThemeStorage.get().then(themeState => toggleTheme(themeState.theme)); + + exampleThemeStorage.onChanged(changes => { + toggleTheme(changes.newValue?.theme); + }); + const appContainer = document.querySelector('#app-container'); if (!appContainer) { throw new Error('Can not find #app-container'); } const root = createRoot(appContainer); + root.render(); }; diff --git a/pages/popup/src/Popup.tsx b/pages/popup/src/Popup.tsx index 18134d39f..b5693132c 100644 --- a/pages/popup/src/Popup.tsx +++ b/pages/popup/src/Popup.tsx @@ -1,8 +1,7 @@ import '@src/Popup.css'; import { t } from '@extension/i18n'; -import { useStorage, withErrorBoundary, withSuspense } from '@extension/shared'; -import { exampleThemeStorage } from '@extension/storage'; -import { cn, ErrorDisplay, LoadingSpinner, ToggleButton } from '@extension/ui'; +import { withErrorBoundary, withSuspense } from '@extension/shared'; +import { ErrorDisplay, LoadingSpinner, ToggleButton } from '@extension/ui'; const notificationOptions = { type: 'basic', @@ -12,9 +11,6 @@ const notificationOptions = { } as const; const Popup = () => { - const { isLight } = useStorage(exampleThemeStorage); - const logo = isLight ? 'popup/logo_vertical.svg' : 'popup/logo_vertical_dark.svg'; - const goGithubSite = () => chrome.tabs.create({ url: 'https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite' }); @@ -39,19 +35,23 @@ const Popup = () => { }; return ( -
-
+
+

Edit pages/popup/src/Popup.tsx

diff --git a/pages/popup/src/index.tsx b/pages/popup/src/index.tsx index afd2e44ed..c75d4ad89 100644 --- a/pages/popup/src/index.tsx +++ b/pages/popup/src/index.tsx @@ -1,8 +1,24 @@ import '@src/index.css'; +import { exampleThemeStorage } from '@extension/storage/lib'; import Popup from '@src/Popup'; import { createRoot } from 'react-dom/client'; +import type { ThemeState } from '@extension/storage/lib/types'; const init = () => { + const toggleTheme = (theme?: ThemeState['theme']) => { + if (!theme) { + return; + } + + document.querySelector('html')?.classList.toggle('dark', theme === 'dark'); + }; + + exampleThemeStorage.get().then(themeState => toggleTheme(themeState.theme)); + + exampleThemeStorage.onChanged(changes => { + toggleTheme(changes.newValue?.theme); + }); + const appContainer = document.querySelector('#app-container'); if (!appContainer) { throw new Error('Can not find #app-container'); diff --git a/pages/side-panel/src/index.tsx b/pages/side-panel/src/index.tsx index 350ac23b8..644f0ebaa 100644 --- a/pages/side-panel/src/index.tsx +++ b/pages/side-panel/src/index.tsx @@ -1,13 +1,30 @@ import '@src/index.css'; +import { exampleThemeStorage } from '@extension/storage/lib'; import SidePanel from '@src/SidePanel'; import { createRoot } from 'react-dom/client'; +import type { ThemeState } from '@extension/storage/lib/types'; const init = () => { + const toggleTheme = (theme?: ThemeState['theme']) => { + if (!theme) { + return; + } + + document.querySelector('html')?.classList.toggle('dark', theme === 'dark'); + }; + + exampleThemeStorage.get().then(themeState => toggleTheme(themeState.theme)); + + exampleThemeStorage.onChanged(changes => { + toggleTheme(changes.newValue?.theme); + }); + const appContainer = document.querySelector('#app-container'); if (!appContainer) { throw new Error('Can not find #app-container'); } const root = createRoot(appContainer); + root.render(); };