Footer theme switcher

This commit is contained in:
cond0r 2022-11-29 20:07:16 +02:00
parent 90aa798891
commit 00184e674f
6 changed files with 133 additions and 4 deletions

View File

@ -1,6 +1,7 @@
import { FC } from 'react' import { FC } from 'react'
import cn from 'clsx' import cn from 'clsx'
import Link from 'next/link' import Link from 'next/link'
import dynamic from 'next/dynamic'
import { useRouter } from 'next/router' 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'
@ -9,6 +10,13 @@ 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'
const ThemeSwitcher = dynamic(() => import('@components/ui/ThemeSwitcher'), {
ssr: false,
loading: () => (
<div className="w-24 h-10 rounded-md bg-accent-2 animate-pulse" />
),
})
interface Props { interface Props {
className?: string className?: string
children?: any children?: any
@ -40,7 +48,7 @@ const Footer: FC<Props> = ({ className, pages }) => {
</a> </a>
</Link> </Link>
</div> </div>
<div className="col-span-1 lg:col-span-8"> <div className="col-span-1 lg:col-span-7">
<div className="grid md:grid-rows-4 md:grid-cols-3 md:grid-flow-col"> <div className="grid md:grid-rows-4 md:grid-cols-3 md:grid-flow-col">
{[...links, ...sitePages].map((page) => ( {[...links, ...sitePages].map((page) => (
<span key={page.url} className="py-3 md:py-0 md:pb-4"> <span key={page.url} className="py-3 md:py-0 md:pb-4">
@ -53,8 +61,10 @@ const Footer: FC<Props> = ({ className, pages }) => {
))} ))}
</div> </div>
</div> </div>
<div className="col-span-1 lg:col-span-2 flex items-start lg:justify-end text-primary"> <div className="col-span-1 lg:col-span-3 flex items-start lg:justify-end text-primary">
<div className="flex space-x-6 items-center h-10"> <div className="flex space-x-4 items-center h-10">
<ThemeSwitcher />
<I18nWidget />
<a <a
className={s.link} className={s.link}
aria-label="Github Repository" aria-label="Github Repository"
@ -62,7 +72,6 @@ const Footer: FC<Props> = ({ className, pages }) => {
> >
<Github /> <Github />
</a> </a>
<I18nWidget />
</div> </div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,19 @@
const System = ({ ...props }) => (
<svg
data-testid="geist-icon"
fill="none"
height="16"
shapeRendering="geometricPrecision"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
viewBox="0 0 24 24"
className="text-current"
{...props}
>
<path d="M2 13.381h20M8.66 19.05V22m6.84-2.95V22m-8.955 0h10.932M4 19.05h16a2 2 0 002-2V4a2 2 0 00-2-2H4a2 2 0 00-2 2v13.05a2 2 0 002 2z"></path>
</svg>
)
export default System

View File

@ -11,6 +11,7 @@ export { default as Cross } from './Cross'
export { default as Minus } from './Minus' export { default as Minus } from './Minus'
export { default as Check } from './Check' export { default as Check } from './Check'
export { default as Github } from './Github' export { default as Github } from './Github'
export { default as System } from './System'
export { default as Vercel } from './Vercel' export { default as Vercel } from './Vercel'
export { default as MapPin } from './MapPin' export { default as MapPin } from './MapPin'
export { default as ArrowLeft } from './ArrowLeft' export { default as ArrowLeft } from './ArrowLeft'

View File

@ -0,0 +1,22 @@
import { Moon, Sun, System } from '@components/icons'
interface ThemeIconProps {
theme: string
width: number
height: number
}
const ThemeIcon = ({ theme, ...props }: ThemeIconProps) => {
switch (theme) {
case 'light':
return <Sun {...props} />
case 'dark':
return <Moon {...props} />
default:
return <System {...props} />
}
}
export default ThemeIcon

View File

@ -0,0 +1,77 @@
import { useState } from 'react'
import { useTheme } from 'next-themes'
import { ChevronUp, Cross } from '@components/icons'
import cn from 'clsx'
import ClickOutside from '@lib/click-outside'
import ThemeIcon from './ThemeIcon'
const ThemeSwitcher = () => {
const [display, setDisplay] = useState(false)
const { theme, themes, setTheme } = useTheme()
return (
<ClickOutside active={display} onClick={() => setDisplay(false)}>
<nav className="relative">
<div
className="flex items-center relative"
onClick={() => setDisplay(!display)}
>
<button
className={
'h-10 px-2 rounded-md border border-accent-2 flex items-center justify-center transition-colors ease-linear space-x-2 hover:border-accent-3 hover:shadow-sm'
}
aria-label="Theme Switcher"
>
<ThemeIcon width={20} height={20} theme={theme} />
<span className="capitalize">{theme}</span>
<span className="cursor-pointer">
<ChevronUp
className={cn('transition duration-300', {
['rotate-180']: display,
})}
/>
</span>
</button>
</div>
<div className="absolute top-0 right-0">
{themes.length && display ? (
<div
className={
'fixed shadow-lg right-0 top-12 mt-2 origin-top-right w-full h-full outline-none bg-accent-0 z-40 lg:absolute lg:border lg:border-accent-1 lg:shadow-lg lg:w-56 lg:h-auto'
}
>
<div className="flex flex-row justify-end px-6">
<button
className="md:hidden"
onClick={() => setDisplay(false)}
aria-label="Close panel"
>
<Cross className="h-6 w-6" />
</button>
</div>
<ul>
{themes.map((t: string) => (
<li key={t}>
<button
className="flex w-full capitalize cursor-pointer px-6 py-3 transition ease-in-out duration-150 text-primary leading-6 font-medium items-center hover:bg-accent-1"
role={'link'}
onClick={() => {
setTheme(t)
setDisplay(false)
}}
>
{t}
</button>
</li>
))}
</ul>
</div>
) : null}
</div>
</nav>
</ClickOutside>
)
}
export default ThemeSwitcher

View File

@ -0,0 +1 @@
export { default } from './ThemeSwitcher'