mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 20:26:49 +00:00
Merge branch 'master' of https://github.com/okbel/e-comm-example into image-component
This commit is contained in:
@@ -12,7 +12,7 @@ const Avatar: FC<Props> = ({}) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="inline-block h-8 w-8 rounded-full border-2 border-accents-2"
|
||||
className="inline-block h-8 w-8 rounded-full border-2 border-primary hover:border-secondary transition linear-out duration-100"
|
||||
style={{
|
||||
backgroundImage: `linear-gradient(140deg, ${bg[0]}, ${bg[1]} 100%)`,
|
||||
}}
|
||||
|
@@ -1,9 +0,0 @@
|
||||
.separator {
|
||||
@apply mx-3 bg-secondary;
|
||||
width: 1px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.separator:before {
|
||||
content: '';
|
||||
}
|
@@ -1,6 +1,5 @@
|
||||
import cn from 'classnames'
|
||||
import { FC } from 'react'
|
||||
import s from './Featurebar.module.css'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
@@ -18,15 +17,16 @@ const Featurebar: FC<Props> = ({
|
||||
hide,
|
||||
}) => {
|
||||
const rootClassName = cn(
|
||||
'transition-transform transform duration-500 ease-out p-6 bg-primary text-base text-sm md:flex flex-row justify-center items-center font-medium fixed bottom-0 w-full z-10',
|
||||
'transition-transform transform duration-500 text-center ease-out p-6 bg-primary text-base text-sm md:flex md:text-left flex-row justify-center items-center font-medium fixed bottom-0 w-full z-10',
|
||||
{ 'translate-y-full': hide },
|
||||
className
|
||||
)
|
||||
return (
|
||||
<div className={rootClassName}>
|
||||
<span>{title}</span>
|
||||
<span className={s.separator} />
|
||||
<span>{description}</span>
|
||||
<span className="block md:inline">{title}</span>
|
||||
<span className="block mb-6 md:inline md:mb-0 md:ml-2">
|
||||
{description}
|
||||
</span>
|
||||
{action && action}
|
||||
</div>
|
||||
)
|
||||
|
@@ -3,7 +3,8 @@ import cn from 'classnames'
|
||||
import Link from 'next/link'
|
||||
import type { Page } from '@lib/bigcommerce/api/operations/get-all-pages'
|
||||
import getSlug from '@utils/get-slug'
|
||||
import { Logo } from '@components/ui'
|
||||
import { Logo, Container } from '@components/ui'
|
||||
import { Github, DoubleChevron } from '@components/icon'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
@@ -14,53 +15,100 @@ interface Props {
|
||||
const LEGAL_PAGES = ['terms-of-use', 'shipping-returns', 'privacy-policy']
|
||||
|
||||
const Footer: FC<Props> = ({ className, pages }) => {
|
||||
const rootClassName = cn(
|
||||
'flex flex-col p-6 md:py-12 md:flex-row flex-wrap max-w-screen-xl m-auto',
|
||||
className
|
||||
)
|
||||
const rootClassName = cn(className)
|
||||
const { sitePages, legalPages } = getPages(pages)
|
||||
|
||||
return (
|
||||
<div className="bg-black text-white">
|
||||
<footer className={rootClassName}>
|
||||
<Link href="/">
|
||||
<a className="flex flex-initial items-center md:items-start font-bold md:mr-24">
|
||||
<span className="rounded-full border border-gray-700 mr-2">
|
||||
<Logo />
|
||||
</span>
|
||||
<span>ACME</span>
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<ul className="flex flex-initial flex-col divide-y divide-gray-700 md:divide-y-0 my-12 md:my-0 md:flex-1">
|
||||
{sitePages.map((page) => (
|
||||
<li key={page.url} className="py-3 md:py-0 md:pb-4">
|
||||
<Link href={page.url!}>
|
||||
<a className="text-gray-400 hover:text-white transition ease-in-out duration-100">
|
||||
{page.name}
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<ul className="flex flex-initial flex-col divide-y divide-gray-700 md:divide-y-0 my-12 md:my-0 md:flex-1">
|
||||
{legalPages.map((page) => (
|
||||
<li key={page.url} className="py-3 md:py-0 md:pb-4">
|
||||
<Link href={page.url!}>
|
||||
<a className="text-gray-400 hover:text-white transition ease-in-out duration-100">
|
||||
{page.name}
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<small className="text-base">
|
||||
© 2020 ACME, Inc. All rights reserved.
|
||||
</small>
|
||||
</footer>
|
||||
</div>
|
||||
<footer className={rootClassName}>
|
||||
<Container>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8 border-b border-accents-2 py-12 text-primary bg-primary">
|
||||
<div className="col-span-1 lg:col-span-2">
|
||||
<Link href="/">
|
||||
<a className="flex flex-initial items-center font-bold md:mr-24">
|
||||
<span className="rounded-full border border-gray-700 mr-2">
|
||||
<Logo />
|
||||
</span>
|
||||
<span>ACME</span>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="col-span-1 lg:col-span-2">
|
||||
<ul className="flex flex-initial flex-col md:flex-1">
|
||||
<li className="py-3 md:py-0 md:pb-4">
|
||||
<Link href="/">
|
||||
<a className="text-gray-400 hover:text-white transition ease-in-out duration-100">
|
||||
Home
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li className="py-3 md:py-0 md:pb-4">
|
||||
<Link href="/">
|
||||
<a className="text-gray-400 hover:text-white transition ease-in-out duration-100">
|
||||
Careers
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li className="py-3 md:py-0 md:pb-4">
|
||||
<Link href="/blog">
|
||||
<a className="text-gray-400 hover:text-white transition ease-in-out duration-100">
|
||||
Blog
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
{sitePages.map((page) => (
|
||||
<li key={page.url} className="py-3 md:py-0 md:pb-4">
|
||||
<Link href={page.url!}>
|
||||
<a className="text-gray-400 hover:text-white transition ease-in-out duration-100">
|
||||
{page.name}
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="col-span-1 lg:col-span-2">
|
||||
<ul className="flex flex-initial flex-col md:flex-1">
|
||||
{legalPages.map((page) => (
|
||||
<li key={page.url} className="py-3 md:py-0 md:pb-4">
|
||||
<Link href={page.url!}>
|
||||
<a className="text-gray-400 hover:text-white transition ease-in-out duration-100">
|
||||
{page.name}
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="col-span-1 lg:col-span-6 flex items-start lg:justify-end text-primary">
|
||||
<div className="flex space-x-6 items-center h-10">
|
||||
<Github />
|
||||
<div className="h-10 px-2 rounded-md border border-accents-2 flex items-center space-x-2 justify-center">
|
||||
<img className="" src="/flag-us.png" />
|
||||
<span>English</span>
|
||||
<span className="">
|
||||
<DoubleChevron />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-12 flex flex-col md:flex-row justify-between items-center space-y-4">
|
||||
<div>
|
||||
<span>© 2020 ACME, Inc. All rights reserved.</span>
|
||||
</div>
|
||||
<div className="flex items-center text-accents-4">
|
||||
<span>Crafted by</span>
|
||||
<a href="https://vercel.com">
|
||||
<img
|
||||
src="/vercel.png"
|
||||
alt="Vercel.com Logo"
|
||||
className="inline-block h-6 ml-4"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -33,12 +33,12 @@ const Layout: FC<Props> = ({ children, pageProps }) => {
|
||||
<Featurebar
|
||||
title="This site uses cookies to improve your experience."
|
||||
description="By clicking, you agree to our Privacy Policy."
|
||||
hide={acceptedCookies}
|
||||
action={
|
||||
<Button className="mx-5" onClick={() => setAcceptedCookies(true)}>
|
||||
Accept cookies
|
||||
</Button>
|
||||
}
|
||||
className={cn({ ['translate-y-full']: acceptedCookies })}
|
||||
/>
|
||||
</div>
|
||||
</CommerceProvider>
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import s from './Navbar.module.css'
|
||||
import { FC } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { Logo } from '@components/ui'
|
||||
import { Searchbar, UserNav } from '@components/core'
|
||||
interface Props {
|
||||
|
16
components/core/UserNav/DropdownMenu.module.css
Normal file
16
components/core/UserNav/DropdownMenu.module.css
Normal file
@@ -0,0 +1,16 @@
|
||||
.dropdownMenu {
|
||||
@apply fixed right-0 mt-7 origin-top-right outline-none bg-primary z-40 w-full h-full;
|
||||
|
||||
@screen lg {
|
||||
@apply absolute border border-accents-1 shadow-lg w-56 h-auto;
|
||||
}
|
||||
|
||||
& .link {
|
||||
@apply flex cursor-pointer px-6 py-3 block hover:bg-accents-1 transition ease-in-out duration-150 text-base leading-6 font-medium text-gray-900 items-center;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
&.off {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
76
components/core/UserNav/DropdownMenu.tsx
Normal file
76
components/core/UserNav/DropdownMenu.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import { FC } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useTheme } from 'next-themes'
|
||||
import cn from 'classnames'
|
||||
import s from './DropdownMenu.module.css'
|
||||
import { Moon, Sun } from '@components/icon'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
import { usePreventScroll } from '@react-aria/overlays'
|
||||
interface DropdownMenuProps {
|
||||
onClose: () => void
|
||||
open: boolean
|
||||
}
|
||||
|
||||
const DropdownMenu: FC<DropdownMenuProps> = ({
|
||||
onClose,
|
||||
children,
|
||||
open = false,
|
||||
...props
|
||||
}) => {
|
||||
const { theme, setTheme } = useTheme()
|
||||
|
||||
usePreventScroll({
|
||||
isDisabled: !open,
|
||||
})
|
||||
|
||||
return (
|
||||
<Transition
|
||||
show={open}
|
||||
enter="transition ease-out duration-100 z-20"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className={s.dropdownMenu}>
|
||||
<Menu.Item>
|
||||
{({ active }) => <a className={s.link}>My Purchases</a>}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
{({ active }) => <a className={s.link}>My Account</a>}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<a
|
||||
className={cn(s.link, 'justify-between')}
|
||||
onClick={() =>
|
||||
theme === 'dark' ? setTheme('light') : setTheme('dark')
|
||||
}
|
||||
>
|
||||
<div>
|
||||
Theme: <strong>{theme}</strong>{' '}
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
{theme == 'dark' ? (
|
||||
<Moon width={20} height={20} />
|
||||
) : (
|
||||
<Sun width="20" height={20} />
|
||||
)}
|
||||
</div>
|
||||
</a>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<a className={cn(s.link, 'border-t border-accents-2 mt-4')}>
|
||||
Logout
|
||||
</a>
|
||||
)}
|
||||
</Menu.Item>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
)
|
||||
}
|
||||
|
||||
export default DropdownMenu
|
@@ -13,35 +13,14 @@
|
||||
@apply mr-6 cursor-pointer relative transition ease-in-out duration-100 text-base flex items-center;
|
||||
|
||||
&:hover {
|
||||
@apply text-accents-8;
|
||||
@apply text-accents-8 transition scale-110 duration-100;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
@apply mr-0;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdownMenu {
|
||||
@apply bg-primary fixed right-0 z-50 w-full h-full;
|
||||
|
||||
@screen lg {
|
||||
@apply absolute mt-3 right-0 w-screen;
|
||||
max-width: 160px;
|
||||
}
|
||||
|
||||
& .dropdownMenuContainer {
|
||||
@apply flex-col py-6 bg-primary h-full justify-around;
|
||||
|
||||
@screen lg {
|
||||
@apply border border-accents-1 shadow-lg py-2 h-auto;
|
||||
}
|
||||
}
|
||||
|
||||
& .link {
|
||||
@apply cursor-pointer px-6 py-3 block space-y-1 hover:bg-accents-1 transition ease-in-out duration-150 text-base leading-6 font-medium text-gray-900;
|
||||
}
|
||||
|
||||
&.off {
|
||||
@apply hidden;
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
@@ -1,18 +1,12 @@
|
||||
import Link from 'next/link'
|
||||
import cn from 'classnames'
|
||||
import s from './UserNav.module.css'
|
||||
import { FC, useState, useRef, useCallback } from 'react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { FC, useRef } from 'react'
|
||||
import { Avatar } from '@components/core'
|
||||
import { Heart, Bag } from '@components/icon'
|
||||
import { useUI } from '@components/ui/context'
|
||||
import { FocusScope } from '@react-aria/focus'
|
||||
|
||||
import {
|
||||
useOverlay,
|
||||
DismissButton,
|
||||
usePreventScroll,
|
||||
} from '@react-aria/overlays'
|
||||
import DropdownMenu from './DropdownMenu'
|
||||
import { Menu } from '@headlessui/react'
|
||||
import useCart from '@lib/bigcommerce/cart/use-cart'
|
||||
|
||||
interface Props {
|
||||
@@ -25,12 +19,18 @@ const countItems = (count: number, items: any[]) =>
|
||||
|
||||
const UserNav: FC<Props> = ({ className, children, ...props }) => {
|
||||
const { data } = useCart()
|
||||
const { openSidebar, closeSidebar, displaySidebar } = useUI()
|
||||
const [displayDropdown, setDisplayDropdown] = useState(false)
|
||||
const {
|
||||
openSidebar,
|
||||
closeSidebar,
|
||||
displaySidebar,
|
||||
displayDropdown,
|
||||
openDropdown,
|
||||
closeDropdown,
|
||||
} = useUI()
|
||||
|
||||
const itemsCount = Object.values(data?.line_items ?? {}).reduce(countItems, 0)
|
||||
let ref = useRef() as React.MutableRefObject<HTMLInputElement>
|
||||
|
||||
const toggleDropdown = () => setDisplayDropdown((v) => !v)
|
||||
return (
|
||||
<nav className={cn(s.root, className)}>
|
||||
<div className={s.mainContainer}>
|
||||
@@ -51,71 +51,22 @@ const UserNav: FC<Props> = ({ className, children, ...props }) => {
|
||||
<Heart />
|
||||
</li>
|
||||
</Link>
|
||||
<li className={s.item} onClick={() => toggleDropdown()}>
|
||||
<Avatar />
|
||||
<li className={s.item}>
|
||||
<Menu>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Menu.Button className="inline-flex justify-center rounded-full">
|
||||
<Avatar />
|
||||
</Menu.Button>
|
||||
<DropdownMenu onClose={closeDropdown} open={open} />
|
||||
</>
|
||||
)}
|
||||
</Menu>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<DismissButton onDismiss={() => setDisplayDropdown(false)} />
|
||||
{displayDropdown && (
|
||||
<DropdownMenu
|
||||
onClose={() => setDisplayDropdown(false)}
|
||||
innerRef={ref}
|
||||
/>
|
||||
)}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
interface DropdownMenuProps {
|
||||
onClose: () => void
|
||||
innerRef: React.MutableRefObject<HTMLInputElement>
|
||||
}
|
||||
|
||||
const DropdownMenu: FC<DropdownMenuProps> = ({
|
||||
onClose,
|
||||
children,
|
||||
innerRef,
|
||||
...props
|
||||
}) => {
|
||||
const { theme, setTheme } = useTheme()
|
||||
|
||||
let { overlayProps } = useOverlay(
|
||||
{
|
||||
onClose: onClose,
|
||||
isOpen: true,
|
||||
},
|
||||
innerRef
|
||||
)
|
||||
|
||||
usePreventScroll()
|
||||
return (
|
||||
<FocusScope contain restoreFocus autoFocus>
|
||||
<div className={cn(s.dropdownMenu)} ref={innerRef} {...overlayProps}>
|
||||
<nav className={s.dropdownMenuContainer}>
|
||||
<Link href="#">
|
||||
<a className={s.link}>My Purchases</a>
|
||||
</Link>
|
||||
<Link href="#">
|
||||
<a className={s.link}>My Account</a>
|
||||
</Link>
|
||||
<a
|
||||
className={s.link}
|
||||
onClick={() =>
|
||||
theme === 'dark' ? setTheme('light') : setTheme('dark')
|
||||
}
|
||||
>
|
||||
Theme: <strong>{theme}</strong>
|
||||
</a>
|
||||
<Link href="#">
|
||||
<a className={cn(s.link, 'border-t border-accents-2 mt-4')}>
|
||||
Logout
|
||||
</a>
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</FocusScope>
|
||||
)
|
||||
}
|
||||
|
||||
export default UserNav
|
||||
|
22
components/icon/DoubleChevron.tsx
Normal file
22
components/icon/DoubleChevron.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
const DoubleChevron = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M16 8.90482L12 4L8 8.90482M8 15.0952L12 20L16 15.0952"
|
||||
stroke="white"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default DoubleChevron
|
20
components/icon/Github.tsx
Normal file
20
components/icon/Github.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
const Sun = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M12 0C5.37 0 0 5.50583 0 12.3035C0 17.7478 3.435 22.3463 8.205 23.9765C8.805 24.0842 9.03 23.715 9.03 23.3921C9.03 23.0999 9.015 22.131 9.015 21.1005C6 21.6696 5.22 20.347 4.98 19.6549C4.845 19.3012 4.26 18.2092 3.75 17.917C3.33 17.6863 2.73 17.1173 3.735 17.1019C4.68 17.0865 5.355 17.9939 5.58 18.363C6.66 20.2239 8.385 19.701 9.075 19.3781C9.18 18.5783 9.495 18.04 9.84 17.7325C7.17 17.4249 4.38 16.3637 4.38 11.6576C4.38 10.3196 4.845 9.21227 5.61 8.35102C5.49 8.04343 5.07 6.78232 5.73 5.09058C5.73 5.09058 6.735 4.76762 9.03 6.3517C9.99 6.07487 11.01 5.93645 12.03 5.93645C13.05 5.93645 14.07 6.07487 15.03 6.3517C17.325 4.75224 18.33 5.09058 18.33 5.09058C18.99 6.78232 18.57 8.04343 18.45 8.35102C19.215 9.21227 19.68 10.3042 19.68 11.6576C19.68 16.3791 16.875 17.4249 14.205 17.7325C14.64 18.1169 15.015 18.8552 15.015 20.0086C15.015 21.6542 15 22.9768 15 23.3921C15 23.715 15.225 24.0995 15.825 23.9765C18.2072 23.1519 20.2773 21.5822 21.7438 19.4882C23.2103 17.3942 23.9994 14.8814 24 12.3035C24 5.50583 18.63 0 12 0Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default Sun
|
20
components/icon/Moon.tsx
Normal file
20
components/icon/Moon.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
const Moon = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
height="24"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"
|
||||
shape-rendering="geometricPrecision"
|
||||
{...props}
|
||||
>
|
||||
<path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default Moon
|
28
components/icon/Sun.tsx
Normal file
28
components/icon/Sun.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
const Sun = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
height="24"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"
|
||||
shape-rendering="geometricPrecision"
|
||||
{...props}
|
||||
>
|
||||
<circle cx="12" cy="12" r="5" />
|
||||
<path d="M12 1v2" />
|
||||
<path d="M12 21v2" />
|
||||
<path d="M4.22 4.22l1.42 1.42" />
|
||||
<path d="M18.36 18.36l1.42 1.42" />
|
||||
<path d="M1 12h2" />
|
||||
<path d="M21 12h2" />
|
||||
<path d="M4.22 19.78l1.42-1.42" />
|
||||
<path d="M18.36 5.64l1.42-1.42" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default Sun
|
@@ -6,3 +6,7 @@ export { default as ArrowLeft } from './ArrowLeft'
|
||||
export { default as Plus } from './Plus'
|
||||
export { default as Minus } from './Minus'
|
||||
export { default as Check } from './Check'
|
||||
export { default as Sun } from './Sun'
|
||||
export { default as Moon } from './Moon'
|
||||
export { default as Github } from './Github'
|
||||
export { default as DoubleChevron } from './DoubleChevron'
|
||||
|
@@ -2,8 +2,12 @@
|
||||
@apply relative w-full box-border overflow-hidden bg-no-repeat bg-center bg-cover transition ease-linear cursor-pointer;
|
||||
|
||||
&:hover {
|
||||
& .squareBg {
|
||||
@apply scale-75;
|
||||
& .squareBg:before {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
& .product-image {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
& .productTitle > span,
|
||||
@@ -37,6 +41,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
& .product-image {
|
||||
@apply transform transition-transform duration-500;
|
||||
}
|
||||
|
||||
&:nth-child(6n + 1) .squareBg {
|
||||
@apply bg-violet;
|
||||
}
|
||||
@@ -58,16 +66,23 @@
|
||||
.productTitle > span,
|
||||
.productPrice,
|
||||
.wishlistButton {
|
||||
@apply transition ease-in-out duration-300;
|
||||
@apply transition ease-in-out duration-500;
|
||||
}
|
||||
|
||||
.squareBg {
|
||||
@apply transform absolute inset-0 z-0 bg-secondary;
|
||||
@apply transform absolute inset-0 z-0;
|
||||
background-color: #212529;
|
||||
}
|
||||
|
||||
.squareBg:before {
|
||||
@apply transition ease-in-out duration-500 bg-repeat-space w-full h-full block;
|
||||
content: '';
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='48' height='46' viewBox='0 0 48 46' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cline opacity='0.1' x1='9.41421' y1='8' x2='21' y2='19.5858' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cline opacity='0.1' x1='1' y1='-1' x2='17.3848' y2='-1' transform='matrix(-0.707107 0.707107 0.707107 0.707107 40 8)' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cline opacity='0.1' x1='1' y1='-1' x2='17.3848' y2='-1' transform='matrix(0.707107 -0.707107 -0.707107 -0.707107 8 38)' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cline opacity='0.1' x1='38.5858' y1='38' x2='27' y2='26.4142' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A");
|
||||
}
|
||||
|
||||
.simple {
|
||||
& .squareBg {
|
||||
@apply bg-gray-300 !important;
|
||||
@apply bg-accents-0 !important;
|
||||
}
|
||||
|
||||
& .productTitle {
|
||||
@@ -102,5 +117,5 @@
|
||||
}
|
||||
|
||||
.wishlistButton {
|
||||
@apply w-10 h-10 flex items-center justify-center bg-primary text-base font-semibold inline-block text-xs leading-6 cursor-pointer;
|
||||
@apply w-10 h-10 flex ml-auto items-center justify-center bg-primary text-base font-semibold inline-block text-xs leading-6 cursor-pointer;
|
||||
}
|
||||
|
@@ -62,12 +62,14 @@ const ProductCard: FC<Props> = ({
|
||||
<Heart />
|
||||
</div>
|
||||
</div>
|
||||
<Image
|
||||
src={src}
|
||||
width={imgWidth}
|
||||
height={imgHeight}
|
||||
priority={priority}
|
||||
/>
|
||||
<div className="absolute z-10 inset-0 flex items-center justify-center">
|
||||
<Image
|
||||
src={src}
|
||||
width={imgWidth}
|
||||
height={imgHeight}
|
||||
priority={priority}
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
|
@@ -7,7 +7,7 @@
|
||||
}
|
||||
|
||||
.productDisplay {
|
||||
@apply relative flex px-0 pb-0 relative box-border col-span-7;
|
||||
@apply relative flex px-0 pb-0 relative box-border col-span-1 bg-violet;
|
||||
margin-right: -2rem;
|
||||
margin-left: -2rem;
|
||||
min-height: 400px;
|
||||
@@ -17,7 +17,7 @@
|
||||
}
|
||||
|
||||
@screen lg {
|
||||
@apply mx-0;
|
||||
@apply mx-0 col-span-7;
|
||||
min-height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -34,7 +34,7 @@
|
||||
}
|
||||
|
||||
.nameBox {
|
||||
@apply absolute top-6 left-0 z-50;
|
||||
@apply absolute top-6 left-10 z-10;
|
||||
|
||||
& .name {
|
||||
@apply px-6 py-2 bg-primary text-primary font-bold;
|
||||
@@ -49,15 +49,16 @@
|
||||
@screen md {
|
||||
& .name,
|
||||
& .price {
|
||||
@apply bg-violet text-white;
|
||||
@apply bg-violet-light text-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
@apply flex flex-col col-span-5;
|
||||
@apply flex flex-col col-span-1;
|
||||
|
||||
@screen lg {
|
||||
@apply col-span-5;
|
||||
padding-top: 5rem;
|
||||
}
|
||||
}
|
||||
@@ -75,13 +76,8 @@
|
||||
}
|
||||
|
||||
@screen lg {
|
||||
height: 150%;
|
||||
margin-top: -10%;
|
||||
}
|
||||
|
||||
@screen xl {
|
||||
height: 170%;
|
||||
margin-top: -19%;
|
||||
height: 100%;
|
||||
margin-top: -8%;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,17 +1,17 @@
|
||||
import { FC, useState, useEffect } from 'react'
|
||||
import cn from 'classnames'
|
||||
import Image from 'next/image'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import type { ProductNode } from '@lib/bigcommerce/api/operations/get-product'
|
||||
import useAddItem from '@lib/bigcommerce/cart/use-add-item'
|
||||
import bcImageSrc from '@lib/bc-image-src'
|
||||
import getPathname from '@lib/get-pathname'
|
||||
import s from './ProductView.module.css'
|
||||
import { FC, useState, useEffect } from 'react'
|
||||
import { useUI } from '@components/ui/context'
|
||||
import { Button, Container } from '@components/ui'
|
||||
import { Swatch, ProductSlider } from '@components/product'
|
||||
import { getProductOptions } from '../helpers'
|
||||
import s from './ProductView.module.css'
|
||||
import getPathname from '@lib/get-pathname'
|
||||
import useAddItem from '@lib/bigcommerce/cart/use-add-item'
|
||||
import { isDesktop } from '@lib/browser'
|
||||
import type { ProductNode } from '@lib/bigcommerce/api/operations/get-product'
|
||||
import { getProductOptions } from '../helpers'
|
||||
import bcImageSrc from '@lib/bc-image-src'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
@@ -73,7 +73,6 @@ const ProductView: FC<Props> = ({ product, className }) => {
|
||||
/>
|
||||
<div className={cn(s.root, 'fit')}>
|
||||
<div className={cn(s.productDisplay, 'fit')}>
|
||||
<div className={s.squareBg}></div>
|
||||
<div className={s.nameBox}>
|
||||
<h1 className={s.name}>{product.name}</h1>
|
||||
<div className={s.price}>
|
||||
@@ -138,7 +137,7 @@ const ProductView: FC<Props> = ({ product, className }) => {
|
||||
))}
|
||||
<div className="pb-12">
|
||||
<div
|
||||
className="pb-14 break-words"
|
||||
className="pb-14 break-words w-full"
|
||||
dangerouslySetInnerHTML={{ __html: product.description }}
|
||||
/>
|
||||
<Button
|
||||
|
@@ -8,7 +8,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const Container: FC<Props> = ({ children, className, el = 'div' }) => {
|
||||
const rootClassName = cn('mx-auto max-w-7xl px-6', className)
|
||||
const rootClassName = cn('mx-auto max-w-8xl px-12', className)
|
||||
|
||||
let Component: React.ComponentType<React.HTMLAttributes<
|
||||
HTMLDivElement
|
||||
|
@@ -1,8 +1,8 @@
|
||||
.root {
|
||||
@apply fixed bg-black flex items-center inset-0 z-50 justify-center;
|
||||
@apply fixed bg-primary text-primary flex items-center inset-0 z-50 justify-center;
|
||||
background-color: rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
|
||||
.modal {
|
||||
@apply bg-white p-12;
|
||||
@apply bg-primary p-12 border border-accents-2;
|
||||
}
|
||||
|
@@ -2,13 +2,7 @@ import cn from 'classnames'
|
||||
import { FC, useRef } from 'react'
|
||||
import s from './Modal.module.css'
|
||||
import { useDialog } from '@react-aria/dialog'
|
||||
import {
|
||||
useOverlay,
|
||||
usePreventScroll,
|
||||
useModal,
|
||||
OverlayProvider,
|
||||
OverlayContainer,
|
||||
} from '@react-aria/overlays'
|
||||
import { useOverlay, usePreventScroll, useModal } from '@react-aria/overlays'
|
||||
import { FocusScope } from '@react-aria/focus'
|
||||
|
||||
interface Props {
|
||||
@@ -27,11 +21,14 @@ const Modal: FC<Props> = ({
|
||||
}) => {
|
||||
const rootClassName = cn(s.root, className)
|
||||
let ref = useRef() as React.MutableRefObject<HTMLInputElement>
|
||||
usePreventScroll()
|
||||
let { modalProps } = useModal()
|
||||
let { overlayProps } = useOverlay(props, ref)
|
||||
let { dialogProps } = useDialog(props, ref)
|
||||
|
||||
usePreventScroll({
|
||||
isDisabled: !show,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={rootClassName}>
|
||||
<FocusScope contain restoreFocus autoFocus>
|
||||
|
@@ -72,7 +72,7 @@ const Sidebar: FC<Props> = ({ className, children, show = true, close }) => {
|
||||
leaveFrom="translate-x-0"
|
||||
leaveTo="translate-x-full"
|
||||
>
|
||||
<div className="h-full w-screen max-w-lg">
|
||||
<div className="h-full w-screen max-w-xl">
|
||||
<div className="h-full flex flex-col text-base bg-accents-1 shadow-xl overflow-y-auto">
|
||||
{children}
|
||||
</div>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
.skeleton {
|
||||
@apply block rounded;
|
||||
@apply block;
|
||||
background-image: linear-gradient(
|
||||
270deg,
|
||||
var(--accents-1),
|
||||
|
@@ -4,10 +4,12 @@ import { SSRProvider, OverlayProvider } from 'react-aria'
|
||||
|
||||
export interface State {
|
||||
displaySidebar: boolean
|
||||
displayDropdown: boolean
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
displaySidebar: false,
|
||||
displayDropdown: false,
|
||||
}
|
||||
|
||||
type Action =
|
||||
@@ -17,21 +19,61 @@ type Action =
|
||||
| {
|
||||
type: 'CLOSE_SIDEBAR'
|
||||
}
|
||||
| {
|
||||
type: 'OPEN_DROPDOWN'
|
||||
}
|
||||
| {
|
||||
type: 'CLOSE_DROPDOWN'
|
||||
}
|
||||
|
||||
export const UIContext = React.createContext<State | any>(initialState)
|
||||
|
||||
UIContext.displayName = 'UIContext'
|
||||
|
||||
function uiReducer(state: State, action: Action) {
|
||||
switch (action.type) {
|
||||
case 'OPEN_SIDEBAR': {
|
||||
return {
|
||||
...state,
|
||||
displaySidebar: true,
|
||||
}
|
||||
}
|
||||
case 'CLOSE_SIDEBAR': {
|
||||
return {
|
||||
...state,
|
||||
displaySidebar: false,
|
||||
}
|
||||
}
|
||||
case 'OPEN_DROPDOWN': {
|
||||
return {
|
||||
...state,
|
||||
displayDropdown: true,
|
||||
}
|
||||
}
|
||||
case 'CLOSE_DROPDOWN': {
|
||||
return {
|
||||
...state,
|
||||
displayDropdown: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const UIProvider: FC = (props) => {
|
||||
const [state, dispatch] = React.useReducer(uiReducer, initialState)
|
||||
|
||||
const openSidebar = () => dispatch({ type: 'OPEN_SIDEBAR' })
|
||||
const closeSidebar = () => dispatch({ type: 'CLOSE_SIDEBAR' })
|
||||
|
||||
const openDropdown = () => dispatch({ type: 'OPEN_DROPDOWN' })
|
||||
const closeDropdown = () => dispatch({ type: 'CLOSE_DROPDOWN' })
|
||||
|
||||
const value = {
|
||||
...state,
|
||||
openSidebar,
|
||||
closeSidebar,
|
||||
openDropdown,
|
||||
closeDropdown,
|
||||
}
|
||||
|
||||
return <UIContext.Provider value={value} {...props} />
|
||||
@@ -45,27 +87,6 @@ export const useUI = () => {
|
||||
return context
|
||||
}
|
||||
|
||||
function uiReducer(state: State, action: Action) {
|
||||
switch (action.type) {
|
||||
case 'OPEN_SIDEBAR': {
|
||||
return !state.displaySidebar
|
||||
? {
|
||||
...state,
|
||||
displaySidebar: true,
|
||||
}
|
||||
: state
|
||||
}
|
||||
case 'CLOSE_SIDEBAR': {
|
||||
return state.displaySidebar
|
||||
? {
|
||||
...state,
|
||||
displaySidebar: false,
|
||||
}
|
||||
: state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const ManagedUIContext: FC = ({ children }) => (
|
||||
<UIProvider>
|
||||
<ThemeProvider>
|
||||
|
Reference in New Issue
Block a user