Test suspense in dynamic content manager

This commit is contained in:
Henrik Larsson 2023-08-10 15:16:45 +02:00
parent 6088aa98b9
commit 04477fe83a
34 changed files with 410 additions and 310 deletions

View File

@ -34,7 +34,7 @@ export default async function ProductPage({ params }: CategoryPageParams) {
const { title } = category;
return (
<div className="mb-8 flex w-full flex-col px-4 lg:my-16 lg:px-8 2xl:px-16">
<div className="my-8 flex w-full flex-col px-4 lg:my-16 lg:px-8 2xl:px-16">
<Text variant={'pageHeading'}>{title}</Text>
</div>
);

View File

@ -1,6 +1,6 @@
'use client';
import { PlusIcon } from '@radix-ui/react-icons';
import { PlusIcon } from '@heroicons/react/24/outline';
import clsx from 'clsx';
import { addItem } from 'components/cart/actions';
import LoadingDots from 'components/loading-dots';

View File

@ -1,10 +1,10 @@
import { XMarkIcon } from '@heroicons/react/24/outline';
import clsx from 'clsx';
import CloseIcon from 'components/icons/close';
export default function CloseCart({ className }: { className?: string }) {
return (
<div className="relative flex h-11 w-11 items-center justify-center rounded-md border border-neutral-200 text-black transition-colors dark:border-neutral-700 dark:text-white">
<CloseIcon className={clsx('h-6 transition-all ease-in-out hover:scale-110 ', className)} />
<XMarkIcon className={clsx('h-6 transition-all ease-in-out hover:scale-110 ', className)} />
</div>
);
}

View File

@ -1,7 +1,7 @@
import CloseIcon from 'components/icons/close';
import LoadingDots from 'components/loading-dots';
import { useRouter } from 'next/navigation';
import { XMarkIcon } from '@heroicons/react/24/outline';
import clsx from 'clsx';
import { removeItem } from 'components/cart/actions';
import type { CartItem } from 'lib/shopify/types';
@ -38,7 +38,7 @@ export default function DeleteItemButton({ item }: { item: CartItem }) {
{isPending ? (
<LoadingDots className="bg-white" />
) : (
<CloseIcon className="hover:text-accent-3 mx-[1px] h-4 w-4 text-white " />
<XMarkIcon className="hover:text-accent-3 mx-[1px] h-4 w-4 text-white " />
)}
</button>
);

View File

@ -1,10 +1,9 @@
import { useRouter } from 'next/navigation';
import { useTransition } from 'react';
import { MinusIcon, PlusIcon } from '@heroicons/react/24/outline';
import clsx from 'clsx';
import { removeItem, updateItemQuantity } from 'components/cart/actions';
import MinusIcon from 'components/icons/minus';
import PlusIcon from 'components/icons/plus';
import LoadingDots from 'components/loading-dots';
import type { CartItem } from 'lib/shopify/types';

View File

@ -1,7 +1,7 @@
'use client';
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet';
import ShoppingBagIcon from 'components/icons/shopping-bag';
import { ShoppingBagIcon } from '@heroicons/react/24/outline';
import Price from 'components/price';
import { DEFAULT_OPTION } from 'lib/constants';
import type { Cart } from 'lib/shopify/types';

View File

@ -1,5 +1,5 @@
import { ShoppingBagIcon } from '@heroicons/react/24/outline';
import clsx from 'clsx';
import ShoppingBagIcon from 'components/icons/shopping-bag';
export default function OpenCart({
className,
@ -9,10 +9,10 @@ export default function OpenCart({
quantity?: number;
}) {
return (
<div className="relative flex h-11 w-11 items-center justify-center rounded-md border border-ui-border text-high-contrast transition-colors">
<div className="relative flex h-11 w-11 items-center justify-center text-high-contrast">
<ShoppingBagIcon
className={clsx(
'h-4 stroke-current transition-all ease-in-out hover:scale-110 ',
'h-5 stroke-current transition-all ease-in-out hover:scale-110 ',
className
)}
/>

View File

@ -1,14 +0,0 @@
export default function ArrowLeftIcon({ className }: { className?: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
shapeRendering="geometricPrecision"
className={className}
>
<path d="M19 12H5" />
<path d="M12 19L5 12L12 5" />
</svg>
);
}

View File

@ -1,17 +0,0 @@
export default function CaretRightIcon({ className }: { className?: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
shapeRendering="geometricPrecision"
className={className}
>
<path d="M9 18l6-6-6-6" />
</svg>
);
}

View File

@ -1,26 +0,0 @@
import clsx from 'clsx';
import ShoppingBagIcon from './shopping-bag';
export default function CartIcon({
className,
quantity
}: {
className?: string;
quantity?: number;
}) {
return (
<div className="relative">
<ShoppingBagIcon
className={clsx(
'h-6 transition-all ease-in-out hover:scale-110 hover:text-gray-500 dark:hover:text-gray-300',
className
)}
/>
{quantity ? (
<div className="absolute bottom-0 left-0 -mb-3 -ml-3 flex h-5 w-5 items-center justify-center rounded-full border-2 border-white bg-black text-xs text-white dark:border-black dark:bg-white dark:text-black">
{quantity}
</div>
) : null}
</div>
);
}

View File

@ -1,17 +0,0 @@
export default function CloseIcon({ className }: { className?: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
shapeRendering="geometricPrecision"
className={className}
>
<path d="M18 6L6 18" />
<path d="M6 6l12 12" />
</svg>
);
}

View File

@ -1,18 +0,0 @@
export default function LanguageIcon({ className }: { className?: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth="1.5"
stroke="currentColor"
className={className}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M10.5 21l5.25-11.25L21 21m-9-3h7.5M3 5.621a48.474 48.474 0 016-.371m0 0c1.12 0 2.233.038 3.334.114M9 5.25V3m3.334 2.364C11.176 10.658 7.69 15.08 3 17.502m9.334-12.138c.896.061 1.785.147 2.666.257m-4.589 8.495a18.023 18.023 0 01-3.827-5.802"
/>
</svg>
);
}

View File

@ -1,19 +0,0 @@
export default function LogoIcon({ className }: { className?: string }) {
return (
<svg
aria-label={`${process.env.SITE_NAME} logo`}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 109 80"
className={className}
>
<path
fill="currentColor"
d="M54.6,0C32.8,0,15.1,17.9,15.1,40c0,10.6,4.3,18.1,4.6,18.8h20.6c-0.7-0.5-9.1-6.9-9.1-18.8 c0-13.1,10.5-23.7,23.4-23.7S78,26.9,78,40S67.5,63.7,54.6,63.7H0V80h54.6c21.8,0,39.5-17.9,39.5-40S76.5,0,54.6,0z"
/>
<path
fill="currentColor"
d="M109,63.7V80H75.3c7.2-3.7,13.2-9.4,17.4-16.3H109z"
/>
</svg>
);
}

View File

@ -1,16 +0,0 @@
export default function MenuIcon({ className }: { className?: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
shapeRendering="geometricPrecision"
className={className}
>
<path d="M4 6h16M4 12h16m-7 6h7" />
</svg>
);
}

View File

@ -1,16 +0,0 @@
export default function MinusIcon({ className }: { className?: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
shapeRendering="geometricPrecision"
className={className}
>
<path d="M5 12H19" />
</svg>
);
}

View File

@ -1,17 +0,0 @@
export default function PlusIcon({ className }: { className?: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
shapeRendering="geometricPrecision"
className={className}
>
<path d="M12 5V19" />
<path d="M5 12H19" />
</svg>
);
}

View File

@ -1,11 +0,0 @@
export default function SearchIcon({ className }: { className?: string }) {
return (
<svg className={className} fill="currentColor" viewBox="0 0 20 20">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
/>
</svg>
);
}

View File

@ -1,19 +0,0 @@
export default function ShoppingBagIcon({ className }: { className?: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 22"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
shapeRendering="geometricPrecision"
className={className}
>
<path d="M4 1L1 5V19C1 19.5304 1.21071 20.0391 1.58579 20.4142C1.96086 20.7893 2.46957 21 3 21H17C17.5304 21 18.0391 20.7893 18.4142 20.4142C18.7893 20.0391 19 19.5304 19 19V5L16 1H4Z" />
<path d="M1 5H19" />
<path d="M14 9C14 10.0609 13.5786 11.0783 12.8284 11.8284C12.0783 12.5786 11.0609 13 10 13C8.93913 13 7.92172 12.5786 7.17157 11.8284C6.42143 11.0783 6 10.0609 6 9" />
</svg>
);
}

View File

@ -6,15 +6,100 @@ 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;
// 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>
// );
// };
interface dynamicContentManagerProps {
content: [] | any;
}
const getContentComponent = ({ _type, _key, disabled, ...rest }: getContentComponentProps) => {
const DynamicContentManager = ({ content }: dynamicContentManagerProps) => {
return (
<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) {
@ -69,27 +154,27 @@ const getContentComponent = ({ _type, _key, disabled, ...rest }: getContentCompo
key={`index-${_key}`}
>
<span className="inline-flex items-center bg-red p-2 text-sm font-bold">
<InfoCircledIcon className="mr-1" />
<InformationCircleIcon className="mr-1" />
{`No matching component (Type: ${_type})`}
</span>
</div>
);
}
return Component ? (
<Component key={`${_key}`} {...rest} />
) : (
<div key={`${_key}`}>Something else</div>
);
};
interface dynamicContentManagerProps {
content: [] | any;
}
const DynamicContentManager = ({ content }: dynamicContentManagerProps) => {
if (Component && index === 0) {
return <Component key={`${_key}`} {...rest} />;
} else if (Component) {
return (
<div className="dynamic-content overflow-x-hidden">{content?.map(getContentComponent)}</div>
<Suspense key={`${_key}`}>
<Component {...rest} />
</Suspense>
);
} else {
<div key={`${_key}`}>Something else</div>;
}
}
)}
</div>
);
};

View File

@ -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>

View File

@ -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}>

View File

@ -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>
</>

View 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>
);
}

