mirror of
https://github.com/vercel/commerce.git
synced 2025-07-23 04:36:49 +00:00
Test suspense in dynamic content manager
This commit is contained in:
@@ -6,82 +6,83 @@ import Hero from '@/components/modules/hero';
|
||||
import ReusableSection from '@/components/modules/reusable-section/reusable-section';
|
||||
import Slider from '@/components/modules/slider/slider';
|
||||
import USPSection from '@/components/modules/usp-section/usp-section';
|
||||
import { InfoCircledIcon } from '@radix-ui/react-icons';
|
||||
import { InformationCircleIcon } from '@heroicons/react/24/outline';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
interface getContentComponentProps {
|
||||
_type: string;
|
||||
_key: number;
|
||||
disabled: boolean;
|
||||
}
|
||||
// interface getContentComponentProps {
|
||||
// _type: string;
|
||||
// _key: number;
|
||||
// disabled: boolean;
|
||||
// }
|
||||
|
||||
const getContentComponent = ({ _type, _key, disabled, ...rest }: getContentComponentProps) => {
|
||||
let Component: any;
|
||||
// const getContentComponent = ({ _type, _key, disabled, ...rest }: getContentComponentProps) => {
|
||||
// let Component: any;
|
||||
|
||||
switch (_type) {
|
||||
case 'hero':
|
||||
if (disabled !== true) {
|
||||
Component = Hero;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'slider':
|
||||
if (disabled !== true) {
|
||||
Component = Slider;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'filteredProductList':
|
||||
if (disabled !== true) {
|
||||
Component = FilteredProductList;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'blurbSection':
|
||||
if (disabled !== true) {
|
||||
Component = BlurbSection;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'uspSection':
|
||||
if (disabled !== true) {
|
||||
Component = USPSection;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'reusableSection':
|
||||
if (disabled !== true) {
|
||||
Component = ReusableSection;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return (
|
||||
<div
|
||||
className={`px-4 lg:px-8 2xl:px-16 ${
|
||||
process.env.NODE_ENV === 'production' ? 'hidden' : ''
|
||||
}`}
|
||||
key={`index-${_key}`}
|
||||
>
|
||||
<span className="inline-flex items-center bg-red p-2 text-sm font-bold">
|
||||
<InfoCircledIcon className="mr-1" />
|
||||
{`No matching component (Type: ${_type})`}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// switch (_type) {
|
||||
// case 'hero':
|
||||
// if (disabled !== true) {
|
||||
// Component = Hero;
|
||||
// } else {
|
||||
// return;
|
||||
// }
|
||||
// break;
|
||||
// case 'slider':
|
||||
// if (disabled !== true) {
|
||||
// Component = Slider;
|
||||
// } else {
|
||||
// return;
|
||||
// }
|
||||
// break;
|
||||
// case 'filteredProductList':
|
||||
// if (disabled !== true) {
|
||||
// Component = FilteredProductList;
|
||||
// } else {
|
||||
// return;
|
||||
// }
|
||||
// break;
|
||||
// case 'blurbSection':
|
||||
// if (disabled !== true) {
|
||||
// Component = BlurbSection;
|
||||
// } else {
|
||||
// return;
|
||||
// }
|
||||
// break;
|
||||
// case 'uspSection':
|
||||
// if (disabled !== true) {
|
||||
// Component = USPSection;
|
||||
// } else {
|
||||
// return;
|
||||
// }
|
||||
// break;
|
||||
// case 'reusableSection':
|
||||
// if (disabled !== true) {
|
||||
// Component = ReusableSection;
|
||||
// } else {
|
||||
// return;
|
||||
// }
|
||||
// break;
|
||||
// default:
|
||||
// return (
|
||||
// <div
|
||||
// className={`px-4 lg:px-8 2xl:px-16 ${
|
||||
// process.env.NODE_ENV === 'production' ? 'hidden' : ''
|
||||
// }`}
|
||||
// key={`index-${_key}`}
|
||||
// >
|
||||
// <span className="inline-flex items-center bg-red p-2 text-sm font-bold">
|
||||
// <InformationCircleIcon className="mr-1" />
|
||||
// {`No matching component (Type: ${_type})`}
|
||||
// </span>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
return Component ? (
|
||||
<Component key={`${_key}`} {...rest} />
|
||||
) : (
|
||||
<div key={`${_key}`}>Something else</div>
|
||||
);
|
||||
};
|
||||
// return Component ? (
|
||||
// <Component key={`${_key}`} {...rest} />
|
||||
// ) : (
|
||||
// <div key={`${_key}`}>Something else</div>
|
||||
// );
|
||||
// };
|
||||
|
||||
interface dynamicContentManagerProps {
|
||||
content: [] | any;
|
||||
@@ -89,7 +90,91 @@ interface dynamicContentManagerProps {
|
||||
|
||||
const DynamicContentManager = ({ content }: dynamicContentManagerProps) => {
|
||||
return (
|
||||
<div className="dynamic-content overflow-x-hidden">{content?.map(getContentComponent)}</div>
|
||||
<div className="dynamic-content overflow-x-hidden">
|
||||
{/* {content?.map(getContentComponent)} */}
|
||||
|
||||
{content.map(
|
||||
(
|
||||
component: { _type: string; _key: number; disabled: boolean; rest: any } | any,
|
||||
index: number
|
||||
) => {
|
||||
const { _type, _key, disabled, ...rest } = component;
|
||||
|
||||
let Component: any;
|
||||
|
||||
switch (_type) {
|
||||
case 'hero':
|
||||
if (disabled !== true) {
|
||||
Component = Hero;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'slider':
|
||||
if (disabled !== true) {
|
||||
Component = Slider;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'filteredProductList':
|
||||
if (disabled !== true) {
|
||||
Component = FilteredProductList;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'blurbSection':
|
||||
if (disabled !== true) {
|
||||
Component = BlurbSection;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'uspSection':
|
||||
if (disabled !== true) {
|
||||
Component = USPSection;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'reusableSection':
|
||||
if (disabled !== true) {
|
||||
Component = ReusableSection;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return (
|
||||
<div
|
||||
className={`px-4 lg:px-8 2xl:px-16 ${
|
||||
process.env.NODE_ENV === 'production' ? 'hidden' : ''
|
||||
}`}
|
||||
key={`index-${_key}`}
|
||||
>
|
||||
<span className="inline-flex items-center bg-red p-2 text-sm font-bold">
|
||||
<InformationCircleIcon className="mr-1" />
|
||||
{`No matching component (Type: ${_type})`}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (Component && index === 0) {
|
||||
return <Component key={`${_key}`} {...rest} />;
|
||||
} else if (Component) {
|
||||
return (
|
||||
<Suspense key={`${_key}`}>
|
||||
<Component {...rest} />
|
||||
</Suspense>
|
||||
);
|
||||
} else {
|
||||
<div key={`${_key}`}>Something else</div>;
|
||||
}
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -3,9 +3,14 @@ import OpenCart from 'components/cart/open-cart';
|
||||
import Logo from 'components/ui/logo/logo';
|
||||
import Link from 'next/link';
|
||||
import { Suspense } from 'react';
|
||||
import DesktopMenu from './desktop-menu';
|
||||
import HeaderRoot from './header-root';
|
||||
import MobileModal from './mobile-modal';
|
||||
import MainMenu from './main-menu/main-menu';
|
||||
import MobileMenuModal from './mobile-menu/modal';
|
||||
import OpenMobileMenu from './mobile-menu/open-mobile-menu';
|
||||
import SearchModal from './search/modal';
|
||||
import OpenSearch from './search/open-search';
|
||||
import UserModal from './user-menu/modal';
|
||||
import OpenUserMenu from './user-menu/open-user-menu';
|
||||
|
||||
interface HeaderProps {
|
||||
locale: string;
|
||||
@@ -16,8 +21,10 @@ const Header = ({ locale }: HeaderProps) => {
|
||||
<HeaderRoot>
|
||||
<div className="relative flex flex-col border-b border-ui-border bg-app">
|
||||
<div className="relative flex h-14 w-full items-center justify-between px-4 py-2 lg:h-16 lg:px-8 lg:py-3 2xl:px-16">
|
||||
<div className="md:hidden">
|
||||
<MobileModal />
|
||||
<div className="-translate-x-3 transform md:hidden">
|
||||
<Suspense fallback={<OpenMobileMenu />}>
|
||||
<MobileMenuModal locale={locale} />
|
||||
</Suspense>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
@@ -31,9 +38,15 @@ const Header = ({ locale }: HeaderProps) => {
|
||||
</div>
|
||||
|
||||
<div className="absolute left-1/2 top-1/2 hidden -translate-x-1/2 -translate-y-1/2 transform md:flex">
|
||||
<DesktopMenu locale={locale} />
|
||||
<MainMenu locale={locale} />
|
||||
</div>
|
||||
<div className="flex justify-end md:w-1/3">
|
||||
<div className="flex translate-x-3 transform justify-end space-x-1">
|
||||
<Suspense fallback={<OpenSearch />}>
|
||||
<SearchModal />
|
||||
</Suspense>
|
||||
<Suspense fallback={<OpenUserMenu />}>
|
||||
<UserModal />
|
||||
</Suspense>
|
||||
<Suspense fallback={<OpenCart />}>
|
||||
<Cart />
|
||||
</Suspense>
|
||||
|
@@ -2,11 +2,11 @@ import { categoriesQuery } from '@/lib/sanity/queries';
|
||||
import { clientFetch } from '@/lib/sanity/sanity.client';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface DesktopMenuProps {
|
||||
interface MainMenuProps {
|
||||
locale: string;
|
||||
}
|
||||
|
||||
export default async function DesktopMenu({ locale }: DesktopMenuProps) {
|
||||
export default async function MainMenu({ locale }: MainMenuProps) {
|
||||
const params = {
|
||||
locale: locale
|
||||
};
|
||||
@@ -18,7 +18,7 @@ export default async function DesktopMenu({ locale }: DesktopMenuProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<ul className="flex space-x-4 lg:space-x-6">
|
||||
<ul className="flex flex-col gap-4 lg:flex-row lg:gap-6">
|
||||
{categories.map((category: { slug: string } | any, index: number) => {
|
||||
return (
|
||||
<li className="font-medium" key={index}>
|
@@ -1,24 +1,30 @@
|
||||
'use client';
|
||||
|
||||
import MenuIcon from '@/components/icons/menu';
|
||||
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet';
|
||||
import { useState } from 'react';
|
||||
import MainMenu from '../main-menu/main-menu';
|
||||
import OpenMobileMenu from './open-mobile-menu';
|
||||
|
||||
export default function MobileModal() {
|
||||
interface MobileMenuModalProps {
|
||||
locale: string;
|
||||
}
|
||||
|
||||
export default function MobileMenuModal({ locale }: MobileMenuModalProps) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Sheet open={isOpen} onOpenChange={() => setIsOpen(!isOpen)}>
|
||||
<SheetTrigger aria-label="Open menu">
|
||||
<div className="relative flex h-11 w-11 items-center justify-center rounded-md border border-ui-border text-high-contrast transition-colors">
|
||||
<MenuIcon className="h-4 stroke-current transition-all ease-in-out hover:scale-110" />
|
||||
</div>
|
||||
<OpenMobileMenu />
|
||||
</SheetTrigger>
|
||||
<SheetContent side="left" className="bg-app">
|
||||
<SheetHeader>
|
||||
<SheetTitle className="text-lg font-semibold">Menu</SheetTitle>
|
||||
</SheetHeader>
|
||||
<div className="mt-4">
|
||||
<MainMenu locale={locale} />
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
</>
|
15
components/layout/header/mobile-menu/open-mobile-menu.tsx
Normal file
15
components/layout/header/mobile-menu/open-mobile-menu.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Bars3Icon } from '@heroicons/react/24/outline';
|
||||
import clsx from 'clsx';
|
||||
|
||||
export default function OpenMobileMenu({ className }: { className?: string }) {
|
||||
return (
|
||||
<div className="relative flex h-11 w-11 items-center justify-center text-high-contrast">
|
||||
<Bars3Icon
|
||||
className={clsx(
|
||||
'h-5 stroke-current transition-all ease-in-out hover:scale-110 ',
|
||||
className
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
26
components/layout/header/search/modal.tsx
Normal file
26
components/layout/header/search/modal.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
'use client';
|
||||
|
||||
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useState } from 'react';
|
||||
import OpenSearch from './open-search';
|
||||
|
||||
export default function SearchModal() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const t = useTranslations('search');
|
||||
|
||||
return (
|
||||
<>
|
||||
<Sheet open={isOpen} onOpenChange={() => setIsOpen(!isOpen)}>
|
||||
<SheetTrigger aria-label="Open search">
|
||||
<OpenSearch />
|
||||
</SheetTrigger>
|
||||
<SheetContent side="right" className="bg-app">
|
||||
<SheetHeader>
|
||||
<SheetTitle className="text-lg font-semibold">{t('search')}</SheetTitle>
|
||||
</SheetHeader>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
</>
|
||||
);
|
||||
}
|
12
components/layout/header/search/open-search.tsx
Normal file
12
components/layout/header/search/open-search.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { MagnifyingGlassIcon } from '@heroicons/react/24/outline';
|
||||
import clsx from 'clsx';
|
||||
|
||||
export default function OpenSearch({ className }: { className?: string }) {
|
||||
return (
|
||||
<div className="relative flex h-11 w-11 items-center justify-center text-high-contrast">
|
||||
<MagnifyingGlassIcon
|
||||
className={clsx('h-5 transition-all ease-in-out hover:scale-110 ', className)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
37
components/layout/header/user-menu/modal.tsx
Normal file
37
components/layout/header/user-menu/modal.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
'use client';
|
||||
|
||||
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useState } from 'react';
|
||||
import OpenUserMenu from './open-user-menu';
|
||||
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
|
||||
export default function UserModal() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const t = useTranslations('auth');
|
||||
|
||||
return (
|
||||
<>
|
||||
<Sheet open={isOpen} onOpenChange={() => setIsOpen(!isOpen)}>
|
||||
<SheetTrigger aria-label="Open search">
|
||||
<OpenUserMenu />
|
||||
</SheetTrigger>
|
||||
<SheetContent side="right" className="bg-app">
|
||||
<SheetHeader>
|
||||
<SheetTitle className="text-lg font-semibold">{t('login.logIn')}</SheetTitle>
|
||||
</SheetHeader>
|
||||
|
||||
<Tabs defaultValue="login" className="mt-4 w-full">
|
||||
<TabsList>
|
||||
<TabsTrigger value="login">{t('login.logIn')}</TabsTrigger>
|
||||
<TabsTrigger value="register">{t('signUp.register')}</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="login">Log in to your account here.</TabsContent>
|
||||
<TabsContent value="register">Register for account here.</TabsContent>
|
||||
</Tabs>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
</>
|
||||
);
|
||||
}
|
15
components/layout/header/user-menu/open-user-menu.tsx
Normal file
15
components/layout/header/user-menu/open-user-menu.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { UserCircleIcon } from '@heroicons/react/24/outline';
|
||||
import clsx from 'clsx';
|
||||
|
||||
export default function OpenUserMenu({ className }: { className?: string }) {
|
||||
return (
|
||||
<div className="relative flex h-11 w-11 items-center justify-center text-high-contrast">
|
||||
<UserCircleIcon
|
||||
className={clsx(
|
||||
'h-5 stroke-current transition-all ease-in-out hover:scale-110 ',
|
||||
className
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user