From 00184e674ff469e4ccf25d38f05f73f5159844c7 Mon Sep 17 00:00:00 2001 From: cond0r <1243434+cond0r@users.noreply.github.com> Date: Tue, 29 Nov 2022 20:07:16 +0200 Subject: [PATCH] Footer theme switcher --- site/components/common/Footer/Footer.tsx | 17 +++- site/components/icons/System.tsx | 19 +++++ site/components/icons/index.ts | 1 + .../components/ui/ThemeSwitcher/ThemeIcon.tsx | 22 ++++++ .../ui/ThemeSwitcher/ThemeSwitcher.tsx | 77 +++++++++++++++++++ site/components/ui/ThemeSwitcher/index.ts | 1 + 6 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 site/components/icons/System.tsx create mode 100644 site/components/ui/ThemeSwitcher/ThemeIcon.tsx create mode 100644 site/components/ui/ThemeSwitcher/ThemeSwitcher.tsx create mode 100644 site/components/ui/ThemeSwitcher/index.ts diff --git a/site/components/common/Footer/Footer.tsx b/site/components/common/Footer/Footer.tsx index 18e9b4027..b2447f0cd 100644 --- a/site/components/common/Footer/Footer.tsx +++ b/site/components/common/Footer/Footer.tsx @@ -1,6 +1,7 @@ import { FC } from 'react' import cn from 'clsx' import Link from 'next/link' +import dynamic from 'next/dynamic' import { useRouter } from 'next/router' import type { Page } from '@commerce/types/page' import getSlug from '@lib/get-slug' @@ -9,6 +10,13 @@ import { Logo, Container } from '@components/ui' import { I18nWidget } from '@components/common' 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 { className?: string children?: any @@ -40,7 +48,7 @@ const Footer: FC<Props> = ({ className, pages }) => { </a> </Link> </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"> {[...links, ...sitePages].map((page) => ( <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 className="col-span-1 lg:col-span-2 flex items-start lg:justify-end text-primary"> - <div className="flex space-x-6 items-center h-10"> + <div className="col-span-1 lg:col-span-3 flex items-start lg:justify-end text-primary"> + <div className="flex space-x-4 items-center h-10"> + <ThemeSwitcher /> + <I18nWidget /> <a className={s.link} aria-label="Github Repository" @@ -62,7 +72,6 @@ const Footer: FC<Props> = ({ className, pages }) => { > <Github /> </a> - <I18nWidget /> </div> </div> </div> diff --git a/site/components/icons/System.tsx b/site/components/icons/System.tsx new file mode 100644 index 000000000..bb7367c7a --- /dev/null +++ b/site/components/icons/System.tsx @@ -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 diff --git a/site/components/icons/index.ts b/site/components/icons/index.ts index 12e0cc202..72a65494c 100644 --- a/site/components/icons/index.ts +++ b/site/components/icons/index.ts @@ -11,6 +11,7 @@ export { default as Cross } from './Cross' export { default as Minus } from './Minus' export { default as Check } from './Check' export { default as Github } from './Github' +export { default as System } from './System' export { default as Vercel } from './Vercel' export { default as MapPin } from './MapPin' export { default as ArrowLeft } from './ArrowLeft' diff --git a/site/components/ui/ThemeSwitcher/ThemeIcon.tsx b/site/components/ui/ThemeSwitcher/ThemeIcon.tsx new file mode 100644 index 000000000..ae998602c --- /dev/null +++ b/site/components/ui/ThemeSwitcher/ThemeIcon.tsx @@ -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 diff --git a/site/components/ui/ThemeSwitcher/ThemeSwitcher.tsx b/site/components/ui/ThemeSwitcher/ThemeSwitcher.tsx new file mode 100644 index 000000000..55671afc9 --- /dev/null +++ b/site/components/ui/ThemeSwitcher/ThemeSwitcher.tsx @@ -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 diff --git a/site/components/ui/ThemeSwitcher/index.ts b/site/components/ui/ThemeSwitcher/index.ts new file mode 100644 index 000000000..2b4931135 --- /dev/null +++ b/site/components/ui/ThemeSwitcher/index.ts @@ -0,0 +1 @@ +export { default } from './ThemeSwitcher'