diff --git a/.changeset/honest-dingos-think.md b/.changeset/honest-dingos-think.md new file mode 100644 index 0000000000..1305342ffa --- /dev/null +++ b/.changeset/honest-dingos-think.md @@ -0,0 +1,5 @@ +--- +'@graphcommerce/next-ui': patch +--- + +Created a cssFlags functionality to allow for conditional rendering based on stored flags in the localStorage diff --git a/.changeset/rotten-steaks-yawn.md b/.changeset/rotten-steaks-yawn.md new file mode 100644 index 0000000000..782df3dd53 --- /dev/null +++ b/.changeset/rotten-steaks-yawn.md @@ -0,0 +1,5 @@ +--- +"@graphcommerce/next-ui": minor +--- + +Add props to DarkLightModeThemeProvider to disable dark/light mode or to change the default ssr mode. Save user chosen mode in localStorage diff --git a/docs/framework/theming.md b/docs/framework/theming.md index 061d5c48f0..602ebdb5a6 100644 --- a/docs/framework/theming.md +++ b/docs/framework/theming.md @@ -246,6 +246,12 @@ width, borderRadius and margin. Performance-wise, font-size and line-height should not be scaled with responsiveVal. To learn more, look into [responsive font sizes](../framework/typography.md). +## Disabling darkmode or lightmode site wide + +Remove light={lightTheme} or dark={darkTheme} from the +`` in \_app.tsx to disable darkmode or lightmode +site wide. + ## Next steps - Learn about [icons](../framework/icons.md) in GraphCommerce diff --git a/examples/magento-graphcms/pages/_document.tsx b/examples/magento-graphcms/pages/_document.tsx index 26a35dab3f..4dcb8893e7 100644 --- a/examples/magento-graphcms/pages/_document.tsx +++ b/examples/magento-graphcms/pages/_document.tsx @@ -1,7 +1,7 @@ import { normalizeLocale } from '@graphcommerce/lingui-next' import { withLingui } from '@graphcommerce/lingui-next/document/withLingui' import type { LinguiDocumentProps } from '@graphcommerce/lingui-next/document/withLingui' -import { EmotionCacheProps, withEmotionCache } from '@graphcommerce/next-ui' +import { EmotionCacheProps, getCssFlagsInitScript, withEmotionCache } from '@graphcommerce/next-ui' import NextDocument, { Html, Head, Main, NextScript } from 'next/document' class Document extends NextDocument { @@ -9,6 +9,7 @@ class Document extends NextDocument { return ( + {getCssFlagsInitScript()} {/* Inject MUI styles first to match with the prepend: true configuration. */} {this.props.emotionStyleTags} diff --git a/packages/magento-customer/components/SignOutForm/signOut.ts b/packages/magento-customer/components/SignOutForm/signOut.ts index 8557aaac17..27da5ca095 100644 --- a/packages/magento-customer/components/SignOutForm/signOut.ts +++ b/packages/magento-customer/components/SignOutForm/signOut.ts @@ -1,6 +1,8 @@ import { ApolloClient } from '@graphcommerce/graphql' +import { removeCssFlag } from '@graphcommerce/next-ui' export function signOut(client: ApolloClient) { + removeCssFlag('signed-in') client.cache.evict({ fieldName: 'currentCartId' }) client.cache.evict({ fieldName: 'cart' }) client.cache.evict({ fieldName: 'customerToken' }) diff --git a/packages/magento-customer/hooks/useSignInForm.ts b/packages/magento-customer/hooks/useSignInForm.ts index be0f09f54d..6eefd62347 100644 --- a/packages/magento-customer/hooks/useSignInForm.ts +++ b/packages/magento-customer/hooks/useSignInForm.ts @@ -1,4 +1,5 @@ import { useApolloClient } from '@graphcommerce/graphql' +import { setCssFlag } from '@graphcommerce/next-ui' import { UseFormGraphQlOptions, useFormGqlMutation } from '@graphcommerce/react-hook-form' import { SignInDocument, @@ -38,6 +39,10 @@ export function useSignInForm({ email, ...options }: UseSignInFormProps) { ? options.onBeforeSubmit({ ...values, email }) : { ...values, email } }, + onComplete: (...args) => { + setCssFlag('signed-in', true) + return options.onComplete?.(...args) + }, }, { errorPolicy: 'all' }, ) diff --git a/packages/next-ui/Theme/DarkLightModeThemeProvider.tsx b/packages/next-ui/Theme/DarkLightModeThemeProvider.tsx index c1344e5f22..37c00885a4 100644 --- a/packages/next-ui/Theme/DarkLightModeThemeProvider.tsx +++ b/packages/next-ui/Theme/DarkLightModeThemeProvider.tsx @@ -14,6 +14,7 @@ import { useRouter } from 'next/router' import { createContext, useContext, useEffect, useMemo, useState } from 'react' import { IconSvg } from '../IconSvg' import { iconMoon, iconSun } from '../icons' +import { getCssFlag, setCssFlag } from '../utils/cssFlags' type Mode = 'dark' | 'light' type UserMode = 'auto' | Mode @@ -22,6 +23,7 @@ type ColorModeContext = { userMode: UserMode browserMode: Mode currentMode: Mode + isSingleMode: boolean toggle: () => void } @@ -29,48 +31,59 @@ export const colorModeContext = createContext(undefined as unknown as ColorModeC colorModeContext.displayName = 'ColorModeContext' type ThemeProviderProps = { - // eslint-disable-next-line react/no-unused-prop-types - light: Theme - // eslint-disable-next-line react/no-unused-prop-types - dark: Theme - children: React.ReactNode -} + ssrMode?: Mode + listenToBrowser?: boolean +} & ( + | { light: Theme; dark?: undefined } + | { light?: undefined; dark: Theme } + | { light: Theme; dark: Theme } +) /** * Wrapper around `import { ThemeProvider } from '@mui/material'` * * The multi DarkLightModeThemeProvider allows switching between light and dark mode based on URL * and on user input. - * - * If you _just_ wan't a single theme, use the import { ThemeProvider } from '@mui/material' instead. */ export function DarkLightModeThemeProvider(props: ThemeProviderProps) { - const { children, light, dark } = props + const { children, light, dark, ssrMode = 'light', listenToBrowser = true } = props + const [configuredMode, setConfiguredMode] = useState(listenToBrowser ? 'auto' : ssrMode) + const setThemeMode = (mode: UserMode) => { + setConfiguredMode(mode) + setCssFlag('color-scheme', mode) + } - // todo: Save this in local storage - const [userMode, setUserMode] = useState('auto') const browserMode: Mode = useMediaQuery('(prefers-color-scheme: dark)') ? 'dark' : 'light' // If the user has set a mode, use that. Otherwise, use the browser mode. - const currentMode = userMode === 'auto' ? browserMode : userMode - const theme = currentMode === 'light' ? light : dark + const currentMode = configuredMode === 'auto' ? browserMode : configuredMode + + let theme: Theme = light || dark // Default + if (light && currentMode === 'light') theme = light + else if (dark) theme = dark + + useEffect(() => { + const flag = getCssFlag('color-scheme') as Mode + if (flag) setConfiguredMode(flag) + }, [setConfiguredMode]) // If a URL parameter is present, switch from auto to light or dark mode const { asPath } = useRouter() useEffect(() => { - if (asPath.includes('darkmode')) setUserMode('dark') + if (asPath.includes('darkmode')) setConfiguredMode('dark') }, [asPath]) // Create the context const colorContext: ColorModeContext = useMemo( () => ({ browserMode, - userMode, + userMode: configuredMode, currentMode, - toggle: () => setUserMode(currentMode === 'light' ? 'dark' : 'light'), + isSingleMode: !light || !dark, + toggle: () => setThemeMode(currentMode === 'light' ? 'dark' : 'light'), }), - [browserMode, currentMode, userMode], + [browserMode, configuredMode, currentMode, light, dark], ) return ( @@ -85,7 +98,12 @@ export function useColorMode() { } export function DarkLightModeToggleFab(props: Omit) { - const { currentMode, toggle } = useColorMode() + const { currentMode, isSingleMode, toggle } = useColorMode() + + if (isSingleMode) { + return null + } + return ( @@ -100,7 +118,11 @@ export function DarkLightModeToggleFab(props: Omit) { */ export function DarkLightModeMenuSecondaryItem(props: ListItemButtonProps) { const { sx = [] } = props - const { currentMode, toggle } = useColorMode() + const { currentMode, isSingleMode, toggle } = useColorMode() + + if (isSingleMode) { + return null + } return ( diff --git a/packages/next-ui/index.ts b/packages/next-ui/index.ts index 12f5d0dcd6..eb33fa9244 100644 --- a/packages/next-ui/index.ts +++ b/packages/next-ui/index.ts @@ -61,3 +61,4 @@ export * from './icons' export * from './utils/cookie' export * from './utils/sitemap' export * from './utils/robots' +export * from './utils/cssFlags' diff --git a/packages/next-ui/utils/cssFlags.tsx b/packages/next-ui/utils/cssFlags.tsx new file mode 100644 index 0000000000..a91332c62f --- /dev/null +++ b/packages/next-ui/utils/cssFlags.tsx @@ -0,0 +1,76 @@ +export const FLAGS_STORAGE_KEY = 'gc-flags' +export const FLAG_PREFIX = 'data' + +export function getCssFlagsInitScript() { + return ( +