View 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>
</>
);
}

View 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>
);
}

View 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>
</>
);
}

View 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>
);
}

View File

@ -1,4 +1,4 @@
import { ArrowLeftIcon, ArrowRightIcon } from '@radix-ui/react-icons';
import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline';
import 'glider-js/glider.min.css';
import React from 'react';
import Glider from 'react-glider';

View File

@ -1,7 +1,7 @@
'use client';
import { CheckIcon, ChevronRightIcon } from '@heroicons/react/24/outline';
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
import { CheckIcon, ChevronRightIcon, CircleIcon } from '@radix-ui/react-icons';
import * as React from 'react';
@ -128,7 +128,7 @@ const DropdownMenuRadioItem = React.forwardRef<
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<CircleIcon className="h-2 w-2 fill-current" />
<div className="h-2 w-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}

View File

@ -6,7 +6,7 @@ import {
DropdownMenuItem,
DropdownMenuTrigger
} from '@/components/ui/dropdown-menu';
import LanguageIcon from 'components/icons/language';
import { LanguageIcon } from '@heroicons/react/24/outline';
import { useLocale, useTranslations } from 'next-intl';
import Link from 'next/link';
import { usePathname } from 'next/navigation';

View File

@ -1,7 +1,7 @@
'use client';
import { XMarkIcon } from '@heroicons/react/24/outline';
import * as SheetPrimitive from '@radix-ui/react-dialog';
import { Cross1Icon } from '@radix-ui/react-icons';
import { cva, type VariantProps } from 'class-variance-authority';
import * as React from 'react';
@ -65,7 +65,7 @@ const SheetContent = React.forwardRef<
<SheetPrimitive.Content ref={ref} className={cn(sheetVariants({ side }), className)} {...props}>
{children}
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary md:right-6 md:top-6">
<Cross1Icon className="h-6 w-6" />
<XMarkIcon className="h-6 w-6" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
</SheetPrimitive.Content>

