diff --git a/src/components/Viewport/Viewport.stories.tsx b/src/components/Viewport/Viewport.stories.tsx index 66fb6a7..36ffddf 100644 --- a/src/components/Viewport/Viewport.stories.tsx +++ b/src/components/Viewport/Viewport.stories.tsx @@ -37,7 +37,7 @@ export const Viewport = () => { const App = () => { const [arr, setArr] = useState(Array.from(Array(5))); - const {setMode, resetSizes, setSizes} = useViewport(); + const {setMode, setSizes} = useViewport(); return (
{
diff --git a/src/plugin/index.ts b/src/plugin/index.ts index 5ba2e11..3383d12 100644 --- a/src/plugin/index.ts +++ b/src/plugin/index.ts @@ -36,7 +36,7 @@ export default definePlugin((options: PluginOptions = {}) => { let styleBuilder: BuilderContract; return { - name: "adnbn-ui", + name: "addon-ui", startup: ({config}) => { const {srcDir, appsDir, sharedDir, app, appSrcDir} = config; const normalizeThemeDir = path.normalize(themeDir).split(path.sep); diff --git a/src/providers/theme/ThemeProvider.tsx b/src/providers/theme/ThemeProvider.tsx index 1c05051..6a41698 100644 --- a/src/providers/theme/ThemeProvider.tsx +++ b/src/providers/theme/ThemeProvider.tsx @@ -1,11 +1,12 @@ -import React, {FC, PropsWithChildren, useCallback, useEffect, useState} from "react"; -import {getBrowser} from "adnbn"; +import React, {FC, PropsWithChildren, useCallback, useEffect, useMemo, useState} from "react"; import {ThemeContext} from "./context"; import {Theme, ThemeStorageContract} from "../../types/theme"; import {Config} from "../../types/config"; +import ThemeStorage from "./ThemeStorage"; + const isDarkMedia = () => window?.matchMedia("(prefers-color-scheme: dark)")?.matches; const isValid = (theme: Theme | undefined): theme is Theme => { @@ -13,22 +14,29 @@ const isValid = (theme: Theme | undefined): theme is Theme => { }; export interface ThemeProviderProps extends Pick { - storage?: ThemeStorageContract; - view?: string; + storage?: ThemeStorageContract | true; } -const ThemeProvider: FC> = ({children, components, storage, view}) => { +const ThemeProvider: FC> = ({children, components, storage}) => { const [theme, setTheme] = useState(() => (isDarkMedia() ? Theme.Dark : Theme.Light)); + const currentStorage: ThemeStorageContract | undefined = useMemo(() => { + if (!storage) return; + + if (storage === true) return new ThemeStorage(); + + return storage; + }, [storage]); + const changeTheme = useCallback( (theme: Theme) => { - if (storage) { - storage.change(theme).catch(e => console.error("ThemeProvider: set theme to storage error", e)); + if (currentStorage) { + currentStorage.change(theme).catch(e => console.error("ThemeProvider: set theme to storage error", e)); } else { setTheme(theme); } }, - [storage] + [currentStorage] ); const toggleTheme = useCallback(() => { @@ -36,31 +44,20 @@ const ThemeProvider: FC> = ({children, com }, [theme, changeTheme]); useEffect(() => { - if (!storage) return; + if (!currentStorage) return; - storage + currentStorage .get() .then(newTheme => isValid(newTheme) && setTheme(newTheme)) .catch(e => console.error("ThemeProvider: get theme from storage error", e)); - const unsubscribe = storage.watch(newTheme => isValid(newTheme) && setTheme(newTheme)); + const unsubscribe = currentStorage.watch(newTheme => isValid(newTheme) && setTheme(newTheme)); return () => unsubscribe(); - }, [storage]); + }, [currentStorage]); useEffect(() => { - const html = document.querySelector("html"); - if (html) { - html.setAttribute("theme", theme); - - view && html.setAttribute("view", view); - - try { - html.setAttribute("browser", getBrowser()); - } catch (e) { - console.error("ThemeProvider: get browser error", e); - } - } + document.querySelector("html")?.setAttribute("theme", theme); }, [theme]); return ( diff --git a/src/providers/theme/ThemeStorage.tsx b/src/providers/theme/ThemeStorage.tsx index 2f8a769..af89dea 100644 --- a/src/providers/theme/ThemeStorage.tsx +++ b/src/providers/theme/ThemeStorage.tsx @@ -1,14 +1,21 @@ -import {Storage} from "@addon-core/storage"; +import {Storage, StorageProvider} from "@addon-core/storage"; import {Theme, ThemeStorageContract} from "../../types/theme"; +export type ThemeStorageState = Record; + export default class implements ThemeStorageContract { - private readonly storage = new Storage>({ + protected storage: StorageProvider = new Storage({ area: "local", namespace: "addon-ui", }); - private readonly key = "theme"; + protected key: string = "theme"; + + constructor(storage?: StorageProvider, key?: string) { + this.storage = storage ? storage : this.storage; + this.key = key ? key : this.key; + } public async get(): Promise { return await this.storage.get(this.key); diff --git a/src/providers/theme/index.ts b/src/providers/theme/index.ts index 2a96847..01ce551 100644 --- a/src/providers/theme/index.ts +++ b/src/providers/theme/index.ts @@ -1,3 +1,3 @@ -export {default as ThemeProvider} from "./ThemeProvider"; -export {default as ThemeStorage} from "./ThemeStorage"; +export {default as ThemeProvider, type ThemeProviderProps} from "./ThemeProvider"; +export {default as ThemeStorage, type ThemeStorageState} from "./ThemeStorage"; export {useTheme, useComponentProps} from "./context"; diff --git a/src/providers/ui/UIProvider.tsx b/src/providers/ui/UIProvider.tsx index 4194c5b..f6e633d 100644 --- a/src/providers/ui/UIProvider.tsx +++ b/src/providers/ui/UIProvider.tsx @@ -1,9 +1,12 @@ -import React, {FC, PropsWithChildren, useMemo, useRef} from "react"; +import React, {FC, PropsWithChildren, useEffect, useMemo} from "react"; +import {getBrowser} from "adnbn"; + import {merge} from "ts-deepmerge"; -import {ExtraProvider, IconsProvider, ThemeProvider, ThemeStorage} from "../index"; +import {ExtraProvider} from "../extra"; +import {IconsProvider} from "../icons"; +import {ThemeProvider, ThemeProviderProps} from "../theme"; -import {ThemeStorageContract} from "../../types/theme"; import {ComponentsProps, Config, ExtraProps, Icons} from "../../types/config"; import "./styles/default.scss"; @@ -12,22 +15,18 @@ import "addon-ui-style.scss"; import config from "addon-ui-config"; -export type UIProviderProps = Partial & { +export interface UIProviderProps extends Partial, Pick { view?: string; -}; +} const UIProvider: FC> = ({ children, components = {}, extra = {}, icons = {}, + storage, view, }) => { - const storageRef = useRef(null); - - if (!storageRef.current) { - storageRef.current = new ThemeStorage(); - } const componentsProps = useMemo(() => merge(config.components || {}, components), [components]); @@ -35,8 +34,18 @@ const UIProvider: FC> = ({ const svgIcons = useMemo(() => merge(config.icons || {}, icons), [icons]); + useEffect(() => { + const html = document.querySelector("html"); + if (html) { + if (view) { + html.setAttribute("view", view); + } + html.setAttribute("browser", getBrowser()); + } + }, [view]); + return ( - + {children}