4
0
forked from crowetic/commerce

Removing HeadlessUI and React-Aria

This commit is contained in:
Belen Curcio 2020-11-26 13:13:30 -03:00
parent 9dbd606eef
commit b5fd6bd38e
20 changed files with 417 additions and 2587 deletions

View File

@ -1,11 +1,9 @@
import { FC } from 'react'
import cn from 'classnames'
import { useRouter } from 'next/router'
import Link from 'next/link'
import { Menu } from '@headlessui/react'
import { DoubleChevron } from '@components/icons'
import { FC, useState } from 'react'
import { useRouter } from 'next/router'
import s from './I18nWidget.module.css'
import { Cross } from '@components/icons'
interface LOCALE_DATA {
name: string
img: {
@ -32,6 +30,7 @@ const LOCALES_MAP: Record<string, LOCALE_DATA> = {
}
const I18nWidget: FC = () => {
const [display, setDisplay] = useState(false)
const {
locale,
locales,
@ -39,42 +38,61 @@ const I18nWidget: FC = () => {
asPath: currentPath,
} = useRouter()
const options = locales?.filter((val) => val !== locale)
const currentLocale = locale || defaultLocale
return (
<nav className={s.root}>
<Menu>
<Menu.Button className={s.button} aria-label="Language selector">
<img
className="block mr-2 w-5"
src={`/${LOCALES_MAP[currentLocale].img.filename}`}
alt={LOCALES_MAP[currentLocale].img.alt}
/>
<span className="mr-2">{LOCALES_MAP[currentLocale].name}</span>
{options && (
<span>
<DoubleChevron />
</span>
)}
</Menu.Button>
{options?.length ? (
<Menu.Items className={s.dropdownMenu}>
{options.map((locale) => (
<Menu.Item key={locale}>
{({ active }) => (
<div className="flex items-center relative">
<button className={s.button} aria-label="Language selector" />
<img
className="block mr-2 w-5"
src={`/${LOCALES_MAP[currentLocale].img.filename}`}
alt={LOCALES_MAP[currentLocale].img.alt}
/>
{options && (
<span className="cursor-pointer" onClick={() => setDisplay(!display)}>
<svg
viewBox="0 0 24 24"
width="24"
height="24"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
fill="none"
shapeRendering="geometricPrecision"
>
<path d="M6 9l6 6 6-6" />
</svg>
</span>
)}
</div>
<div className="absolute top-0 right-0">
{options?.length && display ? (
<div className={s.dropdownMenu}>
<div className="flex flex-row justify-end px-6">
<button
onClick={() => setDisplay(false)}
aria-label="Close panel"
className={s.closeButton}
>
<Cross className="h-6 w-6" />
</button>
</div>
<ul>
{options.map((locale) => (
<li key={locale}>
<Link href={currentPath} locale={locale}>
<a className={cn(s.item, { [s.active]: active })}>
<a className={cn(s.item)} onClick={() => setDisplay(false)}>
{LOCALES_MAP[locale].name}
</a>
</Link>
)}
</Menu.Item>
))}
</Menu.Items>
</li>
))}
</ul>
</div>
) : null}
</Menu>
</div>
</nav>
)
}

View File

@ -5,7 +5,6 @@ import { useRouter } from 'next/router'
import React, { FC } from 'react'
import { useUI } from '@components/ui/context'
import { Navbar, Footer } from '@components/common'
import { usePreventScroll } from '@react-aria/overlays'
import { useAcceptCookies } from '@lib/hooks/useAcceptCookies'
import { CommerceProvider } from '@bigcommerce/storefront-data-hooks'
import { Sidebar, Button, Modal, LoadingDots } from '@components/ui'
@ -56,10 +55,6 @@ const Layout: FC<Props> = ({ children, pageProps }) => {
const { acceptedCookies, onAcceptCookies } = useAcceptCookies()
const { locale = 'en-US' } = useRouter()
usePreventScroll({
isDisabled: !(displaySidebar || displayModal),
})
return (
<CommerceProvider locale={locale}>
<div className={cn(s.root)}>

View File

@ -1,2 +0,0 @@
.root {
}

View File

@ -1,55 +0,0 @@
import React, { FC } from 'react'
import { Switch } from '@headlessui/react'
import { Moon, Sun } from '@components/icons'
interface Props {
className?: string
checked: boolean
onChange: any
}
const Toggle: FC<Props> = ({ className, checked, onChange }) => {
return (
<Switch
checked={checked}
onChange={onChange}
className="focus:outline-none"
>
<span
role="checkbox"
aria-checked="false"
tabIndex={0}
className={`${
checked ? 'bg-gray-800' : 'bg-gray-200'
} relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-150 focus:outline-none focus:shadow-outline`}
>
<span
aria-hidden="true"
className={`${
checked ? 'translate-x-5' : 'translate-x-0'
} translate-x-0 relative inline-block h-5 w-5 rounded-full bg-white shadow transform transition ease-in-out duration-150`}
>
<span
className={`${
checked
? 'opacity-0 ease-out duration-150'
: 'opacity-100 ease-in duration-150'
} absolute inset-0 h-full w-full flex items-center justify-center transition-opacity`}
>
<Sun className="h-3 w-3 text-accent-3" />
</span>
<span
className={`${
checked
? 'opacity-100 ease-in duration-150'
: 'opacity-0 ease-out duration-150'
} opacity-0 ease-out duration-150 absolute inset-0 h-full w-full flex items-center justify-center transition-opacity`}
>
<Moon className="h-3 w-3 text-yellow-400" />
</span>
</span>
</span>
</Switch>
)
}
export default Toggle

View File

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

View File

@ -1,16 +1,16 @@
import { FC } from 'react'
import Link from 'next/link'
import { useTheme } from 'next-themes'
import cn from 'classnames'
import Link from 'next/link'
import { FC, useState } 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 { Menu, Transition } from '@headlessui/react'
import useLogout from '@bigcommerce/storefront-data-hooks/use-logout'
import { useRouter } from 'next/router'
import useLogout from '@bigcommerce/storefront-data-hooks/use-logout'
interface DropdownMenuProps {
open: boolean
open?: boolean
}
const LINKS = [
@ -29,68 +29,70 @@ const LINKS = [
]
const DropdownMenu: FC<DropdownMenuProps> = ({ open = false }) => {
const { theme, setTheme } = useTheme()
const logout = useLogout()
const { pathname } = useRouter()
const { theme, setTheme } = useTheme()
const [display, setDisplay] = useState(false)
const { closeSidebarIfPresent } = useUI()
return (
<Transition
show={open}
enter="transition ease-out duration-150 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}>
{LINKS.map(({ name, href }) => (
<Menu.Item key={href}>
<div>
<Link href={href}>
<a
className={cn(s.link, {
[s.active]: pathname === href,
})}
onClick={closeSidebarIfPresent}
>
{name}
</a>
</Link>
</div>
</Menu.Item>
))}
<Menu.Item>
<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>
<a
className={cn(s.link, 'border-t border-accents-2 mt-4')}
onClick={() => logout()}
>
Logout
</a>
</Menu.Item>
</Menu.Items>
</Transition>
<div>
<button
className={s.avatarButton}
onClick={() => setDisplay(!display)}
aria-label="Menu"
>
<Avatar />
</button>
{display && (
<ul className={s.dropdownMenu}>
{LINKS.map(({ name, href }) => (
<li key={href}>
<div>
<Link href={href}>
<a
className={cn(s.link, {
[s.active]: pathname === href,
})}
onClick={closeSidebarIfPresent}
>
{name}
</a>
</Link>
</div>
</li>
))}
<li>
<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>
</li>
<li>
<a
className={cn(s.link, 'border-t border-accents-2 mt-4')}
onClick={() => logout()}
>
Logout
</a>
</li>
</ul>
)}
</div>
)
}

View File

@ -3,12 +3,11 @@ import Link from 'next/link'
import cn from 'classnames'
import useCart from '@bigcommerce/storefront-data-hooks/cart/use-cart'
import useCustomer from '@bigcommerce/storefront-data-hooks/use-customer'
import { Menu } from '@headlessui/react'
import { Heart, Bag } from '@components/icons'
import { Avatar } from '@components/common'
import { useUI } from '@components/ui/context'
import DropdownMenu from './DropdownMenu'
import s from './UserNav.module.css'
import { Avatar } from '@components/common'
interface Props {
className?: string
@ -21,9 +20,9 @@ const countItems = (count: number, items: any[]) =>
const UserNav: FC<Props> = ({ className, children, ...props }) => {
const { data } = useCart()
const { data: customer } = useCustomer()
const { toggleSidebar, closeSidebarIfPresent, openModal } = useUI()
const itemsCount = Object.values(data?.line_items ?? {}).reduce(countItems, 0)
return (
<nav className={cn(s.root, className)}>
<div className={s.mainContainer}>
@ -41,16 +40,7 @@ const UserNav: FC<Props> = ({ className, children, ...props }) => {
</li>
<li className={s.item}>
{customer ? (
<Menu>
{({ open }) => (
<>
<Menu.Button className={s.avatarButton} aria-label="Menu">
<Avatar />
</Menu.Button>
<DropdownMenu open={open} />
</>
)}
</Menu>
<DropdownMenu />
) : (
<button
className={s.avatarButton}

View File

@ -5,7 +5,6 @@ export { default as Layout } from './Layout'
export { default as Navbar } from './Navbar'
export { default as Searchbar } from './Searchbar'
export { default as UserNav } from './UserNav'
export { default as Toggle } from './Toggle'
export { default as Head } from './Head'
export { default as HTMLContent } from './HTMLContent'
export { default as I18nWidget } from './I18nWidget'

View File

@ -6,7 +6,6 @@ import React, {
useRef,
} from 'react'
import mergeRefs from 'react-merge-refs'
import { useButton } from 'react-aria'
import s from './Button.module.css'
import { LoadingDots } from '@components/ui'
@ -34,19 +33,8 @@ const Button: React.FC<ButtonProps> = forwardRef((props, buttonRef) => {
loading = false,
disabled = false,
style = {},
...rest
} = props
const ref = useRef<typeof Component>(null)
const { buttonProps, isPressed } = useButton(
{
...rest,
// @ts-ignore onClick === onPress for our purposes
onPress: onClick,
isDisabled: disabled,
elementType: Component,
},
ref
)
const rootClassName = cn(
s.root,
@ -63,8 +51,6 @@ const Button: React.FC<ButtonProps> = forwardRef((props, buttonRef) => {
aria-pressed={active}
data-variant={variant}
ref={mergeRefs([ref, buttonRef])}
{...buttonProps}
data-active={isPressed ? '' : undefined}
className={rootClassName}
disabled={disabled}
style={{

View File

@ -1,10 +1,13 @@
import { FC, useRef } from 'react'
import s from './Modal.module.css'
import { FocusScope } from '@react-aria/focus'
import { Transition } from '@headlessui/react'
import { Cross } from '@components/icons'
import { useOverlay, OverlayContainer } from '@react-aria/overlays'
import { FC, useRef, useEffect } from 'react'
import Portal from '@reach/portal'
import s from './Modal.module.css'
import { Cross } from '@components/icons'
import {
disableBodyScroll,
enableBodyScroll,
clearAllBodyScrollLocks,
} from 'body-scroll-lock'
interface Props {
className?: string
children?: any
@ -12,48 +15,41 @@ interface Props {
onClose: () => void
}
const Modal: FC<Props> = ({ children, open = false, onClose, ...props }) => {
let ref = useRef() as React.MutableRefObject<HTMLInputElement>
let { overlayProps } = useOverlay(
{
isOpen: open,
isDismissable: false,
onClose: onClose,
...props,
},
ref
)
const Modal: FC<Props> = ({ children, open, onClose }) => {
const ref = useRef() as React.MutableRefObject<HTMLDivElement>
useEffect(() => {
if (ref.current) {
if (open) {
disableBodyScroll(ref.current)
} else {
enableBodyScroll(ref.current)
}
}
return () => {
clearAllBodyScrollLocks()
}
}, [open])
return (
<Transition show={open}>
<OverlayContainer>
<FocusScope contain restoreFocus autoFocus>
<div className={s.root}>
<Transition.Child
enter="transition-opacity ease-linear duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity ease-linear duration-300"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className={s.modal} {...overlayProps} ref={ref}>
<div className="h-7 flex items-center justify-end w-full">
<button
onClick={() => onClose()}
aria-label="Close panel"
className="hover:text-gray-500 transition ease-in-out duration-150 focus:outline-none"
>
<Cross className="h-6 w-6" />
</button>
</div>
{children}
</div>
</Transition.Child>
<Portal>
{open ? (
<div className={s.root} ref={ref}>
<div className={s.modal}>
<div className="h-7 flex items-center justify-end w-full">
<button
onClick={() => onClose()}
aria-label="Close panel"
className="hover:text-gray-500 transition ease-in-out duration-150 focus:outline-none"
>
<Cross className="h-6 w-6" />
</button>
</div>
{children}
</div>
</FocusScope>
</OverlayContainer>
</Transition>
</div>
) : null}
</Portal>
)
}

View File

@ -1,9 +1,11 @@
import { FC, useRef } from 'react'
import s from './Sidebar.module.css'
import { Transition } from '@headlessui/react'
import { useOverlay, OverlayContainer } from '@react-aria/overlays'
import { FocusScope } from '@react-aria/focus'
import Portal from '@reach/portal'
import { FC, useEffect, useRef } from 'react'
import {
disableBodyScroll,
enableBodyScroll,
clearAllBodyScrollLocks,
} from 'body-scroll-lock'
interface Props {
children: any
@ -12,62 +14,40 @@ interface Props {
}
const Sidebar: FC<Props> = ({ children, open = false, onClose }) => {
const ref = useRef<HTMLDivElement>(null)
const { overlayProps } = useOverlay(
{
isOpen: open,
isDismissable: true,
onClose: onClose,
},
ref
)
const ref = useRef() as React.MutableRefObject<HTMLDivElement>
useEffect(() => {
if (ref.current) {
if (open) {
disableBodyScroll(ref.current)
} else {
enableBodyScroll(ref.current)
}
}
return () => {
clearAllBodyScrollLocks()
}
}, [open])
return (
<Portal>
<Transition show={open}>
<OverlayContainer>
<FocusScope contain restoreFocus autoFocus>
<div className={s.root}>
<div className="absolute inset-0 overflow-hidden">
<Transition.Child
enter="transition-opacity ease-linear duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity ease-linear duration-300"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div
className="absolute inset-0 bg-black bg-opacity-50 transition-opacity"
// Close the sidebar when clicking on the backdrop
onClick={onClose}
/>
</Transition.Child>
<section
className="absolute inset-y-0 right-0 pl-10 max-w-full flex sm:pl-16 outline-none"
{...overlayProps}
ref={ref}
>
<Transition.Child
enter="transition ease-in-out duration-300 transform"
enterFrom="translate-x-full"
enterTo="translate-x-0"
leave="transition ease-in-out duration-300 transform"
leaveFrom="translate-x-0"
leaveTo="translate-x-full"
>
<div className="h-full md:w-screen md:max-w-md">
<div className="h-full flex flex-col text-base bg-accents-1 shadow-xl overflow-y-auto">
{children}
</div>
</div>
</Transition.Child>
</section>
{open ? (
<div className={s.root} ref={ref}>
<div className="absolute inset-0 overflow-hidden">
<div
className="absolute inset-0 bg-black bg-opacity-50 transition-opacity"
onClick={onClose}
/>
<section className="absolute inset-y-0 right-0 pl-10 max-w-full flex sm:pl-16 outline-none">
<div className="h-full md:w-screen md:max-w-md">
<div className="h-full flex flex-col text-base bg-accents-1 shadow-xl overflow-y-auto">
{children}
</div>
</div>
</div>
</FocusScope>
</OverlayContainer>
</Transition>
</section>
</div>
</div>
) : null}
</Portal>
)
}

View File

@ -1,9 +0,0 @@
.root {
}
.toast {
@apply absolute bg-primary text-primary flex items-center border border-accents-1
rounded-md z-50 shadow-2xl top-0 right-0 p-6 my-6 mx-3;
width: 420px;
z-index: 20000;
}

View File

@ -1,73 +0,0 @@
import cn from 'classnames'
import { FC, useRef, useEffect, useCallback } from 'react'
import s from './Toast.module.css'
import { useDialog } from '@react-aria/dialog'
import { FocusScope } from '@react-aria/focus'
import { Transition } from '@headlessui/react'
import { useOverlay, useModal, OverlayContainer } from '@react-aria/overlays'
interface Props {
className?: string
children?: any
open?: boolean
onClose: () => void
}
const Toast: FC<Props> = ({
className,
children,
open = false,
onClose,
...props
}) => {
const rootClassName = cn(s.root, className)
let ref = useRef() as React.MutableRefObject<HTMLInputElement>
let { modalProps } = useModal()
let { dialogProps } = useDialog({}, ref)
let { overlayProps } = useOverlay(
{
isOpen: open,
isDismissable: true,
onClose: onClose,
...props,
},
ref
)
// useEffect(() => {
// setTimeout(() => {
// useCallback(onClose, [])
// }, 400)
// })
return (
<Transition show={open}>
<OverlayContainer>
<FocusScope contain restoreFocus autoFocus>
<div className={rootClassName}>
<Transition.Child
enter="transition-opacity ease-linear duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity ease-linear duration-300"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div
className={s.toast}
{...overlayProps}
{...dialogProps}
{...modalProps}
ref={ref}
>
{children}
</div>
</Transition.Child>
</div>
</FocusScope>
</OverlayContainer>
</Transition>
)
}
export default Toast

View File

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

View File

@ -1,6 +1,5 @@
import React, { FC, useMemo } from 'react'
import { ThemeProvider } from 'next-themes'
import { SSRProvider, OverlayProvider } from 'react-aria'
export interface State {
displaySidebar: boolean
@ -181,10 +180,6 @@ export const useUI = () => {
export const ManagedUIContext: FC = ({ children }) => (
<UIProvider>
<ThemeProvider>
<SSRProvider>
<OverlayProvider>{children}</OverlayProvider>
</SSRProvider>
</ThemeProvider>
<ThemeProvider>{children}</ThemeProvider>
</UIProvider>
)

View File

@ -10,4 +10,3 @@ export { default as Skeleton } from './Skeleton'
export { default as Modal } from './Modal'
export { default as Text } from './Text'
export { default as Input } from './Input'
export { default as Toast } from './Toast'

14
lib/defaults.ts Normal file
View File

@ -0,0 +1,14 @@
// Fallback to CMS Data
export const defatultPageProps = {
header: {
links: [
{
link: {
title: 'New Arrivals',
url: '/',
},
},
],
},
}

View File

@ -43,25 +43,25 @@
},
"dependencies": {
"@bigcommerce/storefront-data-hooks": "^1.0.2",
"@headlessui/react": "^0.2.0",
"@reach/portal": "^0.11.2",
"@react-aria/overlays": "^3.4.0",
"@tailwindcss/ui": "^0.6.2",
"@types/body-scroll-lock": "^2.6.1",
"@types/lodash.throttle": "^4.1.6",
"@vercel/fetch": "^6.1.0",
"body-scroll-lock": "^3.1.5",
"bowser": "^2.11.0",
"classnames": "^2.2.6",
"contentstack": "^3.11.0",
"email-validator": "^2.0.4",
"js-cookie": "^2.2.1",
"keen-slider": "^5.2.4",
"lodash.random": "^3.2.0",
"lodash.throttle": "^4.1.1",
"next": "^10.0.1-canary.7",
"next": "^10.0.2-canary.18",
"next-seo": "^4.11.0",
"next-themes": "^0.0.4",
"postcss-nesting": "^7.0.1",
"react": "^16.14.0",
"react-aria": "^3.0.0",
"react-dom": "^16.14.0",
"react-intersection-observer": "^8.30.1",
"react-merge-refs": "^1.1.0",

View File

@ -4,9 +4,9 @@ import getAllPages from '@bigcommerce/storefront-data-hooks/api/operations/get-a
import useWishlist from '@bigcommerce/storefront-data-hooks/wishlist/use-wishlist'
import { Layout } from '@components/common'
import { Heart } from '@components/icons'
import { Container, Text } from '@components/ui'
import { Text } from '@components/ui'
import { WishlistCard } from '@components/wishlist'
import { Transition } from '@headlessui/react'
import { defatultPageProps } from '@lib/defaults'
export async function getStaticProps({
preview,
@ -15,7 +15,7 @@ export async function getStaticProps({
const config = getConfig({ locale })
const { pages } = await getAllPages({ config, preview })
return {
props: { pages },
props: { ...defatultPageProps, pages },
}
}
@ -23,54 +23,27 @@ export default function Wishlist() {
const { data, isEmpty } = useWishlist({ includeProducts: true })
return (
<Container>
<div className="mt-3 mb-20">
<Text variant="pageHeading">My Wishlist</Text>
<div className="group flex flex-col">
{isEmpty ? (
<Transition show>
<Transition.Child
enter="transition-opacity ease-linear duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity ease-linear duration-300"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="flex-1 px-12 py-24 flex flex-col justify-center items-center ">
<span className="border border-dashed border-secondary rounded-full flex items-center justify-center w-16 h-16 bg-primary p-12 rounded-lg text-primary">
<Heart className="absolute" />
</span>
<h2 className="pt-6 text-2xl font-bold tracking-wide text-center">
Your wishlist is empty
</h2>
<p className="text-accents-6 px-10 text-center pt-2">
Biscuit oat cake wafer icing ice cream tiramisu pudding
cupcake.
</p>
</div>
</Transition.Child>
</Transition>
) : (
<Transition show>
{data &&
data.items?.map((item) => (
<Transition.Child
enter="transition-opacity ease-linear duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity ease-linear duration-300"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<WishlistCard key={item.id} item={item} />
</Transition.Child>
))}
</Transition>
)}
</div>
<div className="mt-3 mb-20 px-3">
<Text variant="pageHeading">My Wishlist</Text>
<div className="group flex flex-col">
{isEmpty ? (
<div className="flex-1 px-12 py-24 flex flex-col justify-center items-center ">
<span className="border border-dashed border-secondary rounded-full flex items-center justify-center w-16 h-16 bg-primary p-12 rounded-lg text-primary">
<Heart className="absolute" />
</span>
<h2 className="pt-6 text-2xl font-bold tracking-wide text-center">
Your wishlist is empty
</h2>
<p className="text-accents-6 px-10 text-center pt-2">
Biscuit oat cake wafer icing ice cream tiramisu pudding cupcake.
</p>
</div>
) : (
data &&
data.items?.map((item) => <WishlistCard key={item.id} item={item} />)
)}
</div>
</Container>
</div>
)
}

2334
yarn.lock

File diff suppressed because it is too large Load Diff