55
components/ui/tabs.tsx Normal file
View File

@ -0,0 +1,55 @@
"use client"
import * as React from "react"
import * as TabsPrimitive from "@radix-ui/react-tabs"
import { cn } from "@/lib/utils"
const Tabs = TabsPrimitive.Root
const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => (
<TabsPrimitive.List
ref={ref}
className={cn(
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
className
)}
{...props}
/>
))
TabsList.displayName = TabsPrimitive.List.displayName
const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger
ref={ref}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
className
)}
{...props}
/>
))
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
const TabsContent = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Content
ref={ref}
className={cn(
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
className
)}
{...props}
/>
))
TabsContent.displayName = TabsPrimitive.Content.displayName
export { Tabs, TabsList, TabsTrigger, TabsContent }

View File

@ -19,14 +19,14 @@
"*": "prettier --write --ignore-unknown"
},
"dependencies": {
"@heroicons/react": "^2.0.18",
"@next/bundle-analyzer": "^13.4.3",
"@portabletext/react": "^3.0.0",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.0.4",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-navigation-menu": "^1.1.2",
"@radix-ui/react-tabs": "^1.0.4",
"@sanity/client": "^6.4.4",
"@sanity/icons": "^2.3.1",
"@sanity/image-url": "^1.0.2",
"@sanity/preview-kit": "^2.4.9",
"@sanity/types": "^3.11.1",
@ -72,4 +72,3 @@
"typescript": "^5.1.3"
}
}

