@thorprovider/blocks 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +24 -0
- package/.turbo/turbo-test.log +10 -0
- package/.turbo/turbo-type-check.log +0 -0
- package/AGENTS.md +1 -0
- package/README.md +161 -0
- package/components.json +33 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/contracts.d.mts +563 -0
- package/dist/contracts.d.ts +563 -0
- package/dist/contracts.js +207 -0
- package/dist/contracts.js.map +1 -0
- package/dist/contracts.mjs +168 -0
- package/dist/contracts.mjs.map +1 -0
- package/dist/index.d.mts +2408 -0
- package/dist/index.d.ts +2408 -0
- package/dist/index.js +16422 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +16352 -0
- package/dist/index.mjs.map +1 -0
- package/docs/standards/data-contracts.md +70 -0
- package/docs/standards/theming.md +85 -0
- package/package.json +62 -0
- package/src/blocks/application/app-shells/app-shell-1-sidebar.tsx +198 -0
- package/src/blocks/application/app-shells/app-shell-1.tsx +196 -0
- package/src/blocks/application/login-forms/login-page-1.tsx +246 -0
- package/src/blocks/application/login-forms/login-page-2.tsx +207 -0
- package/src/blocks/application/password-recovery/forgot-password-page-1-brand-mark.tsx +38 -0
- package/src/blocks/application/password-recovery/forgot-password-page-1.tsx +320 -0
- package/src/blocks/application/signup-forms/signup-page-1.tsx +231 -0
- package/src/blocks/application/verification/verify-1.tsx +307 -0
- package/src/blocks/application/verification/verify-confetti.tsx +192 -0
- package/src/blocks/e-commerce/category-filters/category-filter-1.tsx +260 -0
- package/src/blocks/e-commerce/category-filters/category-filter-2-filter-panel.tsx +179 -0
- package/src/blocks/e-commerce/category-filters/category-filter-2.tsx +341 -0
- package/src/blocks/e-commerce/category-filters/category-filter-4-chip-rail.tsx +57 -0
- package/src/blocks/e-commerce/category-filters/category-filter-4-drawer.tsx +124 -0
- package/src/blocks/e-commerce/category-filters/category-filter-4-fields.tsx +170 -0
- package/src/blocks/e-commerce/category-filters/category-filter-4-sidebar.tsx +52 -0
- package/src/blocks/e-commerce/category-filters/category-filter-4-sort-sheet.tsx +57 -0
- package/src/blocks/e-commerce/category-filters/category-filter-4-state.ts +68 -0
- package/src/blocks/e-commerce/category-filters/category-filter-4.tsx +298 -0
- package/src/blocks/e-commerce/checkout-forms/card-formatting.test.ts +47 -0
- package/src/blocks/e-commerce/checkout-forms/card-formatting.ts +9 -0
- package/src/blocks/e-commerce/checkout-forms/checkbox-check.tsx +74 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-confirmation-fulfilment-progress.tsx +189 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-confirmation.tsx +283 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-field-feedback.tsx +29 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-1-contact-step.tsx +96 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-1-country-field.tsx +51 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-1-order-summary.tsx +139 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-1-payment-step.tsx +184 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-1-progress.tsx +36 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-1-shipping-step.tsx +107 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-1.tsx +235 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-2-order-summary.tsx +130 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-2-payment-card.tsx +175 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-2-shipping-card.tsx +190 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-2.tsx +312 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-3-information-step.tsx +91 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-3-order-summary.tsx +110 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-3-payment-step.tsx +137 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-3-progress.tsx +68 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-3-review-step.tsx +79 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-3-shipping-step.tsx +172 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-3.tsx +392 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-form-types.ts +7 -0
- package/src/blocks/e-commerce/navigation/category-icon.test.tsx +54 -0
- package/src/blocks/e-commerce/navigation/category-icon.tsx +94 -0
- package/src/blocks/e-commerce/navigation/mega-menu.test.tsx +139 -0
- package/src/blocks/e-commerce/navigation/mega-menu.tsx +271 -0
- package/src/blocks/e-commerce/navigation/mobile-category-drawer.test.tsx +152 -0
- package/src/blocks/e-commerce/navigation/mobile-category-drawer.tsx +276 -0
- package/src/blocks/e-commerce/order-history/order-history-1.tsx +159 -0
- package/src/blocks/e-commerce/order-history/order-history-2.tsx +222 -0
- package/src/blocks/e-commerce/product-categories/avatar-group.tsx +86 -0
- package/src/blocks/e-commerce/product-categories/product-category-1.tsx +126 -0
- package/src/blocks/e-commerce/product-categories/product-category-2.tsx +100 -0
- package/src/blocks/e-commerce/product-categories/product-category-4.tsx +129 -0
- package/src/blocks/e-commerce/product-categories/product-category-5.tsx +218 -0
- package/src/blocks/e-commerce/product-list/product-card-1.tsx +131 -0
- package/src/blocks/e-commerce/product-list/product-card-2.tsx +71 -0
- package/src/blocks/e-commerce/product-list/product-card-3-reaction-toggle.tsx +166 -0
- package/src/blocks/e-commerce/product-list/product-card-3.tsx +215 -0
- package/src/blocks/e-commerce/product-list/product-card-5-checkbox-check.tsx +98 -0
- package/src/blocks/e-commerce/product-list/product-card-5-reaction-toggle.tsx +171 -0
- package/src/blocks/e-commerce/product-list/product-card-5.tsx +367 -0
- package/src/blocks/e-commerce/product-overview/product-overview-1.tsx +201 -0
- package/src/blocks/e-commerce/product-overview/product-overview-2.tsx +272 -0
- package/src/blocks/e-commerce/product-overview/product-overview-5-gallery.tsx +59 -0
- package/src/blocks/e-commerce/product-overview/product-overview-5-lightbox-tab.tsx +104 -0
- package/src/blocks/e-commerce/product-overview/product-overview-5-reviews-tab.tsx +82 -0
- package/src/blocks/e-commerce/product-overview/product-overview-5-tabs.tsx +69 -0
- package/src/blocks/e-commerce/product-overview/product-overview-5.tsx +274 -0
- package/src/blocks/e-commerce/product-overview/product-quick-view-card.tsx +135 -0
- package/src/blocks/e-commerce/product-overview/product-quick-view-dialog.tsx +393 -0
- package/src/blocks/e-commerce/product-overview/product-quick-view.tsx +136 -0
- package/src/blocks/e-commerce/reviews/review-rating-1-review-form.tsx +133 -0
- package/src/blocks/e-commerce/reviews/review-rating-1.tsx +178 -0
- package/src/blocks/e-commerce/reviews/review-rating-2.tsx +246 -0
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-1-spinning-counter.tsx +142 -0
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-1.tsx +291 -0
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-2-item-row.tsx +110 -0
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-2-recommendations.tsx +51 -0
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-2-spinning-counter.tsx +146 -0
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-2.tsx +295 -0
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-3-spinning-counter.tsx +142 -0
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-3.tsx +342 -0
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-4-spinning-counter.tsx +146 -0
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-4.tsx +391 -0
- package/src/blocks/e-commerce/store-containers/store-container-1.tsx +29 -0
- package/src/blocks/e-commerce/storefront-hero/storefront-hero-1.tsx +244 -0
- package/src/blocks/e-commerce/storefront-hero/storefront-hero-2.tsx +283 -0
- package/src/blocks/e-commerce/storefront-hero/storefront-hero-5.tsx +318 -0
- package/src/blocks/e-commerce/storefront-hero/storefront-hero-6.tsx +205 -0
- package/src/blocks/marketing/banners/avatar-group.tsx +69 -0
- package/src/blocks/marketing/banners/banner-1.tsx +188 -0
- package/src/blocks/marketing/cta/cta-section-1.tsx +112 -0
- package/src/blocks/marketing/faq/faq-block-1.tsx +45 -0
- package/src/blocks/marketing/features/feature-1.tsx +97 -0
- package/src/blocks/marketing/features/feature-2.tsx +82 -0
- package/src/blocks/marketing/footers/footer-section-1.tsx +141 -0
- package/src/blocks/marketing/footers/footer-section-3.tsx +192 -0
- package/src/blocks/marketing/footers/footer-section-4.tsx +355 -0
- package/src/blocks/marketing/hero-sections/avatar-group.tsx +86 -0
- package/src/blocks/marketing/hero-sections/hero-section-1.tsx +135 -0
- package/src/blocks/marketing/logo-clouds/logo-cloud-1-logo-icon.tsx +52 -0
- package/src/blocks/marketing/logo-clouds/logo-cloud-1.tsx +56 -0
- package/src/blocks/marketing/navbars/navbar-1.tsx +267 -0
- package/src/blocks/marketing/navbars/navbar-2.tsx +351 -0
- package/src/blocks/marketing/navbars/navbar-3.tsx +303 -0
- package/src/blocks/marketing/navbars/navbar-4-account-menu.tsx +118 -0
- package/src/blocks/marketing/navbars/navbar-4-cart-sheet.tsx +248 -0
- package/src/blocks/marketing/navbars/navbar-4-like-button.tsx +177 -0
- package/src/blocks/marketing/navbars/navbar-4-mobile-nav.tsx +104 -0
- package/src/blocks/marketing/navbars/navbar-4-product-grid.tsx +219 -0
- package/src/blocks/marketing/navbars/navbar-4-search-bar.tsx +52 -0
- package/src/blocks/marketing/navbars/navbar-4.tsx +411 -0
- package/src/blocks/marketing/pricing/pricing-section-1.tsx +94 -0
- package/src/blocks/marketing/statistics/statistic-1.tsx +93 -0
- package/src/blocks/marketing/testimonials/testimonial-1.tsx +141 -0
- package/src/blocks/marketing/testimonials/testimonial-2.tsx +108 -0
- package/src/blocks/shared/reaction-toggle.tsx +145 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/index.ts +200 -0
- package/src/lib/contracts.test.ts +32 -0
- package/src/lib/contracts.ts +235 -0
- package/src/lib/types/menu.ts +43 -0
- package/src/lib/types.ts +4 -0
- package/src/lib/utils.ts +10 -0
- package/src/ui/accordion.tsx +65 -0
- package/src/ui/avatar.tsx +108 -0
- package/src/ui/badge.tsx +47 -0
- package/src/ui/breadcrumb.tsx +108 -0
- package/src/ui/button-group.tsx +83 -0
- package/src/ui/button.tsx +63 -0
- package/src/ui/card.tsx +91 -0
- package/src/ui/carousel.tsx +241 -0
- package/src/ui/dialog.tsx +158 -0
- package/src/ui/drawer.tsx +134 -0
- package/src/ui/dropdown-menu.tsx +256 -0
- package/src/ui/empty.tsx +103 -0
- package/src/ui/field.tsx +248 -0
- package/src/ui/input-group.tsx +170 -0
- package/src/ui/input-otp.tsx +76 -0
- package/src/ui/input.tsx +20 -0
- package/src/ui/label.tsx +23 -0
- package/src/ui/navigation-menu.tsx +167 -0
- package/src/ui/pagination.tsx +127 -0
- package/src/ui/progress.tsx +30 -0
- package/src/ui/radio-group.tsx +44 -0
- package/src/ui/select.tsx +189 -0
- package/src/ui/separator.tsx +27 -0
- package/src/ui/sheet.tsx +142 -0
- package/src/ui/sidebar.tsx +726 -0
- package/src/ui/skeleton.tsx +13 -0
- package/src/ui/table.tsx +115 -0
- package/src/ui/tabs.tsx +90 -0
- package/src/ui/textarea.tsx +17 -0
- package/src/ui/toggle-group.tsx +83 -0
- package/src/ui/toggle.tsx +46 -0
- package/src/ui/tooltip.tsx +56 -0
- package/tsconfig.json +20 -0
- package/tsup.config.ts +16 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Sparkles } from 'lucide-react'
|
|
2
|
+
|
|
3
|
+
import { LogoCloud1LogoIcon } from './logo-cloud-1-logo-icon'
|
|
4
|
+
|
|
5
|
+
export interface LogoCloud1Platform {
|
|
6
|
+
name: string
|
|
7
|
+
id: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface LogoCloud1Props {
|
|
11
|
+
platforms: LogoCloud1Platform[]
|
|
12
|
+
title?: string
|
|
13
|
+
description?: string
|
|
14
|
+
moreLabel?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function LogoCloud1({ platforms, title, description, moreLabel }: LogoCloud1Props) {
|
|
18
|
+
return (
|
|
19
|
+
<section className='relative w-full py-12 md:py-16'>
|
|
20
|
+
<div className='mx-auto w-full max-w-7xl px-4 sm:px-6 lg:px-8'>
|
|
21
|
+
<div className='mx-auto max-w-7xl'>
|
|
22
|
+
{(title || description) && (
|
|
23
|
+
<div className='mb-12 flex flex-col gap-4 text-center'>
|
|
24
|
+
{title && (
|
|
25
|
+
<div className='flex items-center justify-center gap-2'>
|
|
26
|
+
<Sparkles className='text-primary size-5' aria-hidden='true' />
|
|
27
|
+
<span className='text-primary text-sm font-medium'>{title}</span>
|
|
28
|
+
</div>
|
|
29
|
+
)}
|
|
30
|
+
{description && <p className='text-muted-foreground'>{description}</p>}
|
|
31
|
+
</div>
|
|
32
|
+
)}
|
|
33
|
+
|
|
34
|
+
<div className='relative overflow-hidden'>
|
|
35
|
+
<div className='flex flex-wrap items-center justify-center gap-8 md:gap-12'>
|
|
36
|
+
{platforms.map(platform => (
|
|
37
|
+
<div key={platform.id} className='flex items-center gap-2 opacity-85'>
|
|
38
|
+
<LogoCloud1LogoIcon iconSlug={platform.id} size={22} />
|
|
39
|
+
<span className='text-md font-medium'>{platform.name}</span>
|
|
40
|
+
</div>
|
|
41
|
+
))}
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
{moreLabel && (
|
|
46
|
+
<div className='mt-12 text-center'>
|
|
47
|
+
<p className='text-muted-foreground text-sm'>{moreLabel}</p>
|
|
48
|
+
</div>
|
|
49
|
+
)}
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</section>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default LogoCloud1
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import { motion, useReducedMotion } from 'motion/react'
|
|
5
|
+
import { Menu, ShoppingCart } from 'lucide-react'
|
|
6
|
+
|
|
7
|
+
import { Button } from '../../../ui/button'
|
|
8
|
+
import { Separator } from '../../../ui/separator'
|
|
9
|
+
import {
|
|
10
|
+
Sheet,
|
|
11
|
+
SheetClose,
|
|
12
|
+
SheetContent,
|
|
13
|
+
SheetHeader,
|
|
14
|
+
SheetTitle,
|
|
15
|
+
SheetTrigger,
|
|
16
|
+
} from '../../../ui/sheet'
|
|
17
|
+
import { cn } from '../../../lib/utils'
|
|
18
|
+
import type { CTA } from '../../../lib/types'
|
|
19
|
+
|
|
20
|
+
export interface NavLink {
|
|
21
|
+
label: string
|
|
22
|
+
href?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface Navbar1Props {
|
|
26
|
+
navLinks: NavLink[]
|
|
27
|
+
logoText: string
|
|
28
|
+
logoHref?: string
|
|
29
|
+
currentPath?: string
|
|
30
|
+
signIn?: CTA
|
|
31
|
+
cta?: CTA
|
|
32
|
+
/** Number of items in the cart. When provided, a cart button with badge is shown. */
|
|
33
|
+
cartCount?: number
|
|
34
|
+
/** Fired when the user opens the cart. */
|
|
35
|
+
onOpenCart?: () => void
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function Logo({ text, href }: { text: string; href?: string }) {
|
|
39
|
+
const className =
|
|
40
|
+
'focus-visible:ring-ring flex items-center gap-2 rounded-sm focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none'
|
|
41
|
+
|
|
42
|
+
const content = (
|
|
43
|
+
<>
|
|
44
|
+
<svg viewBox='0 0 32 32' aria-hidden='true' className='text-primary size-7' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
|
45
|
+
<path
|
|
46
|
+
d='M26 24.75C26.4142 24.75 26.75 24.4142 26.75 24C26.75 23.5858 26.4142 23.25 26 23.25V24.75ZM26 23.25H11V24.75H26V23.25ZM8.75 21V15H7.25V21H8.75ZM11 23.25C9.75736 23.25 8.75 22.2426 8.75 21H7.25C7.25 23.0711 8.92893 24.75 11 24.75V23.25Z'
|
|
47
|
+
fill='currentColor'
|
|
48
|
+
/>
|
|
49
|
+
<path
|
|
50
|
+
d='M1.5 3.25C1.08579 3.25 0.75 3.58579 0.75 4C0.75 4.41421 1.08579 4.75 1.5 4.75V3.25ZM1.5 4.75H6V3.25H1.5V4.75ZM7.25 6V21H8.75V6H7.25ZM6 4.75C6.69036 4.75 7.25 5.30964 7.25 6H8.75C8.75 4.48122 7.51878 3.25 6 3.25V4.75Z'
|
|
51
|
+
fill='currentColor'
|
|
52
|
+
/>
|
|
53
|
+
<path
|
|
54
|
+
d='M22 21.75C22.4142 21.75 22.75 21.4142 22.75 21C22.75 20.5858 22.4142 20.25 22 20.25V21.75ZM22 20.25H11V21.75H22V20.25ZM8.75 18V12H7.25V18H8.75ZM11 20.25C9.75736 20.25 8.75 19.2426 8.75 18H7.25C7.25 20.0711 8.92893 21.75 11 21.75V20.25Z'
|
|
55
|
+
fill='currentColor'
|
|
56
|
+
/>
|
|
57
|
+
<path
|
|
58
|
+
d='M27.2057 19.754C27.0654 20.1438 26.6357 20.346 26.246 20.2057C25.8562 20.0654 25.654 19.6357 25.7943 19.246L27.2057 19.754ZM30.0361 9.67744L29.3305 9.4234L29.3305 9.4234L30.0361 9.67744ZM25.7943 19.246L29.3305 9.4234L30.7418 9.93148L27.2057 19.754L25.7943 19.246ZM28.1543 7.75L8 7.75V6.25L28.1543 6.25V7.75ZM29.3305 9.4234C29.6237 8.60882 29.0201 7.75 28.1543 7.75V6.25C30.059 6.25 31.3869 8.13941 30.7418 9.93148L29.3305 9.4234Z'
|
|
59
|
+
fill='currentColor'
|
|
60
|
+
/>
|
|
61
|
+
<path
|
|
62
|
+
d='M13.5 21.75C13.0858 21.75 12.75 21.4142 12.75 21C12.75 20.5858 13.0858 20.25 13.5 20.25V21.75ZM26.7111 19.009L27.4174 19.2613L27.4174 19.2613L26.7111 19.009ZM13.5 20.25H23.8858V21.75H13.5V20.25ZM26.0048 18.7568L27.7937 13.7477L29.2063 14.2523L27.4174 19.2613L26.0048 18.7568ZM23.8858 20.25C24.8367 20.25 25.6849 19.6522 26.0048 18.7568L27.4174 19.2613C26.8843 20.7537 25.4706 21.75 23.8858 21.75V20.25Z'
|
|
63
|
+
fill='currentColor'
|
|
64
|
+
/>
|
|
65
|
+
<path d='M21.1694 10.5806L14.5651 17.1849' stroke='currentColor' strokeWidth='1.5' strokeLinecap='round' />
|
|
66
|
+
<path d='M22.1694 14.5806L18.5632 18.1868' stroke='currentColor' strokeWidth='1.5' strokeLinecap='round' />
|
|
67
|
+
<circle cx='13.1' cy='26.1' r='1.7' stroke='currentColor' strokeWidth='1.2' />
|
|
68
|
+
<circle cx='22.1' cy='26.1' r='1.7' stroke='currentColor' strokeWidth='1.2' />
|
|
69
|
+
</svg>
|
|
70
|
+
<span className='text-base font-semibold tracking-tight'>{text}</span>
|
|
71
|
+
</>
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if (!href) {
|
|
75
|
+
return <span className={className}>{content}</span>
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<a href={href} className={className}>
|
|
80
|
+
{content}
|
|
81
|
+
</a>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function CartButton({
|
|
86
|
+
count,
|
|
87
|
+
onClick,
|
|
88
|
+
label,
|
|
89
|
+
}: {
|
|
90
|
+
count: number
|
|
91
|
+
onClick?: () => void
|
|
92
|
+
label: string
|
|
93
|
+
}) {
|
|
94
|
+
const reduceMotion = useReducedMotion()
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<Button
|
|
98
|
+
variant='ghost'
|
|
99
|
+
size='icon'
|
|
100
|
+
aria-label={`${label} (${count} artículos)`}
|
|
101
|
+
className='relative cursor-pointer'
|
|
102
|
+
onClick={onClick}
|
|
103
|
+
>
|
|
104
|
+
<ShoppingCart />
|
|
105
|
+
{count > 0 && (
|
|
106
|
+
<motion.span
|
|
107
|
+
key={count}
|
|
108
|
+
initial={reduceMotion ? false : { scale: 0.4, opacity: 0 }}
|
|
109
|
+
animate={{ scale: 1, opacity: 1 }}
|
|
110
|
+
transition={{ type: 'spring', stiffness: 420, damping: 22 }}
|
|
111
|
+
className='bg-primary text-primary-foreground absolute -top-1 -right-1 inline-flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-[10px] font-semibold'
|
|
112
|
+
>
|
|
113
|
+
{count}
|
|
114
|
+
</motion.span>
|
|
115
|
+
)}
|
|
116
|
+
</Button>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function Navbar1({
|
|
121
|
+
navLinks,
|
|
122
|
+
logoText,
|
|
123
|
+
logoHref,
|
|
124
|
+
currentPath,
|
|
125
|
+
signIn,
|
|
126
|
+
cta,
|
|
127
|
+
cartCount,
|
|
128
|
+
onOpenCart,
|
|
129
|
+
}: Navbar1Props) {
|
|
130
|
+
const [open, setOpen] = useState(false)
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<header className='border-border bg-background w-full border-b'>
|
|
134
|
+
<div className='mx-auto flex h-16 w-full max-w-7xl items-center justify-between gap-4 px-4 sm:px-6 lg:px-8'>
|
|
135
|
+
<Logo text={logoText} href={logoHref} />
|
|
136
|
+
|
|
137
|
+
<nav aria-label='Main' className='hidden lg:block'>
|
|
138
|
+
<ul className='flex items-center gap-1'>
|
|
139
|
+
{navLinks.map(link => {
|
|
140
|
+
const isActive = link.href !== undefined && link.href === currentPath
|
|
141
|
+
const className = cn(
|
|
142
|
+
'hover:text-foreground focus-visible:ring-ring rounded-md px-3 py-2 text-sm font-medium transition-colors duration-150 ease-out focus-visible:ring-2 focus-visible:outline-none',
|
|
143
|
+
isActive ? 'text-foreground' : 'text-muted-foreground'
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<li key={link.label}>
|
|
148
|
+
{link.href ? (
|
|
149
|
+
<a href={link.href} aria-current={isActive ? 'page' : undefined} className={className}>
|
|
150
|
+
{link.label}
|
|
151
|
+
</a>
|
|
152
|
+
) : (
|
|
153
|
+
<span className={className}>{link.label}</span>
|
|
154
|
+
)}
|
|
155
|
+
</li>
|
|
156
|
+
)
|
|
157
|
+
})}
|
|
158
|
+
</ul>
|
|
159
|
+
</nav>
|
|
160
|
+
|
|
161
|
+
<div className='hidden items-center gap-2 lg:flex'>
|
|
162
|
+
{signIn &&
|
|
163
|
+
(signIn.href ? (
|
|
164
|
+
<Button variant='ghost' size='sm' asChild>
|
|
165
|
+
<a href={signIn.href}>{signIn.label}</a>
|
|
166
|
+
</Button>
|
|
167
|
+
) : (
|
|
168
|
+
<Button variant='ghost' size='sm'>
|
|
169
|
+
{signIn.label}
|
|
170
|
+
</Button>
|
|
171
|
+
))}
|
|
172
|
+
{cta &&
|
|
173
|
+
(cta.href ? (
|
|
174
|
+
<Button size='sm' asChild>
|
|
175
|
+
<a href={cta.href}>{cta.label}</a>
|
|
176
|
+
</Button>
|
|
177
|
+
) : (
|
|
178
|
+
<Button size='sm'>{cta.label}</Button>
|
|
179
|
+
))}
|
|
180
|
+
{cartCount !== undefined && onOpenCart && (
|
|
181
|
+
<CartButton count={cartCount} onClick={onOpenCart} label='Carrito' />
|
|
182
|
+
)}
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<Sheet open={open} onOpenChange={setOpen}>
|
|
186
|
+
<SheetTrigger asChild>
|
|
187
|
+
<Button variant='ghost' size='icon' aria-label='Open menu' className='lg:hidden'>
|
|
188
|
+
<Menu />
|
|
189
|
+
</Button>
|
|
190
|
+
</SheetTrigger>
|
|
191
|
+
<SheetContent side='right' className='w-full max-w-xs p-0'>
|
|
192
|
+
<SheetHeader className='border-border border-b'>
|
|
193
|
+
<SheetTitle className='text-left text-base'>Menu</SheetTitle>
|
|
194
|
+
</SheetHeader>
|
|
195
|
+
|
|
196
|
+
<nav aria-label='Mobile' className='flex flex-col px-2 py-2'>
|
|
197
|
+
{navLinks.map(link => {
|
|
198
|
+
const isActive = link.href !== undefined && link.href === currentPath
|
|
199
|
+
const className = cn(
|
|
200
|
+
'hover:bg-accent hover:text-accent-foreground focus-visible:ring-ring rounded-md px-3 py-3 text-base font-medium transition-colors duration-150 ease-out focus-visible:ring-2 focus-visible:outline-none',
|
|
201
|
+
isActive ? 'text-foreground' : 'text-muted-foreground'
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
return (
|
|
205
|
+
<SheetClose key={link.label} asChild>
|
|
206
|
+
{link.href ? (
|
|
207
|
+
<a href={link.href} aria-current={isActive ? 'page' : undefined} className={className}>
|
|
208
|
+
{link.label}
|
|
209
|
+
</a>
|
|
210
|
+
) : (
|
|
211
|
+
<button type='button' className={className}>
|
|
212
|
+
{link.label}
|
|
213
|
+
</button>
|
|
214
|
+
)}
|
|
215
|
+
</SheetClose>
|
|
216
|
+
)
|
|
217
|
+
})}
|
|
218
|
+
</nav>
|
|
219
|
+
|
|
220
|
+
<div className='mt-auto flex flex-col gap-4 p-4'>
|
|
221
|
+
<Separator />
|
|
222
|
+
<div className='flex flex-col gap-2'>
|
|
223
|
+
{signIn &&
|
|
224
|
+
(signIn.href ? (
|
|
225
|
+
<SheetClose asChild>
|
|
226
|
+
<Button variant='outline' className='w-full' asChild>
|
|
227
|
+
<a href={signIn.href}>{signIn.label}</a>
|
|
228
|
+
</Button>
|
|
229
|
+
</SheetClose>
|
|
230
|
+
) : (
|
|
231
|
+
<SheetClose asChild>
|
|
232
|
+
<Button variant='outline' className='w-full'>
|
|
233
|
+
{signIn.label}
|
|
234
|
+
</Button>
|
|
235
|
+
</SheetClose>
|
|
236
|
+
))}
|
|
237
|
+
{cta &&
|
|
238
|
+
(cta.href ? (
|
|
239
|
+
<SheetClose asChild>
|
|
240
|
+
<Button className='w-full' asChild>
|
|
241
|
+
<a href={cta.href}>{cta.label}</a>
|
|
242
|
+
</Button>
|
|
243
|
+
</SheetClose>
|
|
244
|
+
) : (
|
|
245
|
+
<SheetClose asChild>
|
|
246
|
+
<Button className='w-full'>{cta.label}</Button>
|
|
247
|
+
</SheetClose>
|
|
248
|
+
))}
|
|
249
|
+
{cartCount !== undefined && onOpenCart && (
|
|
250
|
+
<SheetClose asChild>
|
|
251
|
+
<Button variant='outline' className='w-full' onClick={onOpenCart}>
|
|
252
|
+
<ShoppingCart />
|
|
253
|
+
Carrito
|
|
254
|
+
{cartCount > 0 ? ` (${cartCount})` : ''}
|
|
255
|
+
</Button>
|
|
256
|
+
</SheetClose>
|
|
257
|
+
)}
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
</SheetContent>
|
|
261
|
+
</Sheet>
|
|
262
|
+
</div>
|
|
263
|
+
</header>
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export default Navbar1
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import { ArrowRight, Menu, type LucideIcon } from 'lucide-react'
|
|
5
|
+
|
|
6
|
+
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '../../../ui/accordion'
|
|
7
|
+
import { Button } from '../../../ui/button'
|
|
8
|
+
import {
|
|
9
|
+
NavigationMenu,
|
|
10
|
+
NavigationMenuContent,
|
|
11
|
+
NavigationMenuItem,
|
|
12
|
+
NavigationMenuLink,
|
|
13
|
+
NavigationMenuList,
|
|
14
|
+
NavigationMenuTrigger,
|
|
15
|
+
navigationMenuTriggerStyle,
|
|
16
|
+
} from '../../../ui/navigation-menu'
|
|
17
|
+
import { Separator } from '../../../ui/separator'
|
|
18
|
+
import {
|
|
19
|
+
Sheet,
|
|
20
|
+
SheetClose,
|
|
21
|
+
SheetContent,
|
|
22
|
+
SheetHeader,
|
|
23
|
+
SheetTitle,
|
|
24
|
+
SheetTrigger,
|
|
25
|
+
} from '../../../ui/sheet'
|
|
26
|
+
import { cn } from '../../../lib/utils'
|
|
27
|
+
import type { CTA } from '../../../lib/types'
|
|
28
|
+
|
|
29
|
+
export interface MenuItem {
|
|
30
|
+
/** Icon rendered inside the item tile. */
|
|
31
|
+
icon: LucideIcon
|
|
32
|
+
label: string
|
|
33
|
+
description: string
|
|
34
|
+
href: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface MenuGroup {
|
|
38
|
+
trigger: string
|
|
39
|
+
/** Two columns at `lg` when `items.length > 4`, single column otherwise. */
|
|
40
|
+
items: MenuItem[]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface FeaturedLink {
|
|
44
|
+
eyebrow: string
|
|
45
|
+
title: string
|
|
46
|
+
description: string
|
|
47
|
+
href: string
|
|
48
|
+
linkLabel: string
|
|
49
|
+
/** Featured image URL. */
|
|
50
|
+
image: string
|
|
51
|
+
imageAlt: string
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface NavLink {
|
|
55
|
+
label: string
|
|
56
|
+
href: string
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface Navbar2Props {
|
|
60
|
+
/** Grouped menu entries rendered as flyout panels on desktop and accordions on mobile. */
|
|
61
|
+
menuGroups: MenuGroup[]
|
|
62
|
+
logoText: string
|
|
63
|
+
logoHref?: string
|
|
64
|
+
/** Simple top-level links rendered after the menu groups. */
|
|
65
|
+
plainLinks?: NavLink[]
|
|
66
|
+
/** Optional sign-in entry. Rendered as a link when `href` is set, otherwise as a button. */
|
|
67
|
+
signIn?: CTA
|
|
68
|
+
/** Optional primary call to action. Rendered as a link when `href` is set, otherwise as a button. */
|
|
69
|
+
cta?: CTA
|
|
70
|
+
/** Promo card shown on the right of wide flyout panels (groups with more than four items). */
|
|
71
|
+
featured?: FeaturedLink
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function Logo({ text, href }: { text: string; href?: string }) {
|
|
75
|
+
const className =
|
|
76
|
+
'focus-visible:ring-ring flex items-center gap-2 rounded-sm focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none'
|
|
77
|
+
|
|
78
|
+
const content = (
|
|
79
|
+
<>
|
|
80
|
+
<svg viewBox='0 0 32 32' aria-hidden='true' className='text-primary size-7' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
|
81
|
+
<path
|
|
82
|
+
d='M26 24.75C26.4142 24.75 26.75 24.4142 26.75 24C26.75 23.5858 26.4142 23.25 26 23.25V24.75ZM26 23.25H11V24.75H26V23.25ZM8.75 21V15H7.25V21H8.75ZM11 23.25C9.75736 23.25 8.75 22.2426 8.75 21H7.25C7.25 23.0711 8.92893 24.75 11 24.75V23.25Z'
|
|
83
|
+
fill='currentColor'
|
|
84
|
+
/>
|
|
85
|
+
<path
|
|
86
|
+
d='M1.5 3.25C1.08579 3.25 0.75 3.58579 0.75 4C0.75 4.41421 1.08579 4.75 1.5 4.75V3.25ZM1.5 4.75H6V3.25H1.5V4.75ZM7.25 6V21H8.75V6H7.25ZM6 4.75C6.69036 4.75 7.25 5.30964 7.25 6H8.75C8.75 4.48122 7.51878 3.25 6 3.25V4.75Z'
|
|
87
|
+
fill='currentColor'
|
|
88
|
+
/>
|
|
89
|
+
<path
|
|
90
|
+
d='M22 21.75C22.4142 21.75 22.75 21.4142 22.75 21C22.75 20.5858 22.4142 20.25 22 20.25V21.75ZM22 20.25H11V21.75H22V20.25ZM8.75 18V12H7.25V18H8.75ZM11 20.25C9.75736 20.25 8.75 19.2426 8.75 18H7.25C7.25 20.0711 8.92893 21.75 11 21.75V20.25Z'
|
|
91
|
+
fill='currentColor'
|
|
92
|
+
/>
|
|
93
|
+
<path
|
|
94
|
+
d='M27.2057 19.754C27.0654 20.1438 26.6357 20.346 26.246 20.2057C25.8562 20.0654 25.654 19.6357 25.7943 19.246L27.2057 19.754ZM30.0361 9.67744L29.3305 9.4234L29.3305 9.4234L30.0361 9.67744ZM25.7943 19.246L29.3305 9.4234L30.7418 9.93148L27.2057 19.754L25.7943 19.246ZM28.1543 7.75L8 7.75V6.25L28.1543 6.25V7.75ZM29.3305 9.4234C29.6237 8.60882 29.0201 7.75 28.1543 7.75V6.25C30.059 6.25 31.3869 8.13941 30.7418 9.93148L29.3305 9.4234Z'
|
|
95
|
+
fill='currentColor'
|
|
96
|
+
/>
|
|
97
|
+
<path
|
|
98
|
+
d='M13.5 21.75C13.0858 21.75 12.75 21.4142 12.75 21C12.75 20.5858 13.0858 20.25 13.5 20.25V21.75ZM26.7111 19.009L27.4174 19.2613L27.4174 19.2613L26.7111 19.009ZM13.5 20.25H23.8858V21.75H13.5V20.25ZM26.0048 18.7568L27.7937 13.7477L29.2063 14.2523L27.4174 19.2613L26.0048 18.7568ZM23.8858 20.25C24.8367 20.25 25.6849 19.6522 26.0048 18.7568L27.4174 19.2613C26.8843 20.7537 25.4706 21.75 23.8858 21.75V20.25Z'
|
|
99
|
+
fill='currentColor'
|
|
100
|
+
/>
|
|
101
|
+
<path d='M21.1694 10.5806L14.5651 17.1849' stroke='currentColor' strokeWidth='1.5' strokeLinecap='round' />
|
|
102
|
+
<path d='M22.1694 14.5806L18.5632 18.1868' stroke='currentColor' strokeWidth='1.5' strokeLinecap='round' />
|
|
103
|
+
<circle cx='13.1' cy='26.1' r='1.7' stroke='currentColor' strokeWidth='1.2' />
|
|
104
|
+
<circle cx='22.1' cy='26.1' r='1.7' stroke='currentColor' strokeWidth='1.2' />
|
|
105
|
+
</svg>
|
|
106
|
+
<span className='text-base font-semibold tracking-tight'>{text}</span>
|
|
107
|
+
</>
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
if (!href) {
|
|
111
|
+
return <span className={className}>{content}</span>
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<a href={href} className={className}>
|
|
116
|
+
{content}
|
|
117
|
+
</a>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function FeaturedCard({ featured }: { featured: FeaturedLink }) {
|
|
122
|
+
return (
|
|
123
|
+
<a
|
|
124
|
+
href={featured.href}
|
|
125
|
+
className='border-border focus-visible:ring-ring group relative flex h-full min-h-56 flex-col justify-end overflow-hidden rounded-md border focus-visible:ring-2 focus-visible:outline-none'
|
|
126
|
+
>
|
|
127
|
+
<img
|
|
128
|
+
src={featured.image || undefined}
|
|
129
|
+
alt={featured.imageAlt}
|
|
130
|
+
className='absolute inset-0 size-full object-cover transition-transform duration-500 ease-out group-hover:scale-105'
|
|
131
|
+
/>
|
|
132
|
+
{/* Soft top sheen + deep bottom scrim so copy stays readable on the product shot. */}
|
|
133
|
+
<div
|
|
134
|
+
aria-hidden='true'
|
|
135
|
+
className='absolute inset-0 bg-gradient-to-t from-black/90 via-black/55 to-black/20'
|
|
136
|
+
/>
|
|
137
|
+
<div
|
|
138
|
+
aria-hidden='true'
|
|
139
|
+
className='absolute inset-0 bg-gradient-to-br from-white/15 via-transparent to-transparent opacity-80'
|
|
140
|
+
/>
|
|
141
|
+
<div className='relative z-10 flex flex-col p-4'>
|
|
142
|
+
<p className='text-xs font-semibold tracking-wide text-white/70 uppercase'>
|
|
143
|
+
{featured.eyebrow}
|
|
144
|
+
</p>
|
|
145
|
+
<p className='mt-1 text-sm font-medium text-white'>{featured.title}</p>
|
|
146
|
+
<p className='mt-1 text-sm text-white/75'>{featured.description}</p>
|
|
147
|
+
<span className='mt-3 inline-flex items-center gap-1 text-sm font-medium text-white'>
|
|
148
|
+
{featured.linkLabel}
|
|
149
|
+
<ArrowRight className='size-3.5 transition-transform duration-200 ease-out group-hover:translate-x-0.5' />
|
|
150
|
+
</span>
|
|
151
|
+
</div>
|
|
152
|
+
</a>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function Navbar2({
|
|
157
|
+
menuGroups,
|
|
158
|
+
logoText,
|
|
159
|
+
logoHref,
|
|
160
|
+
plainLinks = [],
|
|
161
|
+
signIn,
|
|
162
|
+
cta,
|
|
163
|
+
featured,
|
|
164
|
+
}: Navbar2Props) {
|
|
165
|
+
const [open, setOpen] = useState(false)
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<header className='border-border bg-background w-full border-b'>
|
|
169
|
+
<div className='mx-auto flex h-16 w-full max-w-7xl items-center justify-between gap-4 px-4 sm:px-6 lg:px-8'>
|
|
170
|
+
<div className='flex items-center gap-6'>
|
|
171
|
+
<Logo text={logoText} href={logoHref} />
|
|
172
|
+
|
|
173
|
+
<NavigationMenu className='hidden lg:flex' aria-label='Main'>
|
|
174
|
+
<NavigationMenuList>
|
|
175
|
+
{menuGroups.map(group => {
|
|
176
|
+
const isWide = group.items.length > 4
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<NavigationMenuItem key={group.trigger}>
|
|
180
|
+
<NavigationMenuTrigger>{group.trigger}</NavigationMenuTrigger>
|
|
181
|
+
<NavigationMenuContent>
|
|
182
|
+
<div
|
|
183
|
+
className={cn(
|
|
184
|
+
isWide ? 'grid w-[760px] grid-cols-[1fr_248px] gap-3 p-2' : 'w-[360px] p-2'
|
|
185
|
+
)}
|
|
186
|
+
>
|
|
187
|
+
<ul className={isWide ? 'grid grid-cols-2 gap-1' : 'grid gap-1'}>
|
|
188
|
+
{group.items.map(item => (
|
|
189
|
+
<li key={item.href}>
|
|
190
|
+
<NavigationMenuLink href={item.href} className='items-start gap-3 p-3'>
|
|
191
|
+
<span
|
|
192
|
+
aria-hidden='true'
|
|
193
|
+
className='bg-muted text-foreground flex size-8 shrink-0 items-center justify-center rounded-md'
|
|
194
|
+
>
|
|
195
|
+
<item.icon className='size-4' />
|
|
196
|
+
</span>
|
|
197
|
+
<span className='flex flex-col gap-0.5'>
|
|
198
|
+
<span className='text-sm font-medium'>{item.label}</span>
|
|
199
|
+
<span className='text-muted-foreground line-clamp-2 text-sm leading-5'>
|
|
200
|
+
{item.description}
|
|
201
|
+
</span>
|
|
202
|
+
</span>
|
|
203
|
+
</NavigationMenuLink>
|
|
204
|
+
</li>
|
|
205
|
+
))}
|
|
206
|
+
</ul>
|
|
207
|
+
{isWide && featured ? <FeaturedCard featured={featured} /> : null}
|
|
208
|
+
</div>
|
|
209
|
+
</NavigationMenuContent>
|
|
210
|
+
</NavigationMenuItem>
|
|
211
|
+
)
|
|
212
|
+
})}
|
|
213
|
+
|
|
214
|
+
{plainLinks.map(link => (
|
|
215
|
+
<NavigationMenuItem key={link.href}>
|
|
216
|
+
<NavigationMenuLink href={link.href} className={navigationMenuTriggerStyle()}>
|
|
217
|
+
{link.label}
|
|
218
|
+
</NavigationMenuLink>
|
|
219
|
+
</NavigationMenuItem>
|
|
220
|
+
))}
|
|
221
|
+
</NavigationMenuList>
|
|
222
|
+
</NavigationMenu>
|
|
223
|
+
</div>
|
|
224
|
+
|
|
225
|
+
<div className='hidden items-center gap-2 lg:flex'>
|
|
226
|
+
{signIn &&
|
|
227
|
+
(signIn.href ? (
|
|
228
|
+
<Button variant='ghost' size='sm' asChild>
|
|
229
|
+
<a href={signIn.href}>{signIn.label}</a>
|
|
230
|
+
</Button>
|
|
231
|
+
) : (
|
|
232
|
+
<Button variant='ghost' size='sm'>
|
|
233
|
+
{signIn.label}
|
|
234
|
+
</Button>
|
|
235
|
+
))}
|
|
236
|
+
{cta &&
|
|
237
|
+
(cta.href ? (
|
|
238
|
+
<Button size='sm' asChild>
|
|
239
|
+
<a href={cta.href}>{cta.label}</a>
|
|
240
|
+
</Button>
|
|
241
|
+
) : (
|
|
242
|
+
<Button size='sm'>{cta.label}</Button>
|
|
243
|
+
))}
|
|
244
|
+
</div>
|
|
245
|
+
|
|
246
|
+
<Sheet open={open} onOpenChange={setOpen}>
|
|
247
|
+
<SheetTrigger asChild>
|
|
248
|
+
<Button variant='ghost' size='icon' aria-label='Open menu' className='lg:hidden'>
|
|
249
|
+
<Menu />
|
|
250
|
+
</Button>
|
|
251
|
+
</SheetTrigger>
|
|
252
|
+
<SheetContent side='right' className='w-full max-w-sm gap-0 overflow-y-auto p-0'>
|
|
253
|
+
<SheetHeader className='border-border border-b'>
|
|
254
|
+
<SheetTitle className='text-left text-base'>Menu</SheetTitle>
|
|
255
|
+
</SheetHeader>
|
|
256
|
+
|
|
257
|
+
{/* Every flyout destination stays reachable on mobile — groups become accordions
|
|
258
|
+
rather than being dropped. */}
|
|
259
|
+
<nav aria-label='Mobile' className='px-2 py-2'>
|
|
260
|
+
<Accordion type='multiple'>
|
|
261
|
+
{menuGroups.map(group => (
|
|
262
|
+
<AccordionItem key={group.trigger} value={group.trigger} className='border-none'>
|
|
263
|
+
<AccordionTrigger className='px-3 py-3 text-base font-medium hover:no-underline'>
|
|
264
|
+
{group.trigger}
|
|
265
|
+
</AccordionTrigger>
|
|
266
|
+
<AccordionContent className='pb-1'>
|
|
267
|
+
<ul className='flex flex-col'>
|
|
268
|
+
{group.items.map(item => (
|
|
269
|
+
<li key={item.href}>
|
|
270
|
+
<SheetClose asChild>
|
|
271
|
+
<a
|
|
272
|
+
href={item.href}
|
|
273
|
+
className='hover:bg-accent hover:text-accent-foreground focus-visible:ring-ring flex items-start gap-3 rounded-md px-3 py-3 transition-colors duration-150 ease-out focus-visible:ring-2 focus-visible:outline-none'
|
|
274
|
+
>
|
|
275
|
+
<span
|
|
276
|
+
aria-hidden='true'
|
|
277
|
+
className='bg-muted text-foreground flex size-8 shrink-0 items-center justify-center rounded-md'
|
|
278
|
+
>
|
|
279
|
+
<item.icon className='size-4' />
|
|
280
|
+
</span>
|
|
281
|
+
<span className='flex flex-col gap-0.5'>
|
|
282
|
+
<span className='text-sm font-medium'>{item.label}</span>
|
|
283
|
+
<span className='text-muted-foreground text-sm'>
|
|
284
|
+
{item.description}
|
|
285
|
+
</span>
|
|
286
|
+
</span>
|
|
287
|
+
</a>
|
|
288
|
+
</SheetClose>
|
|
289
|
+
</li>
|
|
290
|
+
))}
|
|
291
|
+
</ul>
|
|
292
|
+
</AccordionContent>
|
|
293
|
+
</AccordionItem>
|
|
294
|
+
))}
|
|
295
|
+
</Accordion>
|
|
296
|
+
|
|
297
|
+
{plainLinks.length > 0 && (
|
|
298
|
+
<div className='mt-1 flex flex-col'>
|
|
299
|
+
{plainLinks.map(link => (
|
|
300
|
+
<SheetClose key={link.href} asChild>
|
|
301
|
+
<a
|
|
302
|
+
href={link.href}
|
|
303
|
+
className='text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-ring rounded-md px-3 py-3 text-base font-medium transition-colors duration-150 ease-out focus-visible:ring-2 focus-visible:outline-none'
|
|
304
|
+
>
|
|
305
|
+
{link.label}
|
|
306
|
+
</a>
|
|
307
|
+
</SheetClose>
|
|
308
|
+
))}
|
|
309
|
+
</div>
|
|
310
|
+
)}
|
|
311
|
+
</nav>
|
|
312
|
+
|
|
313
|
+
<div className='mt-auto flex flex-col gap-4 p-4'>
|
|
314
|
+
<Separator />
|
|
315
|
+
<div className='flex flex-col gap-2'>
|
|
316
|
+
{signIn &&
|
|
317
|
+
(signIn.href ? (
|
|
318
|
+
<SheetClose asChild>
|
|
319
|
+
<Button variant='outline' className='w-full' asChild>
|
|
320
|
+
<a href={signIn.href}>{signIn.label}</a>
|
|
321
|
+
</Button>
|
|
322
|
+
</SheetClose>
|
|
323
|
+
) : (
|
|
324
|
+
<SheetClose asChild>
|
|
325
|
+
<Button variant='outline' className='w-full'>
|
|
326
|
+
{signIn.label}
|
|
327
|
+
</Button>
|
|
328
|
+
</SheetClose>
|
|
329
|
+
))}
|
|
330
|
+
{cta &&
|
|
331
|
+
(cta.href ? (
|
|
332
|
+
<SheetClose asChild>
|
|
333
|
+
<Button className='w-full' asChild>
|
|
334
|
+
<a href={cta.href}>{cta.label}</a>
|
|
335
|
+
</Button>
|
|
336
|
+
</SheetClose>
|
|
337
|
+
) : (
|
|
338
|
+
<SheetClose asChild>
|
|
339
|
+
<Button className='w-full'>{cta.label}</Button>
|
|
340
|
+
</SheetClose>
|
|
341
|
+
))}
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
</SheetContent>
|
|
345
|
+
</Sheet>
|
|
346
|
+
</div>
|
|
347
|
+
</header>
|
|
348
|
+
)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export default Navbar2
|