mirror of
https://github.com/vercel/commerce.git
synced 2025-07-23 04:36:49 +00:00
Fixes & Updates (#704)
* Adding Dropdown Component * Styling Issues * Wishlist Fix * Fixes for Wishlist View * Hearts now work again * Rollback ts * Removing extra config to disable BigCommerce * Fixes for Wishlist View * Remove transition/animation for mobile * New Updates. * New Updates. * Dropdown fix * Polish * export * export * revert tsconfig Co-authored-by: Luis Alvarez D. <luis@vercel.com> Co-authored-by: Dom Sip <dom@vercel.com> Co-authored-by: Luis Alvarez D. <luis@vercel.com>
This commit is contained in:
@@ -1,12 +1,11 @@
|
||||
import cn from 'clsx'
|
||||
import React, { FC } from 'react'
|
||||
import s from './Layout.module.css'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useRouter } from 'next/router'
|
||||
import { CommerceProvider } from '@framework'
|
||||
import LoginView from '@components/auth/LoginView'
|
||||
import { useUI } from '@components/ui/context'
|
||||
import type { Page } from '@commerce/types/page'
|
||||
import { Navbar, Footer } from '@components/common'
|
||||
import type { Category } from '@commerce/types/site'
|
||||
import ShippingView from '@components/checkout/ShippingView'
|
||||
import CartSidebarView from '@components/cart/CartSidebarView'
|
||||
import { useAcceptCookies } from '@lib/hooks/useAcceptCookies'
|
||||
@@ -14,10 +13,10 @@ import { Sidebar, Button, LoadingDots } from '@components/ui'
|
||||
import PaymentMethodView from '@components/checkout/PaymentMethodView'
|
||||
import CheckoutSidebarView from '@components/checkout/CheckoutSidebarView'
|
||||
import { CheckoutProvider } from '@components/checkout/context'
|
||||
import MenuSidebarView, { Link } from '../UserNav/MenuSidebarView'
|
||||
|
||||
import LoginView from '@components/auth/LoginView'
|
||||
import s from './Layout.module.css'
|
||||
import { MenuSidebarView } from '@components/common/UserNav'
|
||||
import type { Page } from '@commerce/types/page'
|
||||
import type { Category } from '@commerce/types/site'
|
||||
import type { Link as LinkProps } from '../UserNav/MenuSidebarView'
|
||||
|
||||
const Loading = () => (
|
||||
<div className="w-80 h-80 flex items-center text-center justify-center p-3">
|
||||
@@ -29,35 +28,26 @@ const dynamicProps = {
|
||||
loading: Loading,
|
||||
}
|
||||
|
||||
const SignUpView = dynamic(
|
||||
() => import('@components/auth/SignUpView'),
|
||||
{
|
||||
...dynamicProps
|
||||
}
|
||||
)
|
||||
const SignUpView = dynamic(() => import('@components/auth/SignUpView'), {
|
||||
...dynamicProps,
|
||||
})
|
||||
|
||||
const ForgotPassword = dynamic(
|
||||
() => import('@components/auth/ForgotPassword'),
|
||||
{
|
||||
...dynamicProps
|
||||
}
|
||||
)
|
||||
|
||||
const FeatureBar = dynamic(
|
||||
() => import('@components/common/FeatureBar'),
|
||||
{
|
||||
...dynamicProps
|
||||
}
|
||||
)
|
||||
|
||||
const Modal = dynamic(
|
||||
() => import('@components/ui/Modal'),
|
||||
{
|
||||
...dynamicProps,
|
||||
ssr: false
|
||||
}
|
||||
)
|
||||
|
||||
const FeatureBar = dynamic(() => import('@components/common/FeatureBar'), {
|
||||
...dynamicProps,
|
||||
})
|
||||
|
||||
const Modal = dynamic(() => import('@components/ui/Modal'), {
|
||||
...dynamicProps,
|
||||
ssr: false,
|
||||
})
|
||||
|
||||
interface Props {
|
||||
pageProps: {
|
||||
pages?: Page[]
|
||||
@@ -65,7 +55,7 @@ interface Props {
|
||||
}
|
||||
}
|
||||
|
||||
const ModalView: FC<{ modalView: string; closeModal(): any }> = ({
|
||||
const ModalView: React.FC<{ modalView: string; closeModal(): any }> = ({
|
||||
modalView,
|
||||
closeModal,
|
||||
}) => {
|
||||
@@ -78,41 +68,41 @@ const ModalView: FC<{ modalView: string; closeModal(): any }> = ({
|
||||
)
|
||||
}
|
||||
|
||||
const ModalUI: FC = () => {
|
||||
const ModalUI: React.FC = () => {
|
||||
const { displayModal, closeModal, modalView } = useUI()
|
||||
return displayModal ? (
|
||||
<ModalView modalView={modalView} closeModal={closeModal} />
|
||||
) : null
|
||||
}
|
||||
|
||||
const SidebarView: FC<{
|
||||
const SidebarView: React.FC<{
|
||||
sidebarView: string
|
||||
closeSidebar(): any
|
||||
links: Link[]
|
||||
links: LinkProps[]
|
||||
}> = ({ sidebarView, closeSidebar, links }) => {
|
||||
return (
|
||||
<Sidebar onClose={closeSidebar}>
|
||||
{sidebarView === 'MOBILEMENU_VIEW' && <MenuSidebarView links={links} />}
|
||||
{sidebarView === 'CART_VIEW' && <CartSidebarView />}
|
||||
{sidebarView === 'CHECKOUT_VIEW' && <CheckoutSidebarView />}
|
||||
{sidebarView === 'PAYMENT_VIEW' && <PaymentMethodView />}
|
||||
{sidebarView === 'SHIPPING_VIEW' && <ShippingView />}
|
||||
{sidebarView === 'PAYMENT_VIEW' && <PaymentMethodView />}
|
||||
{sidebarView === 'CHECKOUT_VIEW' && <CheckoutSidebarView />}
|
||||
{sidebarView === 'MOBILE_MENU_VIEW' && <MenuSidebarView links={links} />}
|
||||
</Sidebar>
|
||||
)
|
||||
}
|
||||
|
||||
const SidebarUI: FC<{ links: any }> = ({ links }) => {
|
||||
const SidebarUI: React.FC<{ links: LinkProps[] }> = ({ links }) => {
|
||||
const { displaySidebar, closeSidebar, sidebarView } = useUI()
|
||||
return displaySidebar ? (
|
||||
<SidebarView
|
||||
links={links}
|
||||
sidebarView={sidebarView}
|
||||
closeSidebar={closeSidebar}
|
||||
links={links}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
|
||||
const Layout: FC<Props> = ({
|
||||
const Layout: React.FC<Props> = ({
|
||||
children,
|
||||
pageProps: { categories = [], ...pageProps },
|
||||
}) => {
|
||||
|
@@ -16,7 +16,7 @@ interface NavbarProps {
|
||||
|
||||
const Navbar: FC<NavbarProps> = ({ links }) => (
|
||||
<NavbarRoot>
|
||||
<Container>
|
||||
<Container clean className="mx-auto max-w-8xl px-6">
|
||||
<div className={s.nav}>
|
||||
<div className="flex items-center flex-1">
|
||||
<Link href="/">
|
||||
|
@@ -12,8 +12,8 @@ type ComponentProps = { className?: string } & (
|
||||
const SidebarLayout: FC<ComponentProps> = ({
|
||||
children,
|
||||
className,
|
||||
handleClose,
|
||||
handleBack,
|
||||
handleClose,
|
||||
}) => {
|
||||
return (
|
||||
<div className={cn(s.root, className)}>
|
||||
@@ -38,9 +38,8 @@ const SidebarLayout: FC<ComponentProps> = ({
|
||||
<span className="ml-2 text-accent-7 text-xs">Back</span>
|
||||
</button>
|
||||
)}
|
||||
<span className={s.nav}>
|
||||
<UserNav />
|
||||
</span>
|
||||
|
||||
<UserNav />
|
||||
</header>
|
||||
<div className={s.container}>{children}</div>
|
||||
</div>
|
||||
|
@@ -0,0 +1,31 @@
|
||||
.root {
|
||||
@apply inset-0 fixed;
|
||||
left: 72px;
|
||||
z-index: 10;
|
||||
height: 100vh;
|
||||
min-width: 100vw;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@media screen(lg) {
|
||||
.root {
|
||||
@apply static;
|
||||
min-width: inherit;
|
||||
height: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
@apply text-primary flex cursor-pointer px-6 py-3
|
||||
transition ease-in-out duration-150 leading-6
|
||||
font-medium items-center capitalize w-full box-border
|
||||
outline-0;
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
@apply bg-accent-1 outline-none;
|
||||
}
|
||||
|
||||
.link.active {
|
||||
@apply font-bold bg-accent-2;
|
||||
}
|
@@ -0,0 +1,86 @@
|
||||
import cn from 'clsx'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Moon, Sun } from '@components/icons'
|
||||
import s from './CustomerMenuContent.module.css'
|
||||
import useLogout from '@framework/auth/use-logout'
|
||||
import {
|
||||
DropdownContent,
|
||||
DropdownMenuItem,
|
||||
} from '@components/ui/Dropdown/Dropdown'
|
||||
|
||||
const LINKS = [
|
||||
{
|
||||
name: 'My Orders',
|
||||
href: '/orders',
|
||||
},
|
||||
{
|
||||
name: 'My Profile',
|
||||
href: '/profile',
|
||||
},
|
||||
{
|
||||
name: 'My Cart',
|
||||
href: '/cart',
|
||||
},
|
||||
]
|
||||
|
||||
export default function CustomerMenuContent() {
|
||||
const router = useRouter()
|
||||
const logout = useLogout()
|
||||
const { pathname } = useRouter()
|
||||
const { theme, setTheme } = useTheme()
|
||||
|
||||
function handleClick(_: React.MouseEvent<HTMLAnchorElement>, href: string) {
|
||||
router.push(href)
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownContent
|
||||
asChild
|
||||
side="bottom"
|
||||
sideOffset={10}
|
||||
className={s.root}
|
||||
id="CustomerMenuContent"
|
||||
>
|
||||
{LINKS.map(({ name, href }) => (
|
||||
<DropdownMenuItem key={href}>
|
||||
<a
|
||||
className={cn(s.link, {
|
||||
[s.active]: pathname === href,
|
||||
})}
|
||||
onClick={(e) => handleClick(e, href)}
|
||||
>
|
||||
{name}
|
||||
</a>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
<DropdownMenuItem>
|
||||
<a
|
||||
className={cn(s.link, 'justify-between')}
|
||||
onClick={() => {
|
||||
setTheme(theme === 'dark' ? 'light' : '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>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<a
|
||||
className={cn(s.link, 'border-t border-accent-2 mt-4')}
|
||||
onClick={() => logout()}
|
||||
>
|
||||
Logout
|
||||
</a>
|
||||
</DropdownMenuItem>
|
||||
</DropdownContent>
|
||||
)
|
||||
}
|
@@ -0,0 +1 @@
|
||||
export { default } from './CustomerMenuContent'
|
@@ -1,26 +0,0 @@
|
||||
@screen lg {
|
||||
.dropdownMenu {
|
||||
@apply absolute top-10 border border-accent-1 shadow-lg w-56 h-auto;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdownMenu {
|
||||
@apply fixed right-0 mt-2 origin-top-right outline-none bg-primary z-40 w-full h-full;
|
||||
}
|
||||
|
||||
.link {
|
||||
@apply text-primary flex cursor-pointer px-6 py-3 flex transition ease-in-out duration-150 leading-6 font-medium items-center;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
@apply bg-accent-1;
|
||||
}
|
||||
|
||||
.link.active {
|
||||
@apply font-bold bg-accent-2;
|
||||
}
|
||||
|
||||
.off {
|
||||
@apply hidden;
|
||||
}
|
@@ -1,125 +0,0 @@
|
||||
import cn from 'clsx'
|
||||
import Link from 'next/link'
|
||||
import { FC, useRef, useState, useEffect } from 'react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useRouter } from 'next/router'
|
||||
import s from './DropdownMenu.module.css'
|
||||
import { Avatar } from '@components/common'
|
||||
import { Moon, Sun } from '@components/icons'
|
||||
import { useUI } from '@components/ui/context'
|
||||
import ClickOutside from '@lib/click-outside'
|
||||
import useLogout from '@framework/auth/use-logout'
|
||||
|
||||
import {
|
||||
disableBodyScroll,
|
||||
enableBodyScroll,
|
||||
clearAllBodyScrollLocks,
|
||||
} from 'body-scroll-lock'
|
||||
|
||||
interface DropdownMenuProps {
|
||||
open?: boolean
|
||||
}
|
||||
|
||||
const LINKS = [
|
||||
{
|
||||
name: 'My Orders',
|
||||
href: '/orders',
|
||||
},
|
||||
{
|
||||
name: 'My Profile',
|
||||
href: '/profile',
|
||||
},
|
||||
{
|
||||
name: 'My Cart',
|
||||
href: '/cart',
|
||||
},
|
||||
]
|
||||
|
||||
const DropdownMenu: FC<DropdownMenuProps> = ({ open = false }) => {
|
||||
const logout = useLogout()
|
||||
const { pathname } = useRouter()
|
||||
const { theme, setTheme } = useTheme()
|
||||
const [display, setDisplay] = useState(false)
|
||||
const { closeSidebarIfPresent } = useUI()
|
||||
const ref = useRef() as React.MutableRefObject<HTMLUListElement>
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
if (display) {
|
||||
disableBodyScroll(ref.current)
|
||||
} else {
|
||||
enableBodyScroll(ref.current)
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
clearAllBodyScrollLocks()
|
||||
}
|
||||
}, [display])
|
||||
|
||||
return (
|
||||
<ClickOutside active={display} onClick={() => setDisplay(false)}>
|
||||
<div>
|
||||
<button
|
||||
className={s.avatarButton}
|
||||
onClick={() => setDisplay(!display)}
|
||||
aria-label="Menu"
|
||||
>
|
||||
<Avatar />
|
||||
</button>
|
||||
{display && (
|
||||
<ul className={s.dropdownMenu} ref={ref}>
|
||||
{LINKS.map(({ name, href }) => (
|
||||
<li key={href}>
|
||||
<div>
|
||||
<Link href={href}>
|
||||
<a
|
||||
className={cn(s.link, {
|
||||
[s.active]: pathname === href,
|
||||
})}
|
||||
onClick={() => {
|
||||
setDisplay(false)
|
||||
closeSidebarIfPresent()
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
<li>
|
||||
<a
|
||||
className={cn(s.link, 'justify-between')}
|
||||
onClick={() => {
|
||||
theme === 'dark' ? setTheme('light') : setTheme('dark')
|
||||
setDisplay(false)
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
Theme: <strong>{theme}</strong>{' '}
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
{theme == 'dark' ? (
|
||||
<Moon width={20} height={20} />
|
||||
) : (
|
||||
<Sun width="20" height={20} />
|
||||
)}
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className={cn(s.link, 'border-t border-accent-2 mt-4')}
|
||||
onClick={() => logout()}
|
||||
>
|
||||
Logout
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</ClickOutside>
|
||||
)
|
||||
}
|
||||
|
||||
export default DropdownMenu
|
@@ -3,5 +3,5 @@
|
||||
}
|
||||
|
||||
.item {
|
||||
@apply text-2xl font-bold;
|
||||
}
|
||||
@apply text-xl font-bold py-2;
|
||||
}
|
||||
|
@@ -1,31 +1,32 @@
|
||||
import Link from 'next/link'
|
||||
import s from './MenuSidebarView.module.css'
|
||||
import { FC } from 'react'
|
||||
import { useUI } from '@components/ui/context'
|
||||
import SidebarLayout from '@components/common/SidebarLayout'
|
||||
import { Link as LinkProps} from '.'
|
||||
import type { Link as LinkProps } from './index'
|
||||
|
||||
|
||||
interface MenuProps {
|
||||
export default function MenuSidebarView({
|
||||
links = [],
|
||||
}: {
|
||||
links?: LinkProps[]
|
||||
}
|
||||
|
||||
const MenuSidebarView: FC<MenuProps> = (props) => {
|
||||
}) {
|
||||
const { closeSidebar } = useUI()
|
||||
const handleClose = () => closeSidebar()
|
||||
|
||||
return (
|
||||
<SidebarLayout handleClose={handleClose}>
|
||||
<SidebarLayout handleClose={() => closeSidebar()}>
|
||||
<div className={s.root}>
|
||||
<nav>
|
||||
<ul>
|
||||
<li className={s.item}>
|
||||
<li className={s.item} onClick={() => closeSidebar()}>
|
||||
<Link href="/search">
|
||||
<a>All</a>
|
||||
</Link>
|
||||
</li>
|
||||
{props.links?.map((l: any) => (
|
||||
<li key={l.href} className={s.item}>
|
||||
{links.map((l: any) => (
|
||||
<li
|
||||
key={l.href}
|
||||
className={s.item}
|
||||
onClick={() => closeSidebar()}
|
||||
>
|
||||
<Link href={l.href}>
|
||||
<a>{l.label}</a>
|
||||
</Link>
|
||||
@@ -38,4 +39,4 @@ const MenuSidebarView: FC<MenuProps> = (props) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default MenuSidebarView
|
||||
MenuSidebarView
|
||||
|
@@ -1,6 +1,5 @@
|
||||
export { default } from './MenuSidebarView'
|
||||
|
||||
export interface Link {
|
||||
href: string
|
||||
label: string
|
||||
}
|
||||
}
|
||||
|
@@ -7,20 +7,21 @@
|
||||
}
|
||||
|
||||
.item {
|
||||
@apply ml-6 cursor-pointer relative transition ease-in-out duration-100 flex items-center outline-none text-primary;
|
||||
@apply ml-6 cursor-pointer relative transition ease-in-out
|
||||
duration-100 flex items-center outline-none text-primary;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@apply text-accent-6 transition scale-110 duration-100;
|
||||
}
|
||||
.item:hover {
|
||||
@apply text-accent-6 transition scale-110 duration-100;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
@apply ml-0;
|
||||
}
|
||||
.item:first-child {
|
||||
@apply ml-0;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
@apply outline-none;
|
||||
}
|
||||
.item:focus,
|
||||
.item:active {
|
||||
@apply outline-none;
|
||||
}
|
||||
|
||||
.bagCount {
|
||||
@@ -36,10 +37,23 @@
|
||||
}
|
||||
|
||||
.mobileMenu {
|
||||
@apply flex lg:hidden ml-6
|
||||
@apply flex lg:hidden ml-6 text-white;
|
||||
}
|
||||
|
||||
.avatarButton:focus,
|
||||
.mobileMenu:focus {
|
||||
@apply outline-none;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdownDesktop {
|
||||
@apply hidden -z-10;
|
||||
}
|
||||
|
||||
@media screen(lg) {
|
||||
.dropdownDesktop {
|
||||
@apply block;
|
||||
}
|
||||
.dropdownMobile {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
|
@@ -1,29 +1,40 @@
|
||||
import { FC } from 'react'
|
||||
import Link from 'next/link'
|
||||
import cn from 'clsx'
|
||||
import type { LineItem } from '@commerce/types/cart'
|
||||
import useCart from '@framework/cart/use-cart'
|
||||
import useCustomer from '@framework/customer/use-customer'
|
||||
import { Avatar } from '@components/common'
|
||||
import { Heart, Bag } from '@components/icons'
|
||||
import { useUI } from '@components/ui/context'
|
||||
import Button from '@components/ui/Button'
|
||||
import DropdownMenu from './DropdownMenu'
|
||||
import Link from 'next/link'
|
||||
import s from './UserNav.module.css'
|
||||
import Menu from '@components/icons/Menu'
|
||||
import { Avatar } from '@components/common'
|
||||
import useCart from '@framework/cart/use-cart'
|
||||
import { useUI } from '@components/ui/context'
|
||||
import { Heart, Bag, Menu } from '@components/icons'
|
||||
import CustomerMenuContent from './CustomerMenuContent'
|
||||
import useCustomer from '@framework/customer/use-customer'
|
||||
import React from 'react'
|
||||
import {
|
||||
Dropdown,
|
||||
DropdownTrigger as DropdownTriggerInst,
|
||||
Button,
|
||||
} from '@components/ui'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
}
|
||||
import type { LineItem } from '@commerce/types/cart'
|
||||
|
||||
const countItem = (count: number, item: LineItem) => count + item.quantity
|
||||
|
||||
const UserNav: FC<Props> = ({ className }) => {
|
||||
const UserNav: React.FC<{
|
||||
className?: string
|
||||
}> = ({ className }) => {
|
||||
const { data } = useCart()
|
||||
const { data: customer } = useCustomer()
|
||||
const { toggleSidebar, closeSidebarIfPresent, openModal, setSidebarView } =
|
||||
useUI()
|
||||
const { data: isCustomerLoggedIn } = useCustomer()
|
||||
const {
|
||||
toggleSidebar,
|
||||
closeSidebarIfPresent,
|
||||
openModal,
|
||||
setSidebarView,
|
||||
openSidebar,
|
||||
} = useUI()
|
||||
|
||||
const itemsCount = data?.lineItems.reduce(countItem, 0) ?? 0
|
||||
const DropdownTrigger = isCustomerLoggedIn
|
||||
? DropdownTriggerInst
|
||||
: React.Fragment
|
||||
|
||||
return (
|
||||
<nav className={cn(s.root, className)}>
|
||||
@@ -57,28 +68,29 @@ const UserNav: FC<Props> = ({ className }) => {
|
||||
)}
|
||||
{process.env.COMMERCE_CUSTOMERAUTH_ENABLED && (
|
||||
<li className={s.item}>
|
||||
{customer ? (
|
||||
<DropdownMenu />
|
||||
) : (
|
||||
<button
|
||||
className={s.avatarButton}
|
||||
aria-label="Menu"
|
||||
onClick={() => openModal()}
|
||||
>
|
||||
<Avatar />
|
||||
</button>
|
||||
)}
|
||||
<Dropdown>
|
||||
<DropdownTrigger asChild>
|
||||
<button
|
||||
aria-label="Menu"
|
||||
className={s.avatarButton}
|
||||
onClick={() => (isCustomerLoggedIn ? null : openModal())}
|
||||
>
|
||||
<Avatar />
|
||||
</button>
|
||||
</DropdownTrigger>
|
||||
<CustomerMenuContent />
|
||||
</Dropdown>
|
||||
</li>
|
||||
)}
|
||||
<li className={s.mobileMenu}>
|
||||
<Button
|
||||
className={s.item}
|
||||
aria-label="Menu"
|
||||
variant="naked"
|
||||
onClick={() => {
|
||||
setSidebarView('MOBILEMENU_VIEW')
|
||||
toggleSidebar()
|
||||
openSidebar()
|
||||
setSidebarView('MOBILE_MENU_VIEW')
|
||||
}}
|
||||
aria-label="Menu"
|
||||
>
|
||||
<Menu />
|
||||
</Button>
|
||||
|
@@ -1 +1,3 @@
|
||||
export { default } from './UserNav'
|
||||
export { default as MenuSidebarView } from './MenuSidebarView'
|
||||
export { default as CustomerMenuContent } from './CustomerMenuContent'
|
||||
|
@@ -1,17 +1,18 @@
|
||||
export { default as Bag } from './Bag'
|
||||
export { default as Sun } from './Sun'
|
||||
export { default as Moon } from './Moon'
|
||||
export { default as Menu } from './Menu'
|
||||
export { default as Info } from './Info'
|
||||
export { default as Star } from './Star'
|
||||
export { default as Plus } from './Plus'
|
||||
export { default as Heart } from './Heart'
|
||||
export { default as Trash } from './Trash'
|
||||
export { default as Cross } from './Cross'
|
||||
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 Info } from './Info'
|
||||
export { default as Vercel } from './Vercel'
|
||||
export { default as MapPin } from './MapPin'
|
||||
export { default as Star } from './Star'
|
||||
export { default as ArrowLeft } from './ArrowLeft'
|
||||
export { default as ArrowRight } from './ArrowRight'
|
||||
export { default as CreditCard } from './CreditCard'
|
||||
|
@@ -12,10 +12,10 @@ const Container: FC<ContainerProps> = ({
|
||||
children,
|
||||
className,
|
||||
el = 'div',
|
||||
clean,
|
||||
clean = false, // Full Width Screen
|
||||
}) => {
|
||||
const rootClassName = cn(className, {
|
||||
'mx-auto max-w-8xl px-6': !clean,
|
||||
'mx-auto max-w-7xl px-6 w-full': !clean,
|
||||
})
|
||||
|
||||
let Component: React.ComponentType<React.HTMLAttributes<HTMLDivElement>> =
|
||||
|
32
site/components/ui/Dropdown/Dropdown.module.css
Normal file
32
site/components/ui/Dropdown/Dropdown.module.css
Normal file
@@ -0,0 +1,32 @@
|
||||
.root {
|
||||
@apply bg-accent-0;
|
||||
animation: none;
|
||||
transition: none;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
@media screen(lg) {
|
||||
.root {
|
||||
@apply bg-accent-0;
|
||||
box-shadow: hsl(206 22% 7% / 45%) 0px 10px 38px -10px,
|
||||
hsl(206 22% 7% / 20%) 0px 10px 20px -15px;
|
||||
min-width: 14rem;
|
||||
will-change: transform, opacity;
|
||||
animation-duration: 600ms;
|
||||
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
animation-fill-mode: forwards;
|
||||
transform-origin: var(--radix-dropdown-menu-content-transform-origin);
|
||||
animation-name: slideIn;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(2px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0px);
|
||||
}
|
||||
}
|
22
site/components/ui/Dropdown/Dropdown.tsx
Normal file
22
site/components/ui/Dropdown/Dropdown.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import cn from 'clsx'
|
||||
import React from 'react'
|
||||
import s from './Dropdown.module.css'
|
||||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
|
||||
|
||||
export const Dropdown = DropdownMenu.Root
|
||||
export const DropdownMenuItem = DropdownMenu.Item
|
||||
export const DropdownTrigger = DropdownMenu.Trigger
|
||||
export const DropdownMenuLabel = DropdownMenu.Label
|
||||
export const DropdownMenuGroup = DropdownMenu.Group
|
||||
|
||||
export const DropdownContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
{ children: React.ReactNode } & DropdownMenu.DropdownMenuContentProps &
|
||||
React.RefAttributes<HTMLDivElement>
|
||||
>(function DropdownContent({ children, className, ...props }, forwardedRef) {
|
||||
return (
|
||||
<DropdownMenu.Content ref={forwardedRef} sideOffset={8} {...props}>
|
||||
<div className={cn(s.root, className)}>{children}</div>
|
||||
</DropdownMenu.Content>
|
||||
)
|
||||
})
|
@@ -1,14 +1,13 @@
|
||||
import { FC, useEffect, useRef } from 'react'
|
||||
import s from './Sidebar.module.css'
|
||||
import cn from 'clsx'
|
||||
import s from './Sidebar.module.css'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { disableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock'
|
||||
|
||||
interface SidebarProps {
|
||||
children: any
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const Sidebar: FC<SidebarProps> = ({ children, onClose }) => {
|
||||
const Sidebar: React.FC<SidebarProps> = ({ children, onClose }) => {
|
||||
const sidebarRef = useRef() as React.MutableRefObject<HTMLDivElement>
|
||||
const contentRef = useRef() as React.MutableRefObject<HTMLDivElement>
|
||||
|
||||
|
@@ -13,4 +13,5 @@ export { default as Input } from './Input'
|
||||
export { default as Collapse } from './Collapse'
|
||||
export { default as Quantity } from './Quantity'
|
||||
export { default as Rating } from './Rating'
|
||||
export * from './Dropdown/Dropdown'
|
||||
export { useUI } from './context'
|
||||
|
@@ -31,8 +31,8 @@ const WishlistButton: FC<Props> = ({
|
||||
const itemInWishlist = data?.items?.find(
|
||||
// @ts-ignore Wishlist is not always enabled
|
||||
(item) =>
|
||||
item.product_id === productId &&
|
||||
item.variant_id === variant.id
|
||||
item.product_id === Number(productId) &&
|
||||
item.variant_id === Number(variant.id)
|
||||
)
|
||||
|
||||
const handleWishlistChange = async (e: any) => {
|
||||
|
@@ -1,21 +1,38 @@
|
||||
.root {
|
||||
@apply grid grid-cols-12 w-full gap-6 px-3 py-6 border-b border-accent-2 transition duration-100 ease-in-out;
|
||||
@apply relative grid sm:grid-cols-1 lg:grid-cols-12
|
||||
w-full gap-6 px-3 py-6 border-b border-accent-2
|
||||
transition duration-100 ease-in-out;
|
||||
}
|
||||
|
||||
&:nth-child(3n + 1) {
|
||||
& .productBg {
|
||||
@apply bg-violet;
|
||||
}
|
||||
}
|
||||
.root:nth-child(3n + 1) .imageWrapper {
|
||||
@apply bg-violet;
|
||||
}
|
||||
|
||||
&:nth-child(3n + 2) {
|
||||
& .productBg {
|
||||
@apply bg-pink;
|
||||
}
|
||||
}
|
||||
.root:nth-child(3n + 2) .imageWrapper {
|
||||
@apply bg-pink;
|
||||
}
|
||||
|
||||
&:nth-child(3n + 3) {
|
||||
& .productBg {
|
||||
@apply bg-blue;
|
||||
}
|
||||
.root:nth-child(3n + 3) .imageWrapper {
|
||||
@apply bg-blue;
|
||||
}
|
||||
|
||||
.imageWrapper {
|
||||
@apply col-span-3;
|
||||
min-width: 230px;
|
||||
width: 230px;
|
||||
height: 230px;
|
||||
}
|
||||
|
||||
.description {
|
||||
@apply col-span-7 flex flex-col;
|
||||
}
|
||||
|
||||
.actions {
|
||||
@apply absolute bg-accent-0 p-3 top-0 right-4;
|
||||
}
|
||||
|
||||
@media screen(lg) {
|
||||
.actions {
|
||||
@apply static col-span-2 flex flex-col justify-between space-y-4;
|
||||
}
|
||||
}
|
||||
|
@@ -13,13 +13,11 @@ import useAddItem from '@framework/cart/use-add-item'
|
||||
import useRemoveItem from '@framework/wishlist/use-remove-item'
|
||||
import type { Wishlist } from '@commerce/types/wishlist'
|
||||
|
||||
interface Props {
|
||||
item: Wishlist
|
||||
}
|
||||
|
||||
const placeholderImg = '/product-img-placeholder.svg'
|
||||
|
||||
const WishlistCard: FC<Props> = ({ item }) => {
|
||||
const WishlistCard: React.FC<{
|
||||
item: Wishlist
|
||||
}> = ({ item }) => {
|
||||
const product: Product = item.product
|
||||
const { price } = usePrice({
|
||||
amount: product.price?.value,
|
||||
@@ -63,41 +61,41 @@ const WishlistCard: FC<Props> = ({ item }) => {
|
||||
|
||||
return (
|
||||
<div className={cn(s.root, { 'opacity-75 pointer-events-none': removing })}>
|
||||
<div className={`col-span-3 ${s.productBg}`}>
|
||||
<div>
|
||||
<Image
|
||||
src={product.images[0]?.url || placeholderImg}
|
||||
width={400}
|
||||
height={400}
|
||||
alt={product.images[0]?.alt || 'Product Image'}
|
||||
/>
|
||||
</div>
|
||||
<div className={s.imageWrapper}>
|
||||
<Image
|
||||
width={230}
|
||||
height={230}
|
||||
src={product.images[0]?.url || placeholderImg}
|
||||
alt={product.images[0]?.alt || 'Product Image'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-span-7">
|
||||
<h3 className="text-2xl mb-2">
|
||||
<Link href={`/product${product.path}`}>
|
||||
<a>{product.name}</a>
|
||||
</Link>
|
||||
</h3>
|
||||
<div className="mb-4">
|
||||
<Text html={product.description} />
|
||||
<div className={s.description}>
|
||||
<div className="flex-1 mb-6">
|
||||
<h3 className="text-2xl mb-2 -mt-1">
|
||||
<Link href={`/product${product.path}`}>
|
||||
<a>{product.name}</a>
|
||||
</Link>
|
||||
</h3>
|
||||
<div className="mb-4">
|
||||
<Text html={product.description} />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
width={260}
|
||||
aria-label="Add to Cart"
|
||||
type="button"
|
||||
onClick={addToCart}
|
||||
loading={loading}
|
||||
>
|
||||
Add to Cart
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
aria-label="Add to Cart"
|
||||
type="button"
|
||||
className={
|
||||
'py-1 px-3 border border-secondary rounded-md shadow-sm hover:bg-primary-hover'
|
||||
}
|
||||
onClick={addToCart}
|
||||
loading={loading}
|
||||
>
|
||||
Add to Cart
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-span-2 flex flex-col justify-between">
|
||||
<div className={s.actions}>
|
||||
<div className="flex justify-end font-bold">{price}</div>
|
||||
<div className="flex justify-end">
|
||||
<div className="flex justify-end mt-4 lg:mt-0">
|
||||
<button onClick={handleRemove}>
|
||||
<Trash />
|
||||
</button>
|
||||
|
Reference in New Issue
Block a user