@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,355 @@
|
|
|
1
|
+
import type { ReactElement, ReactNode, SVGProps } from 'react'
|
|
2
|
+
import { Input } from '../../../ui/input'
|
|
3
|
+
import { Button } from '../../../ui/button'
|
|
4
|
+
import { ArrowRight, Mail, MapPin, Phone } from 'lucide-react'
|
|
5
|
+
|
|
6
|
+
export type FooterSection4SocialIcon = 'facebook' | 'twitter' | 'instagram'
|
|
7
|
+
|
|
8
|
+
export interface FooterSection4Link {
|
|
9
|
+
label: string
|
|
10
|
+
href?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface FooterSection4LinkColumn {
|
|
14
|
+
title: string
|
|
15
|
+
links: FooterSection4Link[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface FooterSection4Contact {
|
|
19
|
+
/** Título de la columna de contacto. Requerido. */
|
|
20
|
+
heading: string
|
|
21
|
+
/** Dirección del negocio. Por defecto undefined. */
|
|
22
|
+
address?: string
|
|
23
|
+
/** href del enlace de dirección. Por defecto undefined. */
|
|
24
|
+
addressHref?: string
|
|
25
|
+
/** Teléfono de contacto. Por defecto undefined. */
|
|
26
|
+
phone?: string
|
|
27
|
+
/** href del enlace de teléfono (p. ej. tel:). Por defecto undefined. */
|
|
28
|
+
phoneHref?: string
|
|
29
|
+
/** Email de contacto. Por defecto undefined. */
|
|
30
|
+
email?: string
|
|
31
|
+
/** href del enlace de email (p. ej. mailto:). Por defecto undefined. */
|
|
32
|
+
emailHref?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface FooterSection4SocialLink {
|
|
36
|
+
label: string
|
|
37
|
+
href?: string
|
|
38
|
+
icon: FooterSection4SocialIcon
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface FooterSection4Props {
|
|
42
|
+
/** Nombre de la marca mostrado junto al logo. Requerido. */
|
|
43
|
+
brandName: string
|
|
44
|
+
/** Descripción corta de la marca bajo el logo. Requerida. */
|
|
45
|
+
tagline: string
|
|
46
|
+
/** Placeholder del input de email del newsletter. Por defecto 'Tu correo electrónico'. */
|
|
47
|
+
newsletterPlaceholder?: string
|
|
48
|
+
/** Texto del botón de suscripción. Por defecto 'Suscribirse'. */
|
|
49
|
+
newsletterSubmitLabel?: string
|
|
50
|
+
/** Datos de la columna de contacto (dirección, teléfono, email). Requerido. */
|
|
51
|
+
contact: FooterSection4Contact
|
|
52
|
+
/** Columnas de enlaces (Quick Links, Resources, Legal...). Requerido. */
|
|
53
|
+
linkColumns: FooterSection4LinkColumn[]
|
|
54
|
+
/** Texto de copyright de la barra inferior (incluye año). Requerido. */
|
|
55
|
+
copyright: string
|
|
56
|
+
/** Redes sociales con su icono serializable por nombre. Por defecto []. */
|
|
57
|
+
socialLinks?: FooterSection4SocialLink[]
|
|
58
|
+
/** Logo personalizado; si se omite se usa el SVG interno del bloque. Por defecto undefined. */
|
|
59
|
+
logo?: ReactNode
|
|
60
|
+
/** Callback al enviar el formulario newsletter. Recibe el email. */
|
|
61
|
+
onNewsletterSubmit?: (email: string) => void
|
|
62
|
+
/** Callback al hacer clic en un enlace de columna o contacto. Recibe el enlace. */
|
|
63
|
+
onLinkClick?: (link: FooterSection4Link) => void
|
|
64
|
+
/** Callback al hacer clic en una red social. Recibe el label. */
|
|
65
|
+
onSocialClick?: (label: string) => void
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const socialIconPaths: Record<
|
|
69
|
+
FooterSection4SocialIcon,
|
|
70
|
+
(props: SVGProps<SVGSVGElement>) => ReactElement
|
|
71
|
+
> = {
|
|
72
|
+
facebook: props => (
|
|
73
|
+
<svg
|
|
74
|
+
xmlns='http://www.w3.org/2000/svg'
|
|
75
|
+
width='24'
|
|
76
|
+
height='24'
|
|
77
|
+
viewBox='0 0 24 24'
|
|
78
|
+
fill='none'
|
|
79
|
+
stroke='currentColor'
|
|
80
|
+
strokeWidth='2'
|
|
81
|
+
strokeLinecap='round'
|
|
82
|
+
strokeLinejoin='round'
|
|
83
|
+
{...props}
|
|
84
|
+
>
|
|
85
|
+
<path d='M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z' />
|
|
86
|
+
</svg>
|
|
87
|
+
),
|
|
88
|
+
twitter: props => (
|
|
89
|
+
<svg
|
|
90
|
+
xmlns='http://www.w3.org/2000/svg'
|
|
91
|
+
width='24'
|
|
92
|
+
height='24'
|
|
93
|
+
viewBox='0 0 24 24'
|
|
94
|
+
fill='none'
|
|
95
|
+
stroke='currentColor'
|
|
96
|
+
strokeWidth='2'
|
|
97
|
+
strokeLinecap='round'
|
|
98
|
+
strokeLinejoin='round'
|
|
99
|
+
{...props}
|
|
100
|
+
>
|
|
101
|
+
<path d='M22 4s-.7 2.1-2 3.4c1.6 10-9.4 17.3-18 11.6 2.2.1 4.4-.6 6-2C3 15.5.5 9.6 3 5c2.2 2.6 5.6 4.1 9 4-.9-4.2 4-6.6 7-3.8 1.1 0 3-1.2 3-1.2z' />
|
|
102
|
+
</svg>
|
|
103
|
+
),
|
|
104
|
+
instagram: props => (
|
|
105
|
+
<svg
|
|
106
|
+
xmlns='http://www.w3.org/2000/svg'
|
|
107
|
+
width='24'
|
|
108
|
+
height='24'
|
|
109
|
+
viewBox='0 0 24 24'
|
|
110
|
+
fill='none'
|
|
111
|
+
stroke='currentColor'
|
|
112
|
+
strokeWidth='2'
|
|
113
|
+
strokeLinecap='round'
|
|
114
|
+
strokeLinejoin='round'
|
|
115
|
+
{...props}
|
|
116
|
+
>
|
|
117
|
+
<rect width='20' height='20' x='2' y='2' rx='5' ry='5' />
|
|
118
|
+
<path d='M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z' />
|
|
119
|
+
<line x1='17.5' y1='6.5' x2='17.51' y2='6.5' />
|
|
120
|
+
</svg>
|
|
121
|
+
),
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const DefaultLogo = (
|
|
125
|
+
<svg width='32' height='32' viewBox='0 0 32 32' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
|
126
|
+
<path
|
|
127
|
+
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'
|
|
128
|
+
fill='currentColor'
|
|
129
|
+
/>
|
|
130
|
+
<path
|
|
131
|
+
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'
|
|
132
|
+
fill='currentColor'
|
|
133
|
+
/>
|
|
134
|
+
<path
|
|
135
|
+
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'
|
|
136
|
+
fill='currentColor'
|
|
137
|
+
/>
|
|
138
|
+
<path
|
|
139
|
+
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'
|
|
140
|
+
fill='currentColor'
|
|
141
|
+
/>
|
|
142
|
+
<path
|
|
143
|
+
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'
|
|
144
|
+
fill='currentColor'
|
|
145
|
+
/>
|
|
146
|
+
<path d='M21.1694 10.5806L14.5651 17.1849' stroke='currentColor' />
|
|
147
|
+
<path d='M22.1694 14.5806L18.5632 18.1868' stroke='currentColor' />
|
|
148
|
+
<circle cx='13.1' cy='26.1' r='1.7' stroke='currentColor' />
|
|
149
|
+
<circle cx='22.1' cy='26.1' r='1.7' stroke='currentColor' />
|
|
150
|
+
</svg>
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
const contactItems = (contact: FooterSection4Contact, onLinkClick?: FooterSection4Props['onLinkClick']) => {
|
|
154
|
+
const items: { key: string; icon: ReactElement; href?: string; text?: string }[] = []
|
|
155
|
+
if (contact.address) {
|
|
156
|
+
items.push({
|
|
157
|
+
key: 'address',
|
|
158
|
+
icon: <MapPin className='mt-0.5 size-4 shrink-0' aria-hidden />,
|
|
159
|
+
href: contact.addressHref,
|
|
160
|
+
text: contact.address,
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
if (contact.phone) {
|
|
164
|
+
items.push({
|
|
165
|
+
key: 'phone',
|
|
166
|
+
icon: <Phone className='size-4 shrink-0' aria-hidden />,
|
|
167
|
+
href: contact.phoneHref,
|
|
168
|
+
text: contact.phone,
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
if (contact.email) {
|
|
172
|
+
items.push({
|
|
173
|
+
key: 'email',
|
|
174
|
+
icon: <Mail className='size-4 shrink-0' aria-hidden />,
|
|
175
|
+
href: contact.emailHref,
|
|
176
|
+
text: contact.email,
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
return items
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function FooterSection4({
|
|
183
|
+
brandName,
|
|
184
|
+
tagline,
|
|
185
|
+
newsletterPlaceholder = 'Tu correo electrónico',
|
|
186
|
+
newsletterSubmitLabel = 'Suscribirse',
|
|
187
|
+
contact,
|
|
188
|
+
linkColumns,
|
|
189
|
+
copyright,
|
|
190
|
+
socialLinks = [],
|
|
191
|
+
logo,
|
|
192
|
+
onNewsletterSubmit,
|
|
193
|
+
onLinkClick,
|
|
194
|
+
onSocialClick,
|
|
195
|
+
}: FooterSection4Props) {
|
|
196
|
+
const logoMark = logo ?? DefaultLogo
|
|
197
|
+
return (
|
|
198
|
+
<footer className='from-background to-muted/50 w-full bg-linear-to-b py-16'>
|
|
199
|
+
<div className='mx-auto w-full max-w-7xl px-4 sm:px-6 lg:px-8'>
|
|
200
|
+
{/* Top Section with Logo and Newsletter */}
|
|
201
|
+
<section className='mb-16 flex flex-col items-start justify-between gap-8 md:flex-row md:items-center'>
|
|
202
|
+
<div className='max-w-sm flex flex-col gap-4'>
|
|
203
|
+
<div className='flex items-center gap-2'>
|
|
204
|
+
{logoMark}
|
|
205
|
+
<h2 className='text-2xl font-bold text-balance'>{brandName}</h2>
|
|
206
|
+
</div>
|
|
207
|
+
<p className='text-muted-foreground text-sm leading-relaxed text-balance'>{tagline}</p>
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
<div className='w-full md:w-auto'>
|
|
211
|
+
<form
|
|
212
|
+
className='relative'
|
|
213
|
+
onSubmit={e => {
|
|
214
|
+
e.preventDefault()
|
|
215
|
+
if (onNewsletterSubmit) {
|
|
216
|
+
const email = new FormData(e.currentTarget).get('email')
|
|
217
|
+
if (typeof email === 'string') {
|
|
218
|
+
onNewsletterSubmit(email)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}}
|
|
222
|
+
>
|
|
223
|
+
<Input
|
|
224
|
+
type='email'
|
|
225
|
+
name='email'
|
|
226
|
+
placeholder={newsletterPlaceholder}
|
|
227
|
+
className='border-muted bg-background/50 h-12 w-full ps-4 pe-32 backdrop-blur-xs md:w-[400px]'
|
|
228
|
+
aria-label={newsletterPlaceholder}
|
|
229
|
+
/>
|
|
230
|
+
<Button
|
|
231
|
+
type='submit'
|
|
232
|
+
className='bg-primary hover:bg-primary/90 absolute end-1 top-1 h-10 cursor-pointer px-4'
|
|
233
|
+
aria-label={newsletterSubmitLabel}
|
|
234
|
+
>
|
|
235
|
+
{newsletterSubmitLabel}
|
|
236
|
+
<ArrowRight />
|
|
237
|
+
</Button>
|
|
238
|
+
</form>
|
|
239
|
+
</div>
|
|
240
|
+
</section>
|
|
241
|
+
|
|
242
|
+
{/* Middle Section with Contact Info and Links */}
|
|
243
|
+
<div className='mb-16 grid grid-cols-1 gap-12 md:grid-cols-2 lg:grid-cols-5'>
|
|
244
|
+
{/* Contact Information */}
|
|
245
|
+
<address className='not-italic lg:col-span-2'>
|
|
246
|
+
<h3 className='mb-6 text-lg font-semibold text-balance'>{contact.heading}</h3>
|
|
247
|
+
<ul className='flex flex-col gap-4'>
|
|
248
|
+
{contactItems(contact, onLinkClick).map(item => (
|
|
249
|
+
<li key={item.key} className='text-muted-foreground flex items-start gap-3 text-sm'>
|
|
250
|
+
{item.icon}
|
|
251
|
+
{item.href ? (
|
|
252
|
+
<a
|
|
253
|
+
href={item.href}
|
|
254
|
+
onClick={e => {
|
|
255
|
+
if (!onLinkClick) {
|
|
256
|
+
e.preventDefault()
|
|
257
|
+
} else {
|
|
258
|
+
onLinkClick({ label: item.text ?? '' })
|
|
259
|
+
}
|
|
260
|
+
}}
|
|
261
|
+
className='hover:text-foreground transition-colors'
|
|
262
|
+
>
|
|
263
|
+
{item.text}
|
|
264
|
+
</a>
|
|
265
|
+
) : (
|
|
266
|
+
<span>{item.text}</span>
|
|
267
|
+
)}
|
|
268
|
+
</li>
|
|
269
|
+
))}
|
|
270
|
+
</ul>
|
|
271
|
+
</address>
|
|
272
|
+
|
|
273
|
+
{/* Link columns */}
|
|
274
|
+
{linkColumns.map(column => (
|
|
275
|
+
<nav key={column.title} aria-label={column.title}>
|
|
276
|
+
<h3 className='mb-6 text-lg font-semibold text-balance'>{column.title}</h3>
|
|
277
|
+
<ul className='grid grid-cols-1 gap-3'>
|
|
278
|
+
{column.links.map(link => (
|
|
279
|
+
<li key={link.label}>
|
|
280
|
+
{link.href ? (
|
|
281
|
+
<a
|
|
282
|
+
href={link.href}
|
|
283
|
+
onClick={e => {
|
|
284
|
+
if (!onLinkClick) {
|
|
285
|
+
e.preventDefault()
|
|
286
|
+
} else {
|
|
287
|
+
onLinkClick(link)
|
|
288
|
+
}
|
|
289
|
+
}}
|
|
290
|
+
className='group text-muted-foreground hover:text-foreground focus-visible:ring-ring relative inline-block rounded-sm text-sm transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden'
|
|
291
|
+
>
|
|
292
|
+
{link.label}
|
|
293
|
+
</a>
|
|
294
|
+
) : (
|
|
295
|
+
<span className='text-muted-foreground relative inline-block text-sm'>
|
|
296
|
+
{link.label}
|
|
297
|
+
</span>
|
|
298
|
+
)}
|
|
299
|
+
</li>
|
|
300
|
+
))}
|
|
301
|
+
</ul>
|
|
302
|
+
</nav>
|
|
303
|
+
))}
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
{/* Bottom Section */}
|
|
307
|
+
<div className='border-muted-foreground/20 flex flex-col items-center justify-between border-t pt-8 md:flex-row'>
|
|
308
|
+
<p className='text-muted-foreground mb-4 text-sm md:mb-0'>{copyright}</p>
|
|
309
|
+
|
|
310
|
+
<nav aria-label='Social media'>
|
|
311
|
+
<ul className='flex gap-4'>
|
|
312
|
+
{socialLinks.map(({ label, href, icon }) => {
|
|
313
|
+
const Icon = socialIconPaths[icon]
|
|
314
|
+
const element = (
|
|
315
|
+
<>
|
|
316
|
+
<Icon className='size-5' aria-hidden />
|
|
317
|
+
<span className='sr-only'>{label}</span>
|
|
318
|
+
</>
|
|
319
|
+
)
|
|
320
|
+
const classes =
|
|
321
|
+
'bg-accent text-muted-foreground hover:text-foreground focus-visible:ring-ring inline-flex size-10 items-center justify-center rounded-full p-2 transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden'
|
|
322
|
+
return (
|
|
323
|
+
<li key={label}>
|
|
324
|
+
{href ? (
|
|
325
|
+
<a
|
|
326
|
+
href={href}
|
|
327
|
+
onClick={e => {
|
|
328
|
+
if (!onSocialClick) {
|
|
329
|
+
e.preventDefault()
|
|
330
|
+
} else {
|
|
331
|
+
onSocialClick(label)
|
|
332
|
+
}
|
|
333
|
+
}}
|
|
334
|
+
className={classes}
|
|
335
|
+
aria-label={`Seguir en ${label}`}
|
|
336
|
+
>
|
|
337
|
+
{element}
|
|
338
|
+
</a>
|
|
339
|
+
) : (
|
|
340
|
+
<span className={classes} aria-hidden='true'>
|
|
341
|
+
{element}
|
|
342
|
+
</span>
|
|
343
|
+
)}
|
|
344
|
+
</li>
|
|
345
|
+
)
|
|
346
|
+
})}
|
|
347
|
+
</ul>
|
|
348
|
+
</nav>
|
|
349
|
+
</div>
|
|
350
|
+
</div>
|
|
351
|
+
</footer>
|
|
352
|
+
)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export default FooterSection4
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { useEffect, useState } from 'react'
|
|
5
|
+
|
|
6
|
+
import { cn } from '../../../lib/utils'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @title Avatar group
|
|
10
|
+
* @description Overlapping avatar stack whose neighbours lift with a distance falloff on hover.
|
|
11
|
+
*
|
|
12
|
+
* Adapted from Transitions.dev "Avatar group hover".
|
|
13
|
+
*
|
|
14
|
+
* A dock, not a switch. Lifting only the hovered item makes the row twitch — one element jumps
|
|
15
|
+
* while the rest sit rigid — so the response falls off with distance: the neighbour rises about
|
|
16
|
+
* half as far, the next barely at all. The falloff is what makes the row read as one soft object.
|
|
17
|
+
*
|
|
18
|
+
* The return is deliberately springier than the approach: a strong overshoot curve on the way back
|
|
19
|
+
* reads as elastic rather than mechanical.
|
|
20
|
+
*/
|
|
21
|
+
const LIFT = -4
|
|
22
|
+
const FALLOFF = 0.45
|
|
23
|
+
const ACTIVE_SCALE = 1.05
|
|
24
|
+
const DURATION = 320
|
|
25
|
+
const EASE_IN = 'cubic-bezier(0.22, 1, 0.36, 1)'
|
|
26
|
+
const EASE_OUT = 'cubic-bezier(0.34, 3.85, 0.64, 1)'
|
|
27
|
+
|
|
28
|
+
export function AvatarGroup({
|
|
29
|
+
children,
|
|
30
|
+
className,
|
|
31
|
+
overlap = '-ml-2',
|
|
32
|
+
}: {
|
|
33
|
+
children: React.ReactNode
|
|
34
|
+
className?: string
|
|
35
|
+
/** Negative margin applied to every item but the first. Set to `''` for a non-overlapping row. */
|
|
36
|
+
overlap?: string
|
|
37
|
+
}) {
|
|
38
|
+
const items = React.Children.toArray(children)
|
|
39
|
+
const [activeIndex, setActiveIndex] = useState<number | null>(null)
|
|
40
|
+
const [reducedMotion, setReducedMotion] = useState(false)
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const query = window.matchMedia('(prefers-reduced-motion: reduce)')
|
|
44
|
+
const sync = () => setReducedMotion(query.matches)
|
|
45
|
+
|
|
46
|
+
sync()
|
|
47
|
+
query.addEventListener('change', sync)
|
|
48
|
+
return () => query.removeEventListener('change', sync)
|
|
49
|
+
}, [])
|
|
50
|
+
|
|
51
|
+
const itemStyle = (index: number): React.CSSProperties | undefined => {
|
|
52
|
+
if (reducedMotion) {
|
|
53
|
+
return undefined
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const isActive = index === activeIndex
|
|
57
|
+
const shift = activeIndex === null ? 0 : LIFT * FALLOFF ** Math.abs(index - activeIndex)
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
// translateY before scale, so scaling never amplifies the lift distance.
|
|
61
|
+
transform: `translateY(${shift.toFixed(3)}px) scale(${isActive ? ACTIVE_SCALE : 1})`,
|
|
62
|
+
transition: `transform ${DURATION}ms ${activeIndex === null ? EASE_OUT : EASE_IN}`,
|
|
63
|
+
willChange: 'transform',
|
|
64
|
+
// A lifted item has to sit above the neighbour overlapping it, or the lift happens underneath.
|
|
65
|
+
zIndex: isActive ? 1 : undefined,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div className={cn('flex', className)} onMouseLeave={() => setActiveIndex(null)}>
|
|
71
|
+
{items.map((child, index) => (
|
|
72
|
+
<div
|
|
73
|
+
// eslint-disable-next-line react/no-array-index-key -- position is the identity here
|
|
74
|
+
key={index}
|
|
75
|
+
className={cn('relative', index > 0 && overlap)}
|
|
76
|
+
style={itemStyle(index)}
|
|
77
|
+
onMouseEnter={() => setActiveIndex(index)}
|
|
78
|
+
>
|
|
79
|
+
{child}
|
|
80
|
+
</div>
|
|
81
|
+
))}
|
|
82
|
+
</div>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export default AvatarGroup
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Button } from '../../../ui/button'
|
|
2
|
+
import { Avatar, AvatarFallback, AvatarImage } from '../../../ui/avatar'
|
|
3
|
+
import { Badge } from '../../../ui/badge'
|
|
4
|
+
import { AvatarGroup } from './avatar-group'
|
|
5
|
+
import { Star } from 'lucide-react'
|
|
6
|
+
import type { CTA } from '../../../lib/types'
|
|
7
|
+
|
|
8
|
+
interface AvatarItem {
|
|
9
|
+
id: string | number
|
|
10
|
+
avatarUrl: string
|
|
11
|
+
fallback: string
|
|
12
|
+
alt?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface Rating {
|
|
16
|
+
value: number
|
|
17
|
+
count: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface HeroSection1Props {
|
|
21
|
+
title: string
|
|
22
|
+
description: string
|
|
23
|
+
badge?: string
|
|
24
|
+
cta?: CTA
|
|
25
|
+
reviewers?: AvatarItem[]
|
|
26
|
+
rating?: Rating
|
|
27
|
+
trustedBy?: string
|
|
28
|
+
image?: { src: string; alt: string }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function HeroSection1({
|
|
32
|
+
title,
|
|
33
|
+
description,
|
|
34
|
+
badge,
|
|
35
|
+
cta,
|
|
36
|
+
reviewers,
|
|
37
|
+
rating,
|
|
38
|
+
trustedBy,
|
|
39
|
+
image,
|
|
40
|
+
}: HeroSection1Props) {
|
|
41
|
+
return (
|
|
42
|
+
<section className='w-full py-12 lg:py-20'>
|
|
43
|
+
<div className='mx-auto w-full max-w-7xl px-4 sm:px-6 lg:px-8'>
|
|
44
|
+
<div className='grid items-center gap-8 lg:grid-cols-2 lg:gap-12'>
|
|
45
|
+
<div className='flex flex-col gap-8 text-center lg:text-start'>
|
|
46
|
+
<header className='flex flex-col gap-4'>
|
|
47
|
+
{badge && (
|
|
48
|
+
<Badge variant='outline' className='h-auto font-semibold rounded-md text-muted-foreground px-3.5 py-1 self-center lg:self-start'>
|
|
49
|
+
{badge}
|
|
50
|
+
</Badge>
|
|
51
|
+
)}
|
|
52
|
+
<h1 className='text-4xl font-bold tracking-tight text-balance md:text-5xl'>{title}</h1>
|
|
53
|
+
<p className='text-muted-foreground text-balance md:text-lg'>
|
|
54
|
+
{description}
|
|
55
|
+
</p>
|
|
56
|
+
</header>
|
|
57
|
+
|
|
58
|
+
{cta &&
|
|
59
|
+
(cta.href ? (
|
|
60
|
+
<Button
|
|
61
|
+
asChild
|
|
62
|
+
size='lg'
|
|
63
|
+
className='h-10 px-8 cursor-pointer text-base w-fit self-center lg:self-start'
|
|
64
|
+
>
|
|
65
|
+
<a href={cta.href}>{cta.label}</a>
|
|
66
|
+
</Button>
|
|
67
|
+
) : (
|
|
68
|
+
<Button
|
|
69
|
+
size='lg'
|
|
70
|
+
className='h-10 px-8 cursor-pointer text-base w-fit self-center lg:self-start'
|
|
71
|
+
>
|
|
72
|
+
{cta.label}
|
|
73
|
+
</Button>
|
|
74
|
+
))}
|
|
75
|
+
|
|
76
|
+
{(reviewers || rating || trustedBy) && (
|
|
77
|
+
<div className='flex flex-col items-center gap-4 sm:flex-row sm:justify-center lg:justify-start'>
|
|
78
|
+
{reviewers && (
|
|
79
|
+
<AvatarGroup overlap='-ml-3'>
|
|
80
|
+
{reviewers.map(reviewer => (
|
|
81
|
+
<Avatar
|
|
82
|
+
key={reviewer.id}
|
|
83
|
+
className='ring-background bg-muted size-10 ring-2'
|
|
84
|
+
>
|
|
85
|
+
<AvatarImage
|
|
86
|
+
src={reviewer.avatarUrl || undefined}
|
|
87
|
+
alt={reviewer.alt ?? `User ${reviewer.id}`}
|
|
88
|
+
/>
|
|
89
|
+
<AvatarFallback className='text-xs'>{reviewer.fallback}</AvatarFallback>
|
|
90
|
+
</Avatar>
|
|
91
|
+
))}
|
|
92
|
+
<div className='border-background bg-accent relative -ms-1.5 flex size-10 items-center justify-center rounded-full border-2'>
|
|
93
|
+
<span className='text-xs font-medium'>+{reviewers.length}</span>
|
|
94
|
+
</div>
|
|
95
|
+
</AvatarGroup>
|
|
96
|
+
)}
|
|
97
|
+
|
|
98
|
+
{(rating || trustedBy) && (
|
|
99
|
+
<div className='flex flex-col gap-0.5'>
|
|
100
|
+
{rating && (
|
|
101
|
+
<div className='flex items-center justify-center gap-1 lg:justify-start'>
|
|
102
|
+
{Array.from({ length: 5 }).map((_, i) => (
|
|
103
|
+
<Star
|
|
104
|
+
key={i}
|
|
105
|
+
className={`size-4 ${i < Math.round(rating.value) ? 'fill-foreground' : ''}`}
|
|
106
|
+
/>
|
|
107
|
+
))}
|
|
108
|
+
<span className='ms-2 text-sm font-medium'>({rating.count})</span>
|
|
109
|
+
</div>
|
|
110
|
+
)}
|
|
111
|
+
{trustedBy && <p className='text-muted-foreground text-sm'>{trustedBy}</p>}
|
|
112
|
+
</div>
|
|
113
|
+
)}
|
|
114
|
+
</div>
|
|
115
|
+
)}
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
{image && (
|
|
119
|
+
<div className='aspect-[4/3] overflow-hidden rounded-xl border'>
|
|
120
|
+
<img
|
|
121
|
+
src={image.src || undefined}
|
|
122
|
+
alt={image.alt}
|
|
123
|
+
className='size-full object-cover'
|
|
124
|
+
width={800}
|
|
125
|
+
height={600}
|
|
126
|
+
/>
|
|
127
|
+
</div>
|
|
128
|
+
)}
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</section>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export default HeroSection1
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Skeleton } from '../../../ui/skeleton'
|
|
2
|
+
import { cn } from '../../../lib/utils'
|
|
3
|
+
|
|
4
|
+
const iconMap = {
|
|
5
|
+
nextdotjs:
|
|
6
|
+
'M18.665 21.978C16.758 23.255 14.465 24 12 24 5.377 24 0 18.623 0 12S5.377 0 12 0s12 5.377 12 12c0 3.583-1.574 6.801-4.067 9.001L9.219 7.2H7.2v9.596h1.615V9.251l9.85 12.727Zm-3.332-8.533 1.6 2.061V7.2h-1.6v6.245Z',
|
|
7
|
+
typescript:
|
|
8
|
+
'M1.125 0C.502 0 0 .502 0 1.125v21.75C0 23.498.502 24 1.125 24h21.75c.623 0 1.125-.502 1.125-1.125V1.125C24 .502 23.498 0 22.875 0zm17.363 9.75c.612 0 1.154.037 1.627.111a6.38 6.38 0 0 1 1.306.34v2.458a3.95 3.95 0 0 0-.643-.361 5.093 5.093 0 0 0-.717-.26 5.453 5.453 0 0 0-1.426-.2c-.3 0-.573.028-.819.086a2.1 2.1 0 0 0-.623.242c-.17.104-.3.229-.393.374a.888.888 0 0 0-.14.49c0 .196.053.373.156.529.104.156.252.304.443.444s.423.276.696.41c.273.135.582.274.926.416.47.197.892.407 1.266.628.374.222.695.473.963.753.268.279.472.598.614.957.142.359.214.776.214 1.253 0 .657-.125 1.21-.373 1.656a3.033 3.033 0 0 1-1.012 1.085 4.38 4.38 0 0 1-1.487.596c-.566.12-1.163.18-1.79.18a9.916 9.916 0 0 1-1.84-.164 5.544 5.544 0 0 1-1.512-.493v-2.63a5.033 5.033 0 0 0 3.237 1.2c.333 0 .624-.03.872-.09.249-.06.456-.144.623-.25.166-.108.29-.234.373-.38a1.023 1.023 0 0 0-.074-1.089 2.12 2.12 0 0 0-.537-.5 5.597 5.597 0 0 0-.807-.444 27.72 27.72 0 0 0-1.007-.436c-.918-.383-1.602-.852-2.053-1.405-.45-.553-.676-1.222-.676-2.005 0-.614.123-1.141.369-1.582.246-.441.58-.804 1.004-1.089a4.494 4.494 0 0 1 1.47-.629 7.536 7.536 0 0 1 1.77-.201zm-15.113.188h9.563v2.166H9.506v9.646H6.789v-9.646H3.375z',
|
|
9
|
+
tailwindcss:
|
|
10
|
+
'M12.001,4.8c-3.2,0-5.2,1.6-6,4.8c1.2-1.6,2.6-2.2,4.2-1.8c0.913,0.228,1.565,0.89,2.288,1.624 C13.666,10.618,15.027,12,18.001,12c3.2,0,5.2-1.6,6-4.8c-1.2,1.6-2.6,2.2-4.2,1.8c-0.913-0.228-1.565-0.89-2.288-1.624 C16.337,6.182,14.976,4.8,12.001,4.8z M6.001,12c-3.2,0-5.2,1.6-6,4.8c1.2-1.6,2.6-2.2,4.2-1.8c0.913,0.228,1.565,0.89,2.288,1.624 c1.177,1.194,2.538,2.576,5.512,2.576c3.2,0,5.2-1.6,6-4.8c-1.2,1.6-2.6,2.2-4.2,1.8c-0.913-0.228-1.565-0.89-2.288-1.624 C10.337,13.382,8.976,12,6.001,12z',
|
|
11
|
+
react:
|
|
12
|
+
'M14.23 12.004a2.236 2.236 0 0 1-2.235 2.236 2.236 2.236 0 0 1-2.236-2.236 2.236 2.236 0 0 1 2.235-2.236 2.236 2.236 0 0 1 2.236 2.236zm2.648-10.69c-1.346 0-3.107.96-4.888 2.622-1.78-1.653-3.542-2.602-4.887-2.602-.41 0-.783.093-1.106.278-1.375.793-1.683 3.264-.973 6.365C1.98 8.917 0 10.42 0 12.004c0 1.59 1.99 3.097 5.043 4.03-.704 3.113-.39 5.588.988 6.38.32.187.69.275 1.102.275 1.345 0 3.107-.96 4.888-2.624 1.78 1.654 3.542 2.603 4.887 2.603.41 0 .783-.09 1.106-.275 1.374-.792 1.683-3.263.973-6.365C22.02 15.096 24 13.59 24 12.004c0-1.59-1.99-3.097-5.043-4.032.704-3.11.39-5.587-.988-6.38-.318-.184-.688-.277-1.092-.278zm-.005 1.09v.006c.225 0 .406.044.558.127.666.382.955 1.835.73 3.704-.054.46-.142.945-.25 1.44-.96-.236-2.006-.417-3.107-.534-.66-.905-1.345-1.727-2.035-2.447 1.592-1.48 3.087-2.292 4.105-2.295zm-9.77.02c1.012 0 2.514.808 4.11 2.28-.686.72-1.37 1.537-2.02 2.442-1.107.117-2.154.298-3.113.538-.112-.49-.195-.964-.254-1.42-.23-1.868.054-3.32.714-3.707.19-.09.4-.127.563-.132zm4.882 3.05c.455.468.91.992 1.36 1.564-.44-.02-.89-.034-1.345-.034-.46 0-.915.01-1.36.034.44-.572.895-1.096 1.345-1.565zM12 8.1c.74 0 1.477.034 2.202.093.406.582.802 1.203 1.183 1.86.372.64.71 1.29 1.018 1.946-.308.655-.646 1.31-1.013 1.95-.38.66-.773 1.288-1.18 1.87-.728.063-1.466.098-2.21.098-.74 0-1.477-.035-2.202-.093-.406-.582-.802-1.204-1.183-1.86-.372-.64-.71-1.29-1.018-1.946.303-.657.646-1.313 1.013-1.954.38-.66.773-1.286 1.18-1.868.728-.064 1.466-.098 2.21-.098zm-3.635.254c-.24.377-.48.763-.704 1.16-.225.39-.435.782-.635 1.174-.265-.656-.49-1.31-.676-1.947.64-.15 1.315-.283 2.015-.386zm7.26 0c.695.103 1.365.23 2.006.387-.18.632-.405 1.282-.66 1.933-.2-.39-.41-.783-.64-1.174-.225-.392-.465-.774-.705-1.146zm3.063.675c.484.15.944.317 1.375.498 1.732.74 2.852 1.708 2.852 2.476-.005.768-1.125 1.74-2.857 2.475-.42.18-.88.342-1.355.493-.28-.958-.646-1.956-1.1-2.98.45-1.017.81-2.01 1.085-2.964zm-13.395.004c.278.96.645 1.957 1.1 2.98-.45 1.017-.812 2.01-1.086 2.964-.484-.15-.944-.318-1.37-.5-1.732-.737-2.852-1.706-2.852-2.474 0-.768 1.12-1.742 2.852-2.476.42-.18.88-.342 1.356-.494zm11.678 4.28c.265.657.49 1.312.676 1.948-.64.157-1.316.29-2.016.39.24-.375.48-.762.705-1.158.225-.39.435-.788.636-1.18zm-9.945.02c.2.392.41.783.64 1.175.23.39.465.772.705 1.143-.695-.102-1.365-.23-2.006-.386.18-.63.406-1.282.66-1.933zM17.92 16.32c.112.493.2.968.254 1.423.23 1.868-.054 3.32-.714 3.708-.147.09-.338.128-.563.128-1.012 0-2.514-.807-4.11-2.28.686-.72 1.37-1.536 2.02-2.44 1.107-.118 2.154-.3 3.113-.54zm-11.83.01c.96.234 2.006.415 3.107.532.66.905 1.345 1.727 2.035 2.446-1.595 1.483-3.092 2.295-4.11 2.295-.22-.005-.406-.05-.553-.132-.666-.38-.955-1.834-.73-3.703.054-.46.142-.944.25-1.438zm4.56.64c.44.02.89.034 1.345.034.46 0 .915-.01 1.36-.034-.44.572-.895 1.095-1.345 1.565-.455-.47-.91-.993-1.36-1.565z',
|
|
13
|
+
prisma:
|
|
14
|
+
'M21.8068 18.2848L13.5528.7565c-.207-.4382-.639-.7273-1.1286-.7541-.5023-.0293-.9523.213-1.2062.6253L2.266 15.1271c-.2773.4518-.2718 1.0091.0158 1.4555l4.3759 6.7786c.2608.4046.7127.6388 1.1823.6388.1332 0 .267-.0188.3987-.0577l12.7019-3.7568c.3891-.1151.7072-.3904.8737-.7553s.1633-.7828-.0075-1.1454zm-1.8481.7519L9.1814 22.2242c-.3292.0975-.6448-.1873-.5756-.5194l3.8501-18.4386c.072-.3448.5486-.3996.699-.0803l7.1288 15.138c.1344.2856-.019.6224-.325.7128z',
|
|
15
|
+
github:
|
|
16
|
+
'M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12',
|
|
17
|
+
vercel: 'm12 1.608 12 20.784H0Z',
|
|
18
|
+
stripe:
|
|
19
|
+
'M13.976 9.15c-2.172-.806-3.356-1.426-3.356-2.409 0-.831.683-1.305 1.901-1.305 2.227 0 4.515.858 6.09 1.631l.89-5.494C18.252.975 15.697 0 12.165 0 9.667 0 7.589.654 6.104 1.872 4.56 3.147 3.757 4.992 3.757 7.218c0 4.039 2.467 5.76 6.476 7.219 2.585.92 3.445 1.574 3.445 2.583 0 .98-.84 1.545-2.354 1.545-1.875 0-4.965-.921-6.99-2.109l-.9 5.555C5.175 22.99 8.385 24 11.714 24c2.641 0 4.843-.624 6.328-1.813 1.664-1.305 2.525-3.236 2.525-5.732 0-4.128-2.524-5.851-6.594-7.305h.003z',
|
|
20
|
+
figma:
|
|
21
|
+
'M15.852 8.981h-4.588V0h4.588c2.476 0 4.49 2.014 4.49 4.49s-2.014 4.491-4.49 4.491zM12.735 7.51h3.117c1.665 0 3.019-1.355 3.019-3.019s-1.355-3.019-3.019-3.019h-3.117V7.51zm0 1.471H8.148c-2.476 0-4.49-2.014-4.49-4.49S5.672 0 8.148 0h4.588v8.981zm-4.587-7.51c-1.665 0-3.019 1.355-3.019 3.019s1.354 3.02 3.019 3.02h3.117V1.471H8.148zm4.587 15.019H8.148c-2.476 0-4.49-2.014-4.49-4.49s2.014-4.49 4.49-4.49h4.588v8.98zM8.148 8.981c-1.665 0-3.019 1.355-3.019 3.019s1.355 3.019 3.019 3.019h3.117V8.981H8.148zM8.172 24c-2.489 0-4.515-2.014-4.515-4.49s2.014-4.49 4.49-4.49h4.588v4.441c0 2.503-2.047 4.539-4.563 4.539zm-.024-7.51a3.023 3.023 0 0 0-3.019 3.019c0 1.665 1.365 3.019 3.044 3.019 1.705 0 3.093-1.376 3.093-3.068v-2.97H8.148zm7.704 0h-.098c-2.476 0-4.49-2.014-4.49-4.49s2.014-4.49 4.49-4.49h.098c2.476 0 4.49 2.014 4.49 4.49s-2.014 4.49-4.49 4.49zm-.097-7.509c-1.665 0-3.019 1.355-3.019 3.019s1.355 3.019 3.019 3.019h.098c1.665 0 3.019-1.355 3.019-3.019s-1.355-3.019-3.019-3.019h-.098z',
|
|
22
|
+
} as const
|
|
23
|
+
|
|
24
|
+
export type LogoCloud1LogoSlug = keyof typeof iconMap
|
|
25
|
+
|
|
26
|
+
export function LogoCloud1LogoIcon({
|
|
27
|
+
iconSlug,
|
|
28
|
+
size = 24,
|
|
29
|
+
className,
|
|
30
|
+
}: {
|
|
31
|
+
iconSlug: string
|
|
32
|
+
size?: number
|
|
33
|
+
className?: string
|
|
34
|
+
}) {
|
|
35
|
+
const iconPath = iconMap[iconSlug as LogoCloud1LogoSlug]
|
|
36
|
+
|
|
37
|
+
if (!iconPath) {
|
|
38
|
+
return <Skeleton className={cn('rounded-sm', className)} style={{ width: size, height: size }} />
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<svg
|
|
43
|
+
role='img'
|
|
44
|
+
viewBox='0 0 24 24'
|
|
45
|
+
className={cn('fill-foreground', className)}
|
|
46
|
+
style={{ width: size, height: size }}
|
|
47
|
+
aria-hidden='true'
|
|
48
|
+
>
|
|
49
|
+
<path d={iconPath} />
|
|
50
|
+
</svg>
|
|
51
|
+
)
|
|
52
|
+
}
|