allow users to add their own theme config and logo

This commit is contained in:
Guillaume Bibeau-Laviolette 2022-06-13 14:37:34 -04:00
parent 27fd18ad9f
commit 9479e9bc5b
10 changed files with 77 additions and 24 deletions

View File

@ -98,6 +98,25 @@ For example: Turning `cart` off will disable Cart capabilities.
- Turn `wishlist` on by setting `wishlist` to `true`. - Turn `wishlist` on by setting `wishlist` to `true`.
- Run the app and the wishlist functionality should be back on. - Run the app and the wishlist functionality should be back on.
#### Customizing the theme
You can customize the theme by providing some configuration in `theme.json`
- open `theme.json`
- You'll see a config file like this:
```json
{
"features": {
"logo": false // => switch to true to enable logo
},
"theme": {
"logoSrc": "replace with the path in `public` your logo ex: /assets/logo.svg",
"logoAlt": "Replace with your logo alt text"
}
}
```
### How to create a new provider ### How to create a new provider
Follow our docs for [Adding a new Commerce Provider](packages/commerce/new-provider.md). Follow our docs for [Adding a new Commerce Provider](packages/commerce/new-provider.md).

View File

@ -1,15 +1,11 @@
import { FC, useEffect, useState, useCallback } from 'react' import { FC, useEffect, useState, useCallback } from 'react'
import { validate } from 'email-validator' import { validate } from 'email-validator'
import { useUI } from '@components/ui/context' import { useUI } from '@components/ui/context'
import { AcmeLogo, Button, Input } from '@components/ui' import { Logo, Button, Input } from '@components/ui'
import { useCommerce } from '@framework'
interface Props {} interface Props {}
const ForgotPassword: FC<Props> = () => { const ForgotPassword: FC<Props> = () => {
// @ts-ignore
const { brand = {} } = useCommerce()
const { Logo = AcmeLogo } = brand
// Form State // Form State
const [email, setEmail] = useState('') const [email, setEmail] = useState('')
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)

View File

@ -1,14 +1,11 @@
import { FC, useEffect, useState, useCallback } from 'react' import { FC, useEffect, useState, useCallback } from 'react'
import { AcmeLogo, Button, Input } from '@components/ui' import { Logo, Button, Input } from '@components/ui'
import useLogin from '@framework/auth/use-login' import useLogin from '@framework/auth/use-login'
import { useUI } from '@components/ui/context' import { useUI } from '@components/ui/context'
import { validate } from 'email-validator' import { validate } from 'email-validator'
import { useCommerce } from '@framework' import { useCommerce } from '@framework'
const LoginView: React.FC = () => { const LoginView: React.FC = () => {
// @ts-ignore
const { brand = {} } = useCommerce()
const { Logo = AcmeLogo } = brand
// Form State // Form State
const [email, setEmail] = useState('') const [email, setEmail] = useState('')
const [password, setPassword] = useState('') const [password, setPassword] = useState('')

View File

@ -2,15 +2,12 @@ import { FC, useEffect, useState, useCallback } from 'react'
import { validate } from 'email-validator' import { validate } from 'email-validator'
import { Info } from '@components/icons' import { Info } from '@components/icons'
import { useUI } from '@components/ui/context' import { useUI } from '@components/ui/context'
import { AcmeLogo, Button, Input } from '@components/ui' import { Button, Input, Logo } from '@components/ui'
import useSignup from '@framework/auth/use-signup' import useSignup from '@framework/auth/use-signup'
interface Props {} interface Props {}
const SignUpView: FC<Props> = () => { const SignUpView: FC<Props> = () => {
// @ts-ignore
const { brand = {} } = useCommerce()
const { Logo = AcmeLogo } = brand
// Form State // Form State
const [email, setEmail] = useState('') const [email, setEmail] = useState('')
const [password, setPassword] = useState('') const [password, setPassword] = useState('')

View File

@ -5,7 +5,7 @@ import { useRouter } from 'next/router'
import type { Page } from '@commerce/types/page' import type { Page } from '@commerce/types/page'
import getSlug from '@lib/get-slug' import getSlug from '@lib/get-slug'
import { Github, Vercel } from '@components/icons' import { Github, Vercel } from '@components/icons'
import { AcmeLogo, Container } from '@components/ui' import { Logo, Container } from '@components/ui'
import { I18nWidget } from '@components/common' import { I18nWidget } from '@components/common'
import s from './Footer.module.css' import s from './Footer.module.css'
import { useCommerce } from '@framework' import { useCommerce } from '@framework'
@ -26,9 +26,6 @@ const links = [
const Footer: FC<Props> = ({ className, pages }) => { const Footer: FC<Props> = ({ className, pages }) => {
const { sitePages } = usePages(pages) const { sitePages } = usePages(pages)
const rootClassName = cn(s.root, className) const rootClassName = cn(s.root, className)
// @ts-ignore
const { brand = {} } = useCommerce()
const { Logo = AcmeLogo } = brand
return ( return (
<footer className={rootClassName}> <footer className={rootClassName}>

View File

@ -2,7 +2,7 @@ import { FC } from 'react'
import Link from 'next/link' import Link from 'next/link'
import s from './Navbar.module.css' import s from './Navbar.module.css'
import NavbarRoot from './NavbarRoot' import NavbarRoot from './NavbarRoot'
import { AcmeLogo, Container } from '@components/ui' import { Logo, Container } from '@components/ui'
import { Searchbar, UserNav } from '@components/common' import { Searchbar, UserNav } from '@components/common'
import { useCommerce } from '@framework' import { useCommerce } from '@framework'
@ -16,9 +16,6 @@ interface NavbarProps {
} }
const Navbar: FC<NavbarProps> = ({ links }) => { const Navbar: FC<NavbarProps> = ({ links }) => {
// @ts-ignore
const { brand = {} } = useCommerce()
const { Logo = AcmeLogo } = brand
return ( return (
<NavbarRoot> <NavbarRoot>
<Container clean className="mx-auto max-w-8xl px-6"> <Container clean className="mx-auto max-w-8xl px-6">

View File

@ -1,4 +1,8 @@
const AcmeLogo = ({ className = '', ...props }) => ( import { useCommerce } from '@framework'
import Image from 'next/image'
import { useUI } from '../context'
const DefaultLogo = ({ className = '', ...props }) => (
<svg <svg
width="32" width="32"
height="32" height="32"
@ -18,4 +22,26 @@ const AcmeLogo = ({ className = '', ...props }) => (
</svg> </svg>
) )
export default AcmeLogo const Logo = ({ className = '', ...props }) => {
const { theme } = useUI()
console.log(theme)
// @ts-ignore
const { brand = {} } = useCommerce()
const { Logo = DefaultLogo } = brand
if (theme.logoSrc && theme.logoAlt) {
return (
<Image
src={theme.logoSrc}
alt={theme.logoAlt}
{...props}
height={32}
width={32}
/>
)
}
return <Logo />
}
export default Logo

View File

@ -1,6 +1,8 @@
import React, { FC, useCallback, useMemo } from 'react' import React, { FC, useCallback, useMemo } from 'react'
import { ThemeProvider } from 'next-themes' import { ThemeProvider } from 'next-themes'
import { features, theme } from 'config/theme.json'
export interface State { export interface State {
displaySidebar: boolean displaySidebar: boolean
displayDropdown: boolean displayDropdown: boolean
@ -17,6 +19,9 @@ const initialState = {
modalView: 'LOGIN_VIEW', modalView: 'LOGIN_VIEW',
sidebarView: 'CART_VIEW', sidebarView: 'CART_VIEW',
userAvatar: '', userAvatar: '',
theme: {
...(features.logo && { logoSrc: theme.logoSrc, logoAlt: theme.logoAlt }),
},
} }
type Action = type Action =
@ -198,6 +203,8 @@ export const UIProvider: FC = (props) => {
[state] [state]
) )
console.log(value)
return <UIContext.Provider value={value} {...props} /> return <UIContext.Provider value={value} {...props} />
} }
@ -209,6 +216,14 @@ export const useUI = () => {
return context return context
} }
export const useTheme = () => {
const context = React.useContext(UIContext)
if (context === undefined) {
throw new Error(`useTheme must be used within a UIProvider`)
}
return context.theme
}
export const ManagedUIContext: FC = ({ children }) => ( export const ManagedUIContext: FC = ({ children }) => (
<UIProvider> <UIProvider>
<ThemeProvider>{children}</ThemeProvider> <ThemeProvider>{children}</ThemeProvider>

View File

@ -1,5 +1,5 @@
export { default as Hero } from './Hero' export { default as Hero } from './Hero'
export { default as AcmeLogo } from './Logo' export { default as Logo } from './Logo'
export { default as Grid } from './Grid' export { default as Grid } from './Grid'
export { default as Button } from './Button' export { default as Button } from './Button'
export { default as Sidebar } from './Sidebar' export { default as Sidebar } from './Sidebar'

9
site/config/theme.json Normal file
View File

@ -0,0 +1,9 @@
{
"features": {
"logo": false
},
"theme": {
"logoSrc": "replace with the path in `public` your logo ex: /assets/logo.svg",
"logoAlt": "Replace with your logo alt text"
}
}