52
pnpm-lock.yaml generated
View File

@ -1,15 +1,15 @@
lockfileVersion: 5.4
specifiers:
'@heroicons/react': ^2.0.18
'@next/bundle-analyzer': ^13.4.3
'@playwright/test': ^1.34.1
'@portabletext/react': ^3.0.0
'@radix-ui/react-dialog': ^1.0.4
'@radix-ui/react-dropdown-menu': ^2.0.4
'@radix-ui/react-icons': ^1.3.0
'@radix-ui/react-navigation-menu': ^1.1.2
'@radix-ui/react-tabs': ^1.0.4
'@sanity/client': ^6.4.4
'@sanity/icons': ^2.3.1
'@sanity/image-url': ^1.0.2
'@sanity/preview-kit': ^2.4.9
'@sanity/types': ^3.11.1
@ -52,14 +52,14 @@ specifiers:
typescript: ^5.1.3
dependencies:
'@heroicons/react': 2.0.18_react@18.2.0
'@next/bundle-analyzer': 13.4.13
'@portabletext/react': 3.0.4_react@18.2.0
'@radix-ui/react-dialog': 1.0.4_ml6diaeoljuxdq7psn5bilsrme
'@radix-ui/react-dropdown-menu': 2.0.5_ml6diaeoljuxdq7psn5bilsrme
'@radix-ui/react-icons': 1.3.0_react@18.2.0
'@radix-ui/react-navigation-menu': 1.1.3_ml6diaeoljuxdq7psn5bilsrme
'@radix-ui/react-tabs': 1.0.4_ml6diaeoljuxdq7psn5bilsrme
'@sanity/client': 6.4.4
'@sanity/icons': 2.4.1_react@18.2.0
'@sanity/image-url': 1.0.2
'@sanity/preview-kit': 2.4.9_esptxo4lplqmefuoebcrocvktm
'@sanity/types': 3.15.0
@ -790,6 +790,14 @@ packages:
tslib: 2.6.1
dev: false
/@heroicons/react/2.0.18_react@18.2.0:
resolution: {integrity: sha512-7TyMjRrZZMBPa+/5Y8lN0iyvUU/01PeMGX2+RE7cQWpEUIcb4QotzUObFkJDejj/HUH4qjP/eQ0gzzKs2f+6Yw==}
peerDependencies:
react: '>= 16'
dependencies:
react: 18.2.0
dev: false
/@humanwhocodes/config-array/0.11.10:
resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
engines: {node: '>=10.10.0'}
@ -1274,14 +1282,6 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
/@radix-ui/react-icons/1.3.0_react@18.2.0:
resolution: {integrity: sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==}
peerDependencies:
react: ^16.x || ^17.x || ^18.x
dependencies:
react: 18.2.0
dev: false
/@radix-ui/react-id/1.0.1_6kgymidexis2l23kiss4by6rqm:
resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==}
peerDependencies:
@ -1507,6 +1507,34 @@ packages:
react: 18.2.0
dev: false
/@radix-ui/react-tabs/1.0.4_ml6diaeoljuxdq7psn5bilsrme:
resolution: {integrity: sha512-egZfYY/+wRNCflXNHx+dePvnz9FbmssDTJBtgRfDY7e8SE5oIo3Py2eCB1ckAbh1Q7cQ/6yJZThJ++sgbxibog==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.22.10
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-context': 1.0.1_6kgymidexis2l23kiss4by6rqm
'@radix-ui/react-direction': 1.0.1_6kgymidexis2l23kiss4by6rqm
'@radix-ui/react-id': 1.0.1_6kgymidexis2l23kiss4by6rqm
'@radix-ui/react-presence': 1.0.1_ml6diaeoljuxdq7psn5bilsrme
'@radix-ui/react-primitive': 1.0.3_ml6diaeoljuxdq7psn5bilsrme
'@radix-ui/react-roving-focus': 1.0.4_ml6diaeoljuxdq7psn5bilsrme
'@radix-ui/react-use-controllable-state': 1.0.1_6kgymidexis2l23kiss4by6rqm
'@types/react': 18.2.19
'@types/react-dom': 18.2.7
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
dev: false
/@radix-ui/react-use-callback-ref/1.0.1_6kgymidexis2l23kiss4by6rqm:
resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==}
peerDependencies: