@ecomiq/storefront 2.1.3 → 2.1.4
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/README.md +8 -28
- package/package.json +2 -7
- package/src/codegen.mjs +12 -0
- package/src/index.ts +0 -5
- package/src/pages/pages.$pageSlug.tsx +31 -68
- package/src/server.ts +0 -15
- package/src/styles.css +0 -21
- package/src/cloud-runtime-client.ts +0 -378
- package/src/storefront-definition/contract.ts +0 -1
- package/src/storefront-definition/data.ts +0 -188
- package/src/storefront-definition/index.ts +0 -2
- package/src/storefront-definition/renderer.tsx +0 -1481
- package/src/storefront-layout.ts +0 -59
- package/src/storefront-runtime.ts +0 -173
|
@@ -1,1481 +0,0 @@
|
|
|
1
|
-
import { Fragment, type CSSProperties, type ReactNode } from 'react'
|
|
2
|
-
import type { Collection, ProductSummary } from '@ecomiq/core'
|
|
3
|
-
import { buttonVariants } from '../ui/button'
|
|
4
|
-
import { cn } from '../ui/cn'
|
|
5
|
-
import {
|
|
6
|
-
resolveStorefrontSectionStyle,
|
|
7
|
-
scopeStorefrontCss,
|
|
8
|
-
storefrontSectionCssScope,
|
|
9
|
-
STOREFRONT_SITE_CSS_SCOPE,
|
|
10
|
-
} from '@ecomiq/storefront-definition'
|
|
11
|
-
import type {
|
|
12
|
-
StorefrontPage,
|
|
13
|
-
CollectionGridStorefrontSection,
|
|
14
|
-
HeroStorefrontSection,
|
|
15
|
-
ImageTextStorefrontSection,
|
|
16
|
-
NewsletterStorefrontSection,
|
|
17
|
-
ProductGridStorefrontSection,
|
|
18
|
-
PromoBannerStorefrontSection,
|
|
19
|
-
RichTextStorefrontSection,
|
|
20
|
-
StorefrontAction,
|
|
21
|
-
StorefrontDefinition,
|
|
22
|
-
StorefrontFontFamily,
|
|
23
|
-
StorefrontImage,
|
|
24
|
-
StorefrontNavigationItem,
|
|
25
|
-
StorefrontContentWidth,
|
|
26
|
-
StorefrontNamedStyle,
|
|
27
|
-
StorefrontSection,
|
|
28
|
-
StorefrontSectionStyle,
|
|
29
|
-
StorefrontSpace,
|
|
30
|
-
StorefrontThemeTokens,
|
|
31
|
-
TestimonialsStorefrontSection,
|
|
32
|
-
FeatureGridStorefrontSection,
|
|
33
|
-
FaqStorefrontSection,
|
|
34
|
-
StatsStorefrontSection,
|
|
35
|
-
LogoWallStorefrontSection,
|
|
36
|
-
GalleryStorefrontSection,
|
|
37
|
-
ColumnsStorefrontSection,
|
|
38
|
-
VideoStorefrontSection,
|
|
39
|
-
} from '@ecomiq/storefront-definition'
|
|
40
|
-
|
|
41
|
-
export interface DefinitionData {
|
|
42
|
-
productsBySectionId?: Readonly<
|
|
43
|
-
Record<string, readonly ProductSummary[] | undefined>
|
|
44
|
-
>
|
|
45
|
-
collectionsBySectionId?: Readonly<
|
|
46
|
-
Record<string, readonly Collection[] | undefined>
|
|
47
|
-
>
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface SectionContext {
|
|
51
|
-
data: DefinitionData
|
|
52
|
-
locale: string
|
|
53
|
-
currency: string
|
|
54
|
-
assets: AssetManifest
|
|
55
|
-
styles: readonly StorefrontNamedStyle[]
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export type AssetManifest = Readonly<Record<string, string>>
|
|
59
|
-
|
|
60
|
-
export function storefrontImageSrc(
|
|
61
|
-
image: StorefrontImage | undefined,
|
|
62
|
-
assets: AssetManifest,
|
|
63
|
-
): string | undefined {
|
|
64
|
-
if (image === undefined) return undefined
|
|
65
|
-
if (typeof image === 'string') return image
|
|
66
|
-
return assets[image.assetId]
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function storefrontImageAlt(
|
|
70
|
-
image: StorefrontImage | undefined,
|
|
71
|
-
fallback: string,
|
|
72
|
-
): string {
|
|
73
|
-
return typeof image === 'object' && image.alt ? image.alt : fallback
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export interface SectionProps<
|
|
77
|
-
TSection extends StorefrontSection,
|
|
78
|
-
> {
|
|
79
|
-
section: TSection
|
|
80
|
-
context: SectionContext
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export interface SectionRegistry {
|
|
84
|
-
hero: (
|
|
85
|
-
props: SectionProps<HeroStorefrontSection>,
|
|
86
|
-
) => ReactNode
|
|
87
|
-
'product-grid': (
|
|
88
|
-
props: SectionProps<ProductGridStorefrontSection>,
|
|
89
|
-
) => ReactNode
|
|
90
|
-
'collection-grid': (
|
|
91
|
-
props: SectionProps<CollectionGridStorefrontSection>,
|
|
92
|
-
) => ReactNode
|
|
93
|
-
'promo-banner': (
|
|
94
|
-
props: SectionProps<PromoBannerStorefrontSection>,
|
|
95
|
-
) => ReactNode
|
|
96
|
-
'image-text': (
|
|
97
|
-
props: SectionProps<ImageTextStorefrontSection>,
|
|
98
|
-
) => ReactNode
|
|
99
|
-
'feature-grid': (
|
|
100
|
-
props: SectionProps<FeatureGridStorefrontSection>,
|
|
101
|
-
) => ReactNode
|
|
102
|
-
faq: (props: SectionProps<FaqStorefrontSection>) => ReactNode
|
|
103
|
-
stats: (props: SectionProps<StatsStorefrontSection>) => ReactNode
|
|
104
|
-
'logo-wall': (
|
|
105
|
-
props: SectionProps<LogoWallStorefrontSection>,
|
|
106
|
-
) => ReactNode
|
|
107
|
-
gallery: (props: SectionProps<GalleryStorefrontSection>) => ReactNode
|
|
108
|
-
columns: (props: SectionProps<ColumnsStorefrontSection>) => ReactNode
|
|
109
|
-
video: (props: SectionProps<VideoStorefrontSection>) => ReactNode
|
|
110
|
-
'rich-text': (
|
|
111
|
-
props: SectionProps<RichTextStorefrontSection>,
|
|
112
|
-
) => ReactNode
|
|
113
|
-
testimonials: (
|
|
114
|
-
props: SectionProps<TestimonialsStorefrontSection>,
|
|
115
|
-
) => ReactNode
|
|
116
|
-
newsletter: (
|
|
117
|
-
props: SectionProps<NewsletterStorefrontSection>,
|
|
118
|
-
) => ReactNode
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export interface DefinitionSectionsProps {
|
|
122
|
-
definition: StorefrontDefinition
|
|
123
|
-
pageId?: string
|
|
124
|
-
data?: DefinitionData
|
|
125
|
-
locale?: string
|
|
126
|
-
currency?: string
|
|
127
|
-
registry?: SectionRegistry
|
|
128
|
-
assets?: AssetManifest
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export function storefrontPageFor(
|
|
132
|
-
definition: StorefrontDefinition,
|
|
133
|
-
pageId?: string,
|
|
134
|
-
): StorefrontPage | null {
|
|
135
|
-
if (pageId === undefined) {
|
|
136
|
-
return definition.pages.find((page) => page.kind === 'home') ?? null
|
|
137
|
-
}
|
|
138
|
-
return definition.pages.find((page) => page.id === pageId) ?? null
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export interface DefinitionShellProps {
|
|
142
|
-
definition: StorefrontDefinition
|
|
143
|
-
children: ReactNode
|
|
144
|
-
showChrome?: boolean
|
|
145
|
-
className?: string
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export interface DefinitionRendererProps extends DefinitionSectionsProps {
|
|
149
|
-
showChrome?: boolean
|
|
150
|
-
className?: string
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
type StorefrontThemeStyle = CSSProperties & {
|
|
154
|
-
'--background': string
|
|
155
|
-
'--foreground': string
|
|
156
|
-
'--card': string
|
|
157
|
-
'--card-foreground': string
|
|
158
|
-
'--primary': string
|
|
159
|
-
'--primary-foreground': string
|
|
160
|
-
'--secondary': string
|
|
161
|
-
'--secondary-foreground': string
|
|
162
|
-
'--muted': string
|
|
163
|
-
'--muted-foreground': string
|
|
164
|
-
'--accent': string
|
|
165
|
-
'--accent-foreground': string
|
|
166
|
-
'--border': string
|
|
167
|
-
'--input': string
|
|
168
|
-
'--ring': string
|
|
169
|
-
'--radius': string
|
|
170
|
-
'--storefront-font-body': string
|
|
171
|
-
'--storefront-font-heading': string
|
|
172
|
-
'--storefront-section-padding': string
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function fontStack(font: StorefrontFontFamily): string {
|
|
176
|
-
if (font === 'editorial-serif') {
|
|
177
|
-
return 'Iowan Old Style, Baskerville, Times New Roman, serif'
|
|
178
|
-
}
|
|
179
|
-
if (font === 'humanist-sans') {
|
|
180
|
-
return 'Avenir Next, Avenir, Segoe UI, sans-serif'
|
|
181
|
-
}
|
|
182
|
-
return 'ui-sans-serif, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif'
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function radiusValue(radius: StorefrontThemeTokens['radius']): string {
|
|
186
|
-
if (radius === 'none') return '0rem'
|
|
187
|
-
if (radius === 'small') return '0.375rem'
|
|
188
|
-
if (radius === 'large') return '1rem'
|
|
189
|
-
return '0.625rem'
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function sectionPaddingValue(
|
|
193
|
-
density: StorefrontThemeTokens['density'],
|
|
194
|
-
): string {
|
|
195
|
-
if (density === 'compact') return '2.5rem'
|
|
196
|
-
if (density === 'airy') return '5rem'
|
|
197
|
-
return '4rem'
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function themeStyle(theme: StorefrontThemeTokens): StorefrontThemeStyle {
|
|
201
|
-
return {
|
|
202
|
-
'--background': theme.colors.background,
|
|
203
|
-
'--foreground': theme.colors.foreground,
|
|
204
|
-
'--card': theme.colors.card,
|
|
205
|
-
'--card-foreground': theme.colors.cardForeground,
|
|
206
|
-
'--primary': theme.colors.primary,
|
|
207
|
-
'--primary-foreground': theme.colors.primaryForeground,
|
|
208
|
-
'--secondary': theme.colors.secondary,
|
|
209
|
-
'--secondary-foreground': theme.colors.secondaryForeground,
|
|
210
|
-
'--muted': theme.colors.muted,
|
|
211
|
-
'--muted-foreground': theme.colors.mutedForeground,
|
|
212
|
-
'--accent': theme.colors.accent,
|
|
213
|
-
'--accent-foreground': theme.colors.accentForeground,
|
|
214
|
-
'--border': theme.colors.border,
|
|
215
|
-
'--input': theme.colors.border,
|
|
216
|
-
'--ring': theme.colors.primary,
|
|
217
|
-
'--radius': radiusValue(theme.radius),
|
|
218
|
-
'--storefront-font-body': fontStack(theme.typography.body),
|
|
219
|
-
'--storefront-font-heading': fontStack(theme.typography.heading),
|
|
220
|
-
'--storefront-section-padding': sectionPaddingValue(theme.density),
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function sectionSurfaceClass(section: StorefrontSection): string {
|
|
225
|
-
if (section.surface === 'muted') return 'bg-muted'
|
|
226
|
-
if (section.surface === 'accent') {
|
|
227
|
-
return 'bg-accent text-accent-foreground'
|
|
228
|
-
}
|
|
229
|
-
return 'bg-background'
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const SPACE_VALUES: Record<StorefrontSpace, string> = {
|
|
233
|
-
none: '0rem',
|
|
234
|
-
xs: '1rem',
|
|
235
|
-
sm: '2rem',
|
|
236
|
-
md: '3.5rem',
|
|
237
|
-
lg: '5.5rem',
|
|
238
|
-
xl: '8rem',
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const CONTENT_WIDTH_VALUES: Record<StorefrontContentWidth, string> = {
|
|
242
|
-
narrow: '48rem',
|
|
243
|
-
contained: '72rem',
|
|
244
|
-
wide: '90rem',
|
|
245
|
-
full: '100%',
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const TEXT_STEPS: ReadonlyArray<readonly [string, number]> = [
|
|
249
|
-
['--text-sm', 0.875],
|
|
250
|
-
['--text-base', 1],
|
|
251
|
-
['--text-lg', 1.125],
|
|
252
|
-
['--text-xl', 1.25],
|
|
253
|
-
['--text-2xl', 1.5],
|
|
254
|
-
['--text-3xl', 1.875],
|
|
255
|
-
['--text-4xl', 2.25],
|
|
256
|
-
['--text-5xl', 3],
|
|
257
|
-
['--text-6xl', 3.75],
|
|
258
|
-
]
|
|
259
|
-
|
|
260
|
-
const FONT_SCALE_VALUES = { sm: 0.875, md: 1, lg: 1.125, xl: 1.25 }
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Superficie derivada del fondo de la seccion: en vez de dejar las tarjetas y
|
|
264
|
-
* los bordes con el token del tema (blanco sobre un fondo oscuro), se acercan
|
|
265
|
-
* al color del texto lo justo para seguir leyendose como una capa aparte.
|
|
266
|
-
*/
|
|
267
|
-
function lift(background: string, amount = '8%'): string {
|
|
268
|
-
return `color-mix(in oklab, ${background}, var(--foreground) ${amount})`
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function readableOn(background: string): string {
|
|
272
|
-
const value = background.slice(1)
|
|
273
|
-
const channels = [0, 2, 4].map((offset) => {
|
|
274
|
-
const channel = Number.parseInt(value.slice(offset, offset + 2), 16) / 255
|
|
275
|
-
return channel <= 0.04045
|
|
276
|
-
? channel / 12.92
|
|
277
|
-
: ((channel + 0.055) / 1.055) ** 2.4
|
|
278
|
-
})
|
|
279
|
-
const luminance =
|
|
280
|
-
0.2126 * (channels[0] ?? 0) +
|
|
281
|
-
0.7152 * (channels[1] ?? 0) +
|
|
282
|
-
0.0722 * (channels[2] ?? 0)
|
|
283
|
-
return luminance > 0.36 ? '#0b0b0c' : '#ffffff'
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function sectionFrameStyle(
|
|
287
|
-
style: StorefrontSectionStyle,
|
|
288
|
-
assets: AssetManifest,
|
|
289
|
-
): CSSProperties {
|
|
290
|
-
const variables: Record<string, string> = {}
|
|
291
|
-
|
|
292
|
-
const backgroundImageSrc = storefrontImageSrc(style.backgroundImage, assets)
|
|
293
|
-
if (style.background) {
|
|
294
|
-
variables['--background'] = style.background
|
|
295
|
-
variables['--muted'] = style.background
|
|
296
|
-
variables['--accent'] = style.background
|
|
297
|
-
variables['--card'] = lift(style.background)
|
|
298
|
-
variables['--secondary'] = lift(style.background)
|
|
299
|
-
variables['--card-foreground'] = 'var(--foreground)'
|
|
300
|
-
variables['--secondary-foreground'] = 'var(--foreground)'
|
|
301
|
-
if (!style.border) variables['--border'] = lift(style.background, '18%')
|
|
302
|
-
}
|
|
303
|
-
if (backgroundImageSrc) {
|
|
304
|
-
variables['--background'] = 'transparent'
|
|
305
|
-
variables['--muted'] = 'transparent'
|
|
306
|
-
variables['--accent'] = 'transparent'
|
|
307
|
-
}
|
|
308
|
-
if (style.text) {
|
|
309
|
-
variables['--foreground'] = style.text
|
|
310
|
-
variables['--accent-foreground'] = style.text
|
|
311
|
-
variables['--muted-foreground'] =
|
|
312
|
-
`color-mix(in oklab, ${style.text} 68%, transparent)`
|
|
313
|
-
}
|
|
314
|
-
if (style.accent) {
|
|
315
|
-
variables['--primary'] = style.accent
|
|
316
|
-
variables['--primary-foreground'] = readableOn(style.accent)
|
|
317
|
-
variables['--ring'] = style.accent
|
|
318
|
-
}
|
|
319
|
-
if (style.border) {
|
|
320
|
-
variables['--border'] = style.border
|
|
321
|
-
variables['--input'] = style.border
|
|
322
|
-
}
|
|
323
|
-
if (style.radius) variables['--radius'] = radiusValue(style.radius)
|
|
324
|
-
if (style.paddingTop) {
|
|
325
|
-
variables['--storefront-section-padding-top'] = SPACE_VALUES[style.paddingTop]
|
|
326
|
-
}
|
|
327
|
-
if (style.paddingBottom) {
|
|
328
|
-
variables['--storefront-section-padding-bottom'] =
|
|
329
|
-
SPACE_VALUES[style.paddingBottom]
|
|
330
|
-
}
|
|
331
|
-
if (style.width) {
|
|
332
|
-
variables['--storefront-content-width'] = CONTENT_WIDTH_VALUES[style.width]
|
|
333
|
-
}
|
|
334
|
-
if (style.fontScale && style.fontScale !== 'md') {
|
|
335
|
-
const factor = FONT_SCALE_VALUES[style.fontScale]
|
|
336
|
-
for (const [token, rem] of TEXT_STEPS) {
|
|
337
|
-
variables[token] = `${(rem * factor).toFixed(4)}rem`
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
if (backgroundImageSrc) {
|
|
341
|
-
variables.backgroundImage = `url("${backgroundImageSrc}")`
|
|
342
|
-
variables.backgroundSize = 'cover'
|
|
343
|
-
variables.backgroundPosition = 'center'
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
return variables as CSSProperties
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
function sectionFrameClass(
|
|
350
|
-
style: StorefrontSectionStyle,
|
|
351
|
-
): string | undefined {
|
|
352
|
-
if (style.visibility === 'desktop') return 'hidden md:block'
|
|
353
|
-
if (style.visibility === 'mobile') return 'md:hidden'
|
|
354
|
-
return undefined
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
export function storefrontPageCss(
|
|
358
|
-
page: StorefrontPage,
|
|
359
|
-
styles: readonly StorefrontNamedStyle[],
|
|
360
|
-
): string {
|
|
361
|
-
return page.sections
|
|
362
|
-
.filter((section) => section.enabled)
|
|
363
|
-
.map((section) => {
|
|
364
|
-
const css = resolveStorefrontSectionStyle(section, styles).css
|
|
365
|
-
return css
|
|
366
|
-
? scopeStorefrontCss(css, storefrontSectionCssScope(section.id))
|
|
367
|
-
: ''
|
|
368
|
-
})
|
|
369
|
-
.filter((css) => css.length > 0)
|
|
370
|
-
.join('\n')
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function StorefrontCustomStyle({ css }: { css: string }) {
|
|
374
|
-
if (css.length === 0) return null
|
|
375
|
-
return <style dangerouslySetInnerHTML={{ __html: css }} />
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
function StorefrontActionLink({
|
|
379
|
-
action,
|
|
380
|
-
variant = 'default',
|
|
381
|
-
}: {
|
|
382
|
-
action: StorefrontAction
|
|
383
|
-
variant?: 'default' | 'secondary' | 'outline'
|
|
384
|
-
}) {
|
|
385
|
-
const className = buttonVariants({ variant, size: 'lg' })
|
|
386
|
-
const isExternal =
|
|
387
|
-
action.href.startsWith('https://') ||
|
|
388
|
-
action.href.startsWith('mailto:') ||
|
|
389
|
-
action.href.startsWith('tel:')
|
|
390
|
-
|
|
391
|
-
const opensNewWindow = action.href.startsWith('https://')
|
|
392
|
-
return (
|
|
393
|
-
<a
|
|
394
|
-
href={action.href}
|
|
395
|
-
className={className}
|
|
396
|
-
rel={isExternal && opensNewWindow ? 'noreferrer' : undefined}
|
|
397
|
-
target={isExternal && opensNewWindow ? '_blank' : undefined}
|
|
398
|
-
>
|
|
399
|
-
{action.label}
|
|
400
|
-
</a>
|
|
401
|
-
)
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
function StorefrontNavigationLink({
|
|
405
|
-
item,
|
|
406
|
-
}: {
|
|
407
|
-
item: StorefrontNavigationItem
|
|
408
|
-
}) {
|
|
409
|
-
const className =
|
|
410
|
-
'flex min-h-10 items-center px-2 text-sm text-muted-foreground transition-colors [@media(hover:hover)]:hover:text-foreground'
|
|
411
|
-
const opensNewWindow = item.external || item.href.startsWith('https://')
|
|
412
|
-
const isExternal =
|
|
413
|
-
opensNewWindow ||
|
|
414
|
-
item.href.startsWith('mailto:') ||
|
|
415
|
-
item.href.startsWith('tel:')
|
|
416
|
-
|
|
417
|
-
return (
|
|
418
|
-
<a
|
|
419
|
-
href={item.href}
|
|
420
|
-
className={className}
|
|
421
|
-
rel={isExternal && opensNewWindow ? 'noreferrer' : undefined}
|
|
422
|
-
target={isExternal && opensNewWindow ? '_blank' : undefined}
|
|
423
|
-
>
|
|
424
|
-
{item.label}
|
|
425
|
-
</a>
|
|
426
|
-
)
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
function HeroSectionRenderer({
|
|
430
|
-
section,
|
|
431
|
-
context,
|
|
432
|
-
}: SectionProps<HeroStorefrontSection>) {
|
|
433
|
-
const isCentered = section.align === 'center'
|
|
434
|
-
const heroImageSrc = storefrontImageSrc(section.image, context.assets)
|
|
435
|
-
return (
|
|
436
|
-
<section
|
|
437
|
-
className={cn(
|
|
438
|
-
'storefront-definition-section relative isolate min-h-[min(72vh,680px)] overflow-hidden',
|
|
439
|
-
sectionSurfaceClass(section),
|
|
440
|
-
)}
|
|
441
|
-
aria-labelledby={`${section.id}-title`}
|
|
442
|
-
data-storefront-section={section.id}
|
|
443
|
-
>
|
|
444
|
-
{heroImageSrc ? (
|
|
445
|
-
<>
|
|
446
|
-
<img
|
|
447
|
-
src={heroImageSrc}
|
|
448
|
-
alt={storefrontImageAlt(section.image, section.imageAlt)}
|
|
449
|
-
width={1600}
|
|
450
|
-
height={900}
|
|
451
|
-
fetchPriority="high"
|
|
452
|
-
className="absolute inset-0 -z-20 h-full w-full object-cover"
|
|
453
|
-
/>
|
|
454
|
-
<div className="absolute inset-0 -z-10 bg-black/55" aria-hidden />
|
|
455
|
-
</>
|
|
456
|
-
) : null}
|
|
457
|
-
<div
|
|
458
|
-
className={cn(
|
|
459
|
-
'mx-auto flex min-h-[inherit] w-full max-w-(--storefront-content-width) items-center px-4',
|
|
460
|
-
isCentered && 'justify-center text-center',
|
|
461
|
-
)}
|
|
462
|
-
>
|
|
463
|
-
<div
|
|
464
|
-
className={cn(
|
|
465
|
-
'max-w-2xl',
|
|
466
|
-
section.image && 'text-white',
|
|
467
|
-
isCentered && 'flex flex-col items-center',
|
|
468
|
-
)}
|
|
469
|
-
>
|
|
470
|
-
{section.eyebrow ? (
|
|
471
|
-
<p className="mb-4 text-xs font-semibold uppercase tracking-[0.16em] opacity-75" data-storefront-text="eyebrow">
|
|
472
|
-
{section.eyebrow}
|
|
473
|
-
</p>
|
|
474
|
-
) : null}
|
|
475
|
-
<h1
|
|
476
|
-
id={`${section.id}-title`}
|
|
477
|
-
data-storefront-text="title"
|
|
478
|
-
className="text-balance text-4xl font-semibold leading-[1.02] tracking-[-0.022em] sm:text-5xl lg:text-6xl"
|
|
479
|
-
>
|
|
480
|
-
{section.title}
|
|
481
|
-
</h1>
|
|
482
|
-
{section.description ? (
|
|
483
|
-
<p className="mt-5 max-w-[65ch] text-pretty text-base leading-relaxed opacity-80 sm:text-lg" data-storefront-text="description">
|
|
484
|
-
{section.description}
|
|
485
|
-
</p>
|
|
486
|
-
) : null}
|
|
487
|
-
{section.primaryAction || section.secondaryAction ? (
|
|
488
|
-
<div className="mt-8 flex flex-wrap gap-3">
|
|
489
|
-
{section.primaryAction ? (
|
|
490
|
-
<StorefrontActionLink action={section.primaryAction} />
|
|
491
|
-
) : null}
|
|
492
|
-
{section.secondaryAction ? (
|
|
493
|
-
<StorefrontActionLink
|
|
494
|
-
action={section.secondaryAction}
|
|
495
|
-
variant="outline"
|
|
496
|
-
/>
|
|
497
|
-
) : null}
|
|
498
|
-
</div>
|
|
499
|
-
) : null}
|
|
500
|
-
</div>
|
|
501
|
-
</div>
|
|
502
|
-
</section>
|
|
503
|
-
)
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
function gridColumnsClass(columns: 2 | 3 | 4): string {
|
|
507
|
-
if (columns === 2) return 'sm:grid-cols-2'
|
|
508
|
-
if (columns === 3) return 'sm:grid-cols-2 lg:grid-cols-3'
|
|
509
|
-
return 'sm:grid-cols-2 lg:grid-cols-4'
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
function SectionHeading({
|
|
513
|
-
id,
|
|
514
|
-
title,
|
|
515
|
-
description,
|
|
516
|
-
}: {
|
|
517
|
-
id: string
|
|
518
|
-
title: string
|
|
519
|
-
description?: string
|
|
520
|
-
}) {
|
|
521
|
-
return (
|
|
522
|
-
<header className="mb-8 max-w-2xl">
|
|
523
|
-
<h2
|
|
524
|
-
id={id}
|
|
525
|
-
data-storefront-text="title"
|
|
526
|
-
className="text-balance text-3xl font-semibold tracking-[-0.022em]"
|
|
527
|
-
>
|
|
528
|
-
{title}
|
|
529
|
-
</h2>
|
|
530
|
-
{description ? (
|
|
531
|
-
<p className="mt-3 text-pretty leading-relaxed text-muted-foreground" data-storefront-text="description">
|
|
532
|
-
{description}
|
|
533
|
-
</p>
|
|
534
|
-
) : null}
|
|
535
|
-
</header>
|
|
536
|
-
)
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
function ProductGridSectionRenderer({
|
|
540
|
-
section,
|
|
541
|
-
context,
|
|
542
|
-
}: SectionProps<ProductGridStorefrontSection>) {
|
|
543
|
-
const products = (context.data.productsBySectionId?.[section.id] ?? []).slice(
|
|
544
|
-
0,
|
|
545
|
-
section.limit,
|
|
546
|
-
)
|
|
547
|
-
const formatter = new Intl.NumberFormat(context.locale, {
|
|
548
|
-
style: 'currency',
|
|
549
|
-
currency: context.currency,
|
|
550
|
-
})
|
|
551
|
-
|
|
552
|
-
return (
|
|
553
|
-
<section
|
|
554
|
-
className={cn(
|
|
555
|
-
'storefront-definition-section',
|
|
556
|
-
sectionSurfaceClass(section),
|
|
557
|
-
)}
|
|
558
|
-
aria-labelledby={`${section.id}-title`}
|
|
559
|
-
data-storefront-section={section.id}
|
|
560
|
-
>
|
|
561
|
-
<div className="mx-auto w-full max-w-(--storefront-content-width) px-4">
|
|
562
|
-
<SectionHeading
|
|
563
|
-
id={`${section.id}-title`}
|
|
564
|
-
title={section.title}
|
|
565
|
-
description={section.description}
|
|
566
|
-
/>
|
|
567
|
-
{products.length > 0 ? (
|
|
568
|
-
<div
|
|
569
|
-
className={cn(
|
|
570
|
-
'grid grid-cols-2 gap-x-4 gap-y-8',
|
|
571
|
-
gridColumnsClass(section.columns),
|
|
572
|
-
)}
|
|
573
|
-
>
|
|
574
|
-
{products.map((product) => (
|
|
575
|
-
<a
|
|
576
|
-
key={product.id}
|
|
577
|
-
href={`/products/${product.slug}`}
|
|
578
|
-
className="group rounded-lg focus-visible:ring-2 focus-visible:ring-ring"
|
|
579
|
-
>
|
|
580
|
-
<div className="aspect-square overflow-hidden rounded-lg bg-muted outline outline-1 -outline-offset-1 outline-black/10">
|
|
581
|
-
{product.imageUrl ? (
|
|
582
|
-
<img
|
|
583
|
-
src={product.imageUrl}
|
|
584
|
-
alt={product.name}
|
|
585
|
-
width={640}
|
|
586
|
-
height={640}
|
|
587
|
-
loading="lazy"
|
|
588
|
-
className="h-full w-full object-cover transition-transform duration-300 ease-out [@media(hover:hover)]:group-hover:scale-[1.025] motion-reduce:transition-none"
|
|
589
|
-
/>
|
|
590
|
-
) : (
|
|
591
|
-
<div className="flex h-full items-center justify-center px-4 text-center text-xs text-muted-foreground">
|
|
592
|
-
Imagen no disponible
|
|
593
|
-
</div>
|
|
594
|
-
)}
|
|
595
|
-
</div>
|
|
596
|
-
<div className="pt-3">
|
|
597
|
-
{product.brand ? (
|
|
598
|
-
<p className="text-xs text-muted-foreground">
|
|
599
|
-
{product.brand}
|
|
600
|
-
</p>
|
|
601
|
-
) : null}
|
|
602
|
-
<h3 className="mt-1 line-clamp-2 text-pretty text-sm font-medium leading-snug">
|
|
603
|
-
{product.name}
|
|
604
|
-
</h3>
|
|
605
|
-
<p className="mt-1 text-sm font-semibold tabular-nums">
|
|
606
|
-
{formatter.format(product.price)}
|
|
607
|
-
</p>
|
|
608
|
-
</div>
|
|
609
|
-
</a>
|
|
610
|
-
))}
|
|
611
|
-
</div>
|
|
612
|
-
) : (
|
|
613
|
-
<p className="border-t py-6 text-sm text-muted-foreground">
|
|
614
|
-
Aún no hay productos para mostrar.
|
|
615
|
-
</p>
|
|
616
|
-
)}
|
|
617
|
-
</div>
|
|
618
|
-
</section>
|
|
619
|
-
)
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
function CollectionGridSectionRenderer({
|
|
623
|
-
section,
|
|
624
|
-
context,
|
|
625
|
-
}: SectionProps<CollectionGridStorefrontSection>) {
|
|
626
|
-
const collections = (
|
|
627
|
-
context.data.collectionsBySectionId?.[section.id] ?? []
|
|
628
|
-
).slice(0, section.limit)
|
|
629
|
-
|
|
630
|
-
return (
|
|
631
|
-
<section
|
|
632
|
-
className={cn(
|
|
633
|
-
'storefront-definition-section',
|
|
634
|
-
sectionSurfaceClass(section),
|
|
635
|
-
)}
|
|
636
|
-
aria-labelledby={`${section.id}-title`}
|
|
637
|
-
data-storefront-section={section.id}
|
|
638
|
-
>
|
|
639
|
-
<div className="mx-auto w-full max-w-(--storefront-content-width) px-4">
|
|
640
|
-
<SectionHeading
|
|
641
|
-
id={`${section.id}-title`}
|
|
642
|
-
title={section.title}
|
|
643
|
-
description={section.description}
|
|
644
|
-
/>
|
|
645
|
-
{collections.length > 0 ? (
|
|
646
|
-
<div className={cn('grid gap-4', gridColumnsClass(section.columns))}>
|
|
647
|
-
{collections.map((collection) => (
|
|
648
|
-
<a
|
|
649
|
-
key={collection.id}
|
|
650
|
-
href={`/collections/${collection.slug}`}
|
|
651
|
-
className="group relative min-h-72 overflow-hidden rounded-lg bg-muted focus-visible:ring-2 focus-visible:ring-ring"
|
|
652
|
-
>
|
|
653
|
-
{collection.imageUrl ? (
|
|
654
|
-
<img
|
|
655
|
-
src={collection.imageUrl}
|
|
656
|
-
alt={collection.name}
|
|
657
|
-
width={960}
|
|
658
|
-
height={720}
|
|
659
|
-
loading="lazy"
|
|
660
|
-
className="absolute inset-0 h-full w-full object-cover transition-transform duration-500 ease-out [@media(hover:hover)]:group-hover:scale-105 motion-reduce:transition-none"
|
|
661
|
-
/>
|
|
662
|
-
) : null}
|
|
663
|
-
<div className="absolute inset-0 bg-black/55" aria-hidden />
|
|
664
|
-
<div className="absolute inset-x-0 bottom-0 p-5 text-white">
|
|
665
|
-
<h3 className="text-balance text-xl font-semibold tracking-[-0.012em]">
|
|
666
|
-
{collection.name}
|
|
667
|
-
</h3>
|
|
668
|
-
{collection.description ? (
|
|
669
|
-
<p className="mt-1 line-clamp-2 text-pretty text-sm text-white/80">
|
|
670
|
-
{collection.description}
|
|
671
|
-
</p>
|
|
672
|
-
) : null}
|
|
673
|
-
</div>
|
|
674
|
-
</a>
|
|
675
|
-
))}
|
|
676
|
-
</div>
|
|
677
|
-
) : (
|
|
678
|
-
<p className="border-t py-6 text-sm text-muted-foreground">
|
|
679
|
-
Aún no hay colecciones para mostrar.
|
|
680
|
-
</p>
|
|
681
|
-
)}
|
|
682
|
-
</div>
|
|
683
|
-
</section>
|
|
684
|
-
)
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
function PromoBannerSectionRenderer({
|
|
688
|
-
section,
|
|
689
|
-
}: SectionProps<PromoBannerStorefrontSection>) {
|
|
690
|
-
const isPrimary = section.tone === 'primary'
|
|
691
|
-
return (
|
|
692
|
-
<section
|
|
693
|
-
className={cn(
|
|
694
|
-
'storefront-definition-section',
|
|
695
|
-
isPrimary
|
|
696
|
-
? 'bg-primary text-primary-foreground'
|
|
697
|
-
: sectionSurfaceClass(section),
|
|
698
|
-
)}
|
|
699
|
-
aria-labelledby={`${section.id}-title`}
|
|
700
|
-
data-storefront-section={section.id}
|
|
701
|
-
>
|
|
702
|
-
<div className="mx-auto flex w-full max-w-(--storefront-content-width) flex-col gap-6 px-4 sm:flex-row sm:items-end sm:justify-between">
|
|
703
|
-
<div className="max-w-3xl">
|
|
704
|
-
{section.eyebrow ? (
|
|
705
|
-
<p className="mb-3 text-xs font-semibold uppercase tracking-[0.16em] opacity-75" data-storefront-text="eyebrow">
|
|
706
|
-
{section.eyebrow}
|
|
707
|
-
</p>
|
|
708
|
-
) : null}
|
|
709
|
-
<h2
|
|
710
|
-
id={`${section.id}-title`}
|
|
711
|
-
data-storefront-text="title"
|
|
712
|
-
className="text-balance text-3xl font-semibold tracking-[-0.022em]"
|
|
713
|
-
>
|
|
714
|
-
{section.title}
|
|
715
|
-
</h2>
|
|
716
|
-
{section.description ? (
|
|
717
|
-
<p className="mt-3 max-w-[65ch] text-pretty leading-relaxed opacity-80" data-storefront-text="description">
|
|
718
|
-
{section.description}
|
|
719
|
-
</p>
|
|
720
|
-
) : null}
|
|
721
|
-
</div>
|
|
722
|
-
{section.action ? (
|
|
723
|
-
<StorefrontActionLink
|
|
724
|
-
action={section.action}
|
|
725
|
-
variant={isPrimary ? 'secondary' : 'default'}
|
|
726
|
-
/>
|
|
727
|
-
) : null}
|
|
728
|
-
</div>
|
|
729
|
-
</section>
|
|
730
|
-
)
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
function ImageTextSectionRenderer({
|
|
734
|
-
section,
|
|
735
|
-
context,
|
|
736
|
-
}: SectionProps<ImageTextStorefrontSection>) {
|
|
737
|
-
const imageFirst = section.imagePosition === 'left'
|
|
738
|
-
const imageSrc = storefrontImageSrc(section.image, context.assets)
|
|
739
|
-
return (
|
|
740
|
-
<section
|
|
741
|
-
className={cn(
|
|
742
|
-
'storefront-definition-section',
|
|
743
|
-
sectionSurfaceClass(section),
|
|
744
|
-
)}
|
|
745
|
-
aria-labelledby={`${section.id}-title`}
|
|
746
|
-
data-storefront-section={section.id}
|
|
747
|
-
>
|
|
748
|
-
<div className="mx-auto grid w-full max-w-(--storefront-content-width) items-center gap-8 px-4 md:grid-cols-2 md:gap-12">
|
|
749
|
-
<img
|
|
750
|
-
src={imageSrc}
|
|
751
|
-
alt={storefrontImageAlt(section.image, section.imageAlt)}
|
|
752
|
-
width={960}
|
|
753
|
-
height={720}
|
|
754
|
-
loading="lazy"
|
|
755
|
-
className={cn(
|
|
756
|
-
'aspect-[4/3] h-full w-full rounded-lg object-cover outline outline-1 -outline-offset-1 outline-black/10',
|
|
757
|
-
!imageFirst && 'md:order-2',
|
|
758
|
-
)}
|
|
759
|
-
/>
|
|
760
|
-
<div className={cn(!imageFirst && 'md:order-1')}>
|
|
761
|
-
{section.eyebrow ? (
|
|
762
|
-
<p className="mb-3 text-xs font-semibold uppercase tracking-[0.16em] text-muted-foreground" data-storefront-text="eyebrow">
|
|
763
|
-
{section.eyebrow}
|
|
764
|
-
</p>
|
|
765
|
-
) : null}
|
|
766
|
-
<h2
|
|
767
|
-
id={`${section.id}-title`}
|
|
768
|
-
data-storefront-text="title"
|
|
769
|
-
className="text-balance text-3xl font-semibold tracking-[-0.022em]"
|
|
770
|
-
>
|
|
771
|
-
{section.title}
|
|
772
|
-
</h2>
|
|
773
|
-
<p className="mt-4 max-w-[65ch] whitespace-pre-line text-pretty leading-relaxed text-muted-foreground" data-storefront-text="body">
|
|
774
|
-
{section.body}
|
|
775
|
-
</p>
|
|
776
|
-
{section.action ? (
|
|
777
|
-
<div className="mt-7">
|
|
778
|
-
<StorefrontActionLink action={section.action} />
|
|
779
|
-
</div>
|
|
780
|
-
) : null}
|
|
781
|
-
</div>
|
|
782
|
-
</div>
|
|
783
|
-
</section>
|
|
784
|
-
)
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
function RichTextSectionRenderer({
|
|
788
|
-
section,
|
|
789
|
-
}: SectionProps<RichTextStorefrontSection>) {
|
|
790
|
-
const isCentered = section.align === 'center'
|
|
791
|
-
return (
|
|
792
|
-
<section
|
|
793
|
-
className={cn(
|
|
794
|
-
'storefront-definition-section',
|
|
795
|
-
sectionSurfaceClass(section),
|
|
796
|
-
)}
|
|
797
|
-
aria-labelledby={section.title ? `${section.id}-title` : undefined}
|
|
798
|
-
data-storefront-section={section.id}
|
|
799
|
-
>
|
|
800
|
-
<div
|
|
801
|
-
className={cn(
|
|
802
|
-
'mx-auto w-full max-w-3xl px-4',
|
|
803
|
-
isCentered && 'text-center',
|
|
804
|
-
)}
|
|
805
|
-
>
|
|
806
|
-
{section.title ? (
|
|
807
|
-
<h2
|
|
808
|
-
id={`${section.id}-title`}
|
|
809
|
-
data-storefront-text="title"
|
|
810
|
-
className="text-balance text-3xl font-semibold tracking-[-0.022em]"
|
|
811
|
-
>
|
|
812
|
-
{section.title}
|
|
813
|
-
</h2>
|
|
814
|
-
) : null}
|
|
815
|
-
<div className={cn('space-y-4', section.title && 'mt-5')}>
|
|
816
|
-
{section.paragraphs.map((paragraph, index) => (
|
|
817
|
-
<p
|
|
818
|
-
key={`${section.id}-paragraph-${index}`}
|
|
819
|
-
className="whitespace-pre-line text-pretty leading-7 text-muted-foreground"
|
|
820
|
-
>
|
|
821
|
-
{paragraph}
|
|
822
|
-
</p>
|
|
823
|
-
))}
|
|
824
|
-
</div>
|
|
825
|
-
</div>
|
|
826
|
-
</section>
|
|
827
|
-
)
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
function testimonialColumnsClass(columns: 1 | 2 | 3): string {
|
|
831
|
-
if (columns === 1) return 'grid-cols-1'
|
|
832
|
-
if (columns === 2) return 'md:grid-cols-2'
|
|
833
|
-
return 'md:grid-cols-2 lg:grid-cols-3'
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
function TestimonialsSectionRenderer({
|
|
837
|
-
section,
|
|
838
|
-
}: SectionProps<TestimonialsStorefrontSection>) {
|
|
839
|
-
return (
|
|
840
|
-
<section
|
|
841
|
-
className={cn(
|
|
842
|
-
'storefront-definition-section',
|
|
843
|
-
sectionSurfaceClass(section),
|
|
844
|
-
)}
|
|
845
|
-
aria-labelledby={`${section.id}-title`}
|
|
846
|
-
data-storefront-section={section.id}
|
|
847
|
-
>
|
|
848
|
-
<div className="mx-auto w-full max-w-(--storefront-content-width) px-4">
|
|
849
|
-
<SectionHeading
|
|
850
|
-
id={`${section.id}-title`}
|
|
851
|
-
title={section.title}
|
|
852
|
-
description={section.description}
|
|
853
|
-
/>
|
|
854
|
-
<div
|
|
855
|
-
className={cn('grid gap-5', testimonialColumnsClass(section.columns))}
|
|
856
|
-
>
|
|
857
|
-
{section.blocks.map((item) => (
|
|
858
|
-
<figure
|
|
859
|
-
key={item.id}
|
|
860
|
-
className="flex h-full flex-col justify-between border-t pt-6 text-foreground"
|
|
861
|
-
>
|
|
862
|
-
<blockquote className="text-pretty text-lg leading-relaxed">
|
|
863
|
-
“{item.quote}”
|
|
864
|
-
</blockquote>
|
|
865
|
-
<figcaption className="mt-8 text-sm">
|
|
866
|
-
<span className="font-semibold">{item.author}</span>
|
|
867
|
-
{item.role ? (
|
|
868
|
-
<span className="mt-1 block text-muted-foreground">
|
|
869
|
-
{item.role}
|
|
870
|
-
</span>
|
|
871
|
-
) : null}
|
|
872
|
-
</figcaption>
|
|
873
|
-
</figure>
|
|
874
|
-
))}
|
|
875
|
-
</div>
|
|
876
|
-
</div>
|
|
877
|
-
</section>
|
|
878
|
-
)
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
function NewsletterSectionRenderer({
|
|
882
|
-
section,
|
|
883
|
-
}: SectionProps<NewsletterStorefrontSection>) {
|
|
884
|
-
return (
|
|
885
|
-
<section
|
|
886
|
-
className={cn(
|
|
887
|
-
'storefront-definition-section',
|
|
888
|
-
sectionSurfaceClass(section),
|
|
889
|
-
)}
|
|
890
|
-
aria-labelledby={`${section.id}-title`}
|
|
891
|
-
data-storefront-section={section.id}
|
|
892
|
-
>
|
|
893
|
-
<div className="mx-auto grid w-full max-w-(--storefront-content-width) gap-7 px-4 md:grid-cols-[1fr_minmax(280px,440px)] md:items-end">
|
|
894
|
-
<div className="max-w-2xl">
|
|
895
|
-
{section.eyebrow ? (
|
|
896
|
-
<p className="mb-3 text-xs font-semibold uppercase tracking-[0.16em] text-muted-foreground" data-storefront-text="eyebrow">
|
|
897
|
-
{section.eyebrow}
|
|
898
|
-
</p>
|
|
899
|
-
) : null}
|
|
900
|
-
<h2
|
|
901
|
-
id={`${section.id}-title`}
|
|
902
|
-
data-storefront-text="title"
|
|
903
|
-
className="text-balance text-3xl font-semibold tracking-[-0.022em]"
|
|
904
|
-
>
|
|
905
|
-
{section.title}
|
|
906
|
-
</h2>
|
|
907
|
-
{section.description ? (
|
|
908
|
-
<p className="mt-3 text-pretty leading-relaxed text-muted-foreground" data-storefront-text="description">
|
|
909
|
-
{section.description}
|
|
910
|
-
</p>
|
|
911
|
-
) : null}
|
|
912
|
-
</div>
|
|
913
|
-
<form action={section.formAction} method="post" className="flex gap-2">
|
|
914
|
-
<label htmlFor={`${section.id}-email`} className="sr-only">
|
|
915
|
-
Correo electrónico
|
|
916
|
-
</label>
|
|
917
|
-
<input
|
|
918
|
-
id={`${section.id}-email`}
|
|
919
|
-
name="email"
|
|
920
|
-
type="email"
|
|
921
|
-
autoComplete="email"
|
|
922
|
-
required
|
|
923
|
-
placeholder={section.emailPlaceholder}
|
|
924
|
-
className="min-h-10 min-w-0 flex-1 rounded-md border border-input bg-background px-3 text-sm text-foreground shadow-sm placeholder:text-muted-foreground"
|
|
925
|
-
/>
|
|
926
|
-
<button
|
|
927
|
-
type="submit"
|
|
928
|
-
className={buttonVariants({ variant: 'default', size: 'lg' })}
|
|
929
|
-
>
|
|
930
|
-
{section.buttonLabel}
|
|
931
|
-
</button>
|
|
932
|
-
</form>
|
|
933
|
-
</div>
|
|
934
|
-
</section>
|
|
935
|
-
)
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
function FeatureGridSectionRenderer({
|
|
940
|
-
section,
|
|
941
|
-
}: SectionProps<FeatureGridStorefrontSection>) {
|
|
942
|
-
return (
|
|
943
|
-
<section
|
|
944
|
-
className={cn(
|
|
945
|
-
'storefront-definition-section',
|
|
946
|
-
sectionSurfaceClass(section),
|
|
947
|
-
)}
|
|
948
|
-
data-storefront-section={section.id}
|
|
949
|
-
>
|
|
950
|
-
<div className="mx-auto w-full max-w-(--storefront-content-width) px-4">
|
|
951
|
-
{section.title ? (
|
|
952
|
-
<SectionHeading
|
|
953
|
-
id={`${section.id}-title`}
|
|
954
|
-
title={section.title}
|
|
955
|
-
description={section.description}
|
|
956
|
-
/>
|
|
957
|
-
) : null}
|
|
958
|
-
<div className={cn('grid gap-6', gridColumnsClass(section.columns))}>
|
|
959
|
-
{section.blocks.map((block) => (
|
|
960
|
-
<div key={block.id}>
|
|
961
|
-
<h3
|
|
962
|
-
className="text-base font-semibold"
|
|
963
|
-
data-storefront-text="title"
|
|
964
|
-
>
|
|
965
|
-
{block.title}
|
|
966
|
-
</h3>
|
|
967
|
-
{block.description ? (
|
|
968
|
-
<p
|
|
969
|
-
className="mt-2 text-pretty text-sm leading-relaxed text-muted-foreground"
|
|
970
|
-
data-storefront-text="description"
|
|
971
|
-
>
|
|
972
|
-
{block.description}
|
|
973
|
-
</p>
|
|
974
|
-
) : null}
|
|
975
|
-
</div>
|
|
976
|
-
))}
|
|
977
|
-
</div>
|
|
978
|
-
</div>
|
|
979
|
-
</section>
|
|
980
|
-
)
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
function FaqSectionRenderer({ section }: SectionProps<FaqStorefrontSection>) {
|
|
984
|
-
return (
|
|
985
|
-
<section
|
|
986
|
-
className={cn(
|
|
987
|
-
'storefront-definition-section',
|
|
988
|
-
sectionSurfaceClass(section),
|
|
989
|
-
)}
|
|
990
|
-
aria-labelledby={`${section.id}-title`}
|
|
991
|
-
data-storefront-section={section.id}
|
|
992
|
-
>
|
|
993
|
-
<div className="mx-auto w-full max-w-3xl px-4">
|
|
994
|
-
<SectionHeading
|
|
995
|
-
id={`${section.id}-title`}
|
|
996
|
-
title={section.title}
|
|
997
|
-
description={section.description}
|
|
998
|
-
/>
|
|
999
|
-
<dl className="divide-y border-t">
|
|
1000
|
-
{section.blocks.map((block) => (
|
|
1001
|
-
<div key={block.id} className="py-5">
|
|
1002
|
-
<dt className="text-base font-semibold">{block.question}</dt>
|
|
1003
|
-
<dd className="mt-2 whitespace-pre-line text-pretty leading-relaxed text-muted-foreground">
|
|
1004
|
-
{block.answer}
|
|
1005
|
-
</dd>
|
|
1006
|
-
</div>
|
|
1007
|
-
))}
|
|
1008
|
-
</dl>
|
|
1009
|
-
</div>
|
|
1010
|
-
</section>
|
|
1011
|
-
)
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
function StatsSectionRenderer({
|
|
1015
|
-
section,
|
|
1016
|
-
}: SectionProps<StatsStorefrontSection>) {
|
|
1017
|
-
return (
|
|
1018
|
-
<section
|
|
1019
|
-
className={cn(
|
|
1020
|
-
'storefront-definition-section',
|
|
1021
|
-
sectionSurfaceClass(section),
|
|
1022
|
-
)}
|
|
1023
|
-
data-storefront-section={section.id}
|
|
1024
|
-
>
|
|
1025
|
-
<div className="mx-auto w-full max-w-(--storefront-content-width) px-4">
|
|
1026
|
-
{section.title ? (
|
|
1027
|
-
<SectionHeading id={`${section.id}-title`} title={section.title} />
|
|
1028
|
-
) : null}
|
|
1029
|
-
<dl className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
|
|
1030
|
-
{section.blocks.map((block) => (
|
|
1031
|
-
<div key={block.id}>
|
|
1032
|
-
<dt className="text-3xl font-semibold tracking-[-0.02em]">
|
|
1033
|
-
{block.value}
|
|
1034
|
-
</dt>
|
|
1035
|
-
<dd className="mt-1 text-sm text-muted-foreground">
|
|
1036
|
-
{block.label}
|
|
1037
|
-
</dd>
|
|
1038
|
-
</div>
|
|
1039
|
-
))}
|
|
1040
|
-
</dl>
|
|
1041
|
-
</div>
|
|
1042
|
-
</section>
|
|
1043
|
-
)
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
function LogoWallSectionRenderer({
|
|
1047
|
-
section,
|
|
1048
|
-
context,
|
|
1049
|
-
}: SectionProps<LogoWallStorefrontSection>) {
|
|
1050
|
-
return (
|
|
1051
|
-
<section
|
|
1052
|
-
className={cn(
|
|
1053
|
-
'storefront-definition-section',
|
|
1054
|
-
sectionSurfaceClass(section),
|
|
1055
|
-
)}
|
|
1056
|
-
data-storefront-section={section.id}
|
|
1057
|
-
>
|
|
1058
|
-
<div className="mx-auto w-full max-w-(--storefront-content-width) px-4">
|
|
1059
|
-
{section.title ? (
|
|
1060
|
-
<SectionHeading id={`${section.id}-title`} title={section.title} />
|
|
1061
|
-
) : null}
|
|
1062
|
-
<ul className="flex flex-wrap items-center justify-center gap-x-10 gap-y-6">
|
|
1063
|
-
{section.blocks.map((block) => {
|
|
1064
|
-
const src = storefrontImageSrc(block.image, context.assets)
|
|
1065
|
-
if (!src) return null
|
|
1066
|
-
return (
|
|
1067
|
-
<li key={block.id}>
|
|
1068
|
-
<img
|
|
1069
|
-
alt={block.alt}
|
|
1070
|
-
className="h-8 w-auto opacity-70 transition-opacity hover:opacity-100"
|
|
1071
|
-
loading="lazy"
|
|
1072
|
-
src={src}
|
|
1073
|
-
/>
|
|
1074
|
-
</li>
|
|
1075
|
-
)
|
|
1076
|
-
})}
|
|
1077
|
-
</ul>
|
|
1078
|
-
</div>
|
|
1079
|
-
</section>
|
|
1080
|
-
)
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
function GallerySectionRenderer({
|
|
1084
|
-
section,
|
|
1085
|
-
context,
|
|
1086
|
-
}: SectionProps<GalleryStorefrontSection>) {
|
|
1087
|
-
return (
|
|
1088
|
-
<section
|
|
1089
|
-
className={cn(
|
|
1090
|
-
'storefront-definition-section',
|
|
1091
|
-
sectionSurfaceClass(section),
|
|
1092
|
-
)}
|
|
1093
|
-
data-storefront-section={section.id}
|
|
1094
|
-
>
|
|
1095
|
-
<div className="mx-auto w-full max-w-(--storefront-content-width) px-4">
|
|
1096
|
-
{section.title ? (
|
|
1097
|
-
<SectionHeading
|
|
1098
|
-
id={`${section.id}-title`}
|
|
1099
|
-
title={section.title}
|
|
1100
|
-
description={section.description}
|
|
1101
|
-
/>
|
|
1102
|
-
) : null}
|
|
1103
|
-
<div className={cn('grid gap-3', gridColumnsClass(section.columns))}>
|
|
1104
|
-
{section.blocks.map((block) => {
|
|
1105
|
-
const src = storefrontImageSrc(block.image, context.assets)
|
|
1106
|
-
if (!src) return null
|
|
1107
|
-
return (
|
|
1108
|
-
<figure key={block.id}>
|
|
1109
|
-
<img
|
|
1110
|
-
alt={block.alt}
|
|
1111
|
-
className="aspect-[4/3] w-full rounded-md object-cover"
|
|
1112
|
-
loading="lazy"
|
|
1113
|
-
src={src}
|
|
1114
|
-
/>
|
|
1115
|
-
{block.caption ? (
|
|
1116
|
-
<figcaption className="mt-2 text-xs text-muted-foreground">
|
|
1117
|
-
{block.caption}
|
|
1118
|
-
</figcaption>
|
|
1119
|
-
) : null}
|
|
1120
|
-
</figure>
|
|
1121
|
-
)
|
|
1122
|
-
})}
|
|
1123
|
-
</div>
|
|
1124
|
-
</div>
|
|
1125
|
-
</section>
|
|
1126
|
-
)
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
function ColumnsSectionRenderer({
|
|
1130
|
-
section,
|
|
1131
|
-
context,
|
|
1132
|
-
}: SectionProps<ColumnsStorefrontSection>) {
|
|
1133
|
-
const columns = Math.min(Math.max(section.blocks.length, 2), 4) as 2 | 3 | 4
|
|
1134
|
-
return (
|
|
1135
|
-
<section
|
|
1136
|
-
className={cn(
|
|
1137
|
-
'storefront-definition-section',
|
|
1138
|
-
sectionSurfaceClass(section),
|
|
1139
|
-
)}
|
|
1140
|
-
data-storefront-section={section.id}
|
|
1141
|
-
>
|
|
1142
|
-
<div className="mx-auto w-full max-w-(--storefront-content-width) px-4">
|
|
1143
|
-
{section.title ? (
|
|
1144
|
-
<SectionHeading
|
|
1145
|
-
id={`${section.id}-title`}
|
|
1146
|
-
title={section.title}
|
|
1147
|
-
description={section.description}
|
|
1148
|
-
/>
|
|
1149
|
-
) : null}
|
|
1150
|
-
<div className={cn('grid gap-8', gridColumnsClass(columns))}>
|
|
1151
|
-
{section.blocks.map((block) => {
|
|
1152
|
-
const src = storefrontImageSrc(block.image, context.assets)
|
|
1153
|
-
return (
|
|
1154
|
-
<div key={block.id}>
|
|
1155
|
-
{src ? (
|
|
1156
|
-
<img
|
|
1157
|
-
alt={block.imageAlt ?? ''}
|
|
1158
|
-
className="mb-4 aspect-[4/3] w-full rounded-md object-cover"
|
|
1159
|
-
loading="lazy"
|
|
1160
|
-
src={src}
|
|
1161
|
-
/>
|
|
1162
|
-
) : null}
|
|
1163
|
-
<h3
|
|
1164
|
-
className="text-lg font-semibold"
|
|
1165
|
-
data-storefront-text="title"
|
|
1166
|
-
>
|
|
1167
|
-
{block.title}
|
|
1168
|
-
</h3>
|
|
1169
|
-
{block.body ? (
|
|
1170
|
-
<p className="mt-2 text-pretty leading-relaxed text-muted-foreground">
|
|
1171
|
-
{block.body}
|
|
1172
|
-
</p>
|
|
1173
|
-
) : null}
|
|
1174
|
-
{block.action ? (
|
|
1175
|
-
<div className="mt-4">
|
|
1176
|
-
<StorefrontActionLink action={block.action} />
|
|
1177
|
-
</div>
|
|
1178
|
-
) : null}
|
|
1179
|
-
</div>
|
|
1180
|
-
)
|
|
1181
|
-
})}
|
|
1182
|
-
</div>
|
|
1183
|
-
</div>
|
|
1184
|
-
</section>
|
|
1185
|
-
)
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
function VideoSectionRenderer({
|
|
1189
|
-
section,
|
|
1190
|
-
context,
|
|
1191
|
-
}: SectionProps<VideoStorefrontSection>) {
|
|
1192
|
-
const poster = storefrontImageSrc(section.poster, context.assets)
|
|
1193
|
-
return (
|
|
1194
|
-
<section
|
|
1195
|
-
className={cn(
|
|
1196
|
-
'storefront-definition-section',
|
|
1197
|
-
sectionSurfaceClass(section),
|
|
1198
|
-
)}
|
|
1199
|
-
data-storefront-section={section.id}
|
|
1200
|
-
>
|
|
1201
|
-
<div className="mx-auto w-full max-w-4xl px-4">
|
|
1202
|
-
{section.title ? (
|
|
1203
|
-
<SectionHeading
|
|
1204
|
-
id={`${section.id}-title`}
|
|
1205
|
-
title={section.title}
|
|
1206
|
-
description={section.description}
|
|
1207
|
-
/>
|
|
1208
|
-
) : null}
|
|
1209
|
-
<div className="overflow-hidden rounded-lg border bg-muted">
|
|
1210
|
-
<video
|
|
1211
|
-
className="aspect-video w-full"
|
|
1212
|
-
controls
|
|
1213
|
-
preload="none"
|
|
1214
|
-
{...(poster ? { poster } : {})}
|
|
1215
|
-
src={section.url}
|
|
1216
|
-
>
|
|
1217
|
-
{section.posterAlt ?? ''}
|
|
1218
|
-
</video>
|
|
1219
|
-
</div>
|
|
1220
|
-
</div>
|
|
1221
|
-
</section>
|
|
1222
|
-
)
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
export const defaultStorefrontSectionRegistry: SectionRegistry = {
|
|
1226
|
-
hero: HeroSectionRenderer,
|
|
1227
|
-
'product-grid': ProductGridSectionRenderer,
|
|
1228
|
-
'collection-grid': CollectionGridSectionRenderer,
|
|
1229
|
-
'promo-banner': PromoBannerSectionRenderer,
|
|
1230
|
-
'image-text': ImageTextSectionRenderer,
|
|
1231
|
-
'rich-text': RichTextSectionRenderer,
|
|
1232
|
-
testimonials: TestimonialsSectionRenderer,
|
|
1233
|
-
newsletter: NewsletterSectionRenderer,
|
|
1234
|
-
'feature-grid': FeatureGridSectionRenderer,
|
|
1235
|
-
faq: FaqSectionRenderer,
|
|
1236
|
-
stats: StatsSectionRenderer,
|
|
1237
|
-
'logo-wall': LogoWallSectionRenderer,
|
|
1238
|
-
gallery: GallerySectionRenderer,
|
|
1239
|
-
columns: ColumnsSectionRenderer,
|
|
1240
|
-
video: VideoSectionRenderer,
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
function StorefrontSectionFrame({
|
|
1244
|
-
section,
|
|
1245
|
-
context,
|
|
1246
|
-
children,
|
|
1247
|
-
}: {
|
|
1248
|
-
section: StorefrontSection
|
|
1249
|
-
context: SectionContext
|
|
1250
|
-
children: ReactNode
|
|
1251
|
-
}) {
|
|
1252
|
-
const resolved = resolveStorefrontSectionStyle(section, context.styles)
|
|
1253
|
-
const style = sectionFrameStyle(resolved, context.assets)
|
|
1254
|
-
const className = sectionFrameClass(resolved)
|
|
1255
|
-
const overlay = resolved.backgroundOverlay
|
|
1256
|
-
const hasFrame =
|
|
1257
|
-
Object.keys(style).length > 0 || className !== undefined || Boolean(overlay)
|
|
1258
|
-
if (!hasFrame) return children
|
|
1259
|
-
|
|
1260
|
-
return (
|
|
1261
|
-
<div
|
|
1262
|
-
className={cn(
|
|
1263
|
-
'storefront-definition-frame text-foreground',
|
|
1264
|
-
Boolean(resolved.backgroundImage) && 'relative isolate',
|
|
1265
|
-
className,
|
|
1266
|
-
)}
|
|
1267
|
-
style={style}
|
|
1268
|
-
data-storefront-section-frame={section.id}
|
|
1269
|
-
>
|
|
1270
|
-
{overlay ? (
|
|
1271
|
-
<div
|
|
1272
|
-
aria-hidden
|
|
1273
|
-
className="absolute inset-0 -z-10 bg-black"
|
|
1274
|
-
style={{ opacity: overlay }}
|
|
1275
|
-
/>
|
|
1276
|
-
) : null}
|
|
1277
|
-
{children}
|
|
1278
|
-
</div>
|
|
1279
|
-
)
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
export function renderStorefrontSection(
|
|
1283
|
-
section: StorefrontSection,
|
|
1284
|
-
registry: SectionRegistry,
|
|
1285
|
-
context: SectionContext,
|
|
1286
|
-
): ReactNode {
|
|
1287
|
-
return (
|
|
1288
|
-
<StorefrontSectionFrame section={section} context={context}>
|
|
1289
|
-
{renderStorefrontSectionBody(section, registry, context)}
|
|
1290
|
-
</StorefrontSectionFrame>
|
|
1291
|
-
)
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
function renderStorefrontSectionBody(
|
|
1295
|
-
section: StorefrontSection,
|
|
1296
|
-
registry: SectionRegistry,
|
|
1297
|
-
context: SectionContext,
|
|
1298
|
-
): ReactNode {
|
|
1299
|
-
if (section.type === 'hero') return registry.hero({ section, context })
|
|
1300
|
-
if (section.type === 'product-grid') {
|
|
1301
|
-
return registry['product-grid']({ section, context })
|
|
1302
|
-
}
|
|
1303
|
-
if (section.type === 'collection-grid') {
|
|
1304
|
-
return registry['collection-grid']({ section, context })
|
|
1305
|
-
}
|
|
1306
|
-
if (section.type === 'promo-banner') {
|
|
1307
|
-
return registry['promo-banner']({ section, context })
|
|
1308
|
-
}
|
|
1309
|
-
if (section.type === 'image-text') {
|
|
1310
|
-
return registry['image-text']({ section, context })
|
|
1311
|
-
}
|
|
1312
|
-
if (section.type === 'rich-text') {
|
|
1313
|
-
return registry['rich-text']({ section, context })
|
|
1314
|
-
}
|
|
1315
|
-
if (section.type === 'testimonials') {
|
|
1316
|
-
return registry.testimonials({ section, context })
|
|
1317
|
-
}
|
|
1318
|
-
if (section.type === 'feature-grid') {
|
|
1319
|
-
return registry['feature-grid']({ section, context })
|
|
1320
|
-
}
|
|
1321
|
-
if (section.type === 'faq') {
|
|
1322
|
-
return registry.faq({ section, context })
|
|
1323
|
-
}
|
|
1324
|
-
if (section.type === 'stats') {
|
|
1325
|
-
return registry.stats({ section, context })
|
|
1326
|
-
}
|
|
1327
|
-
if (section.type === 'logo-wall') {
|
|
1328
|
-
return registry['logo-wall']({ section, context })
|
|
1329
|
-
}
|
|
1330
|
-
if (section.type === 'gallery') {
|
|
1331
|
-
return registry.gallery({ section, context })
|
|
1332
|
-
}
|
|
1333
|
-
if (section.type === 'columns') {
|
|
1334
|
-
return registry.columns({ section, context })
|
|
1335
|
-
}
|
|
1336
|
-
if (section.type === 'video') {
|
|
1337
|
-
return registry.video({ section, context })
|
|
1338
|
-
}
|
|
1339
|
-
return registry.newsletter({ section, context })
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
function StorefrontChrome({
|
|
1343
|
-
definition,
|
|
1344
|
-
children,
|
|
1345
|
-
}: {
|
|
1346
|
-
definition: StorefrontDefinition
|
|
1347
|
-
children: ReactNode
|
|
1348
|
-
}) {
|
|
1349
|
-
return (
|
|
1350
|
-
<>
|
|
1351
|
-
<a
|
|
1352
|
-
href="#storefront-definition-main"
|
|
1353
|
-
className="sr-only z-100 rounded-md bg-background px-3 py-2 text-sm focus:not-sr-only focus:fixed focus:left-3 focus:top-3"
|
|
1354
|
-
>
|
|
1355
|
-
Ir al contenido
|
|
1356
|
-
</a>
|
|
1357
|
-
{definition.site.navigation.items.length > 0 ? (
|
|
1358
|
-
<header className="border-b bg-background" data-storefront-chrome="navigation">
|
|
1359
|
-
<nav
|
|
1360
|
-
className="mx-auto flex min-h-14 w-full max-w-(--storefront-content-width) flex-wrap items-center gap-x-4 px-4"
|
|
1361
|
-
aria-label="Navegación principal"
|
|
1362
|
-
>
|
|
1363
|
-
{definition.site.navigation.items.map((item) => (
|
|
1364
|
-
<StorefrontNavigationLink key={item.id} item={item} />
|
|
1365
|
-
))}
|
|
1366
|
-
</nav>
|
|
1367
|
-
</header>
|
|
1368
|
-
) : null}
|
|
1369
|
-
<main id="storefront-definition-main">{children}</main>
|
|
1370
|
-
<footer className="border-t bg-muted/40" data-storefront-chrome="footer">
|
|
1371
|
-
<div className="mx-auto flex w-full max-w-(--storefront-content-width) flex-col gap-4 px-4 py-8 text-sm text-muted-foreground sm:flex-row sm:items-center sm:justify-between">
|
|
1372
|
-
{definition.site.footer.text ? <p>{definition.site.footer.text}</p> : null}
|
|
1373
|
-
{definition.site.footer.items.length > 0 ? (
|
|
1374
|
-
<nav
|
|
1375
|
-
className="flex flex-wrap gap-x-3"
|
|
1376
|
-
aria-label="Navegación del pie"
|
|
1377
|
-
>
|
|
1378
|
-
{definition.site.footer.items.map((item) => (
|
|
1379
|
-
<StorefrontNavigationLink key={item.id} item={item} />
|
|
1380
|
-
))}
|
|
1381
|
-
</nav>
|
|
1382
|
-
) : null}
|
|
1383
|
-
</div>
|
|
1384
|
-
</footer>
|
|
1385
|
-
</>
|
|
1386
|
-
)
|
|
1387
|
-
}
|
|
1388
|
-
|
|
1389
|
-
export function DefinitionSections({
|
|
1390
|
-
definition,
|
|
1391
|
-
pageId,
|
|
1392
|
-
data = {},
|
|
1393
|
-
locale = 'es-PE',
|
|
1394
|
-
currency = 'PEN',
|
|
1395
|
-
registry = defaultStorefrontSectionRegistry,
|
|
1396
|
-
assets = {},
|
|
1397
|
-
}: DefinitionSectionsProps) {
|
|
1398
|
-
const context: SectionContext = {
|
|
1399
|
-
data,
|
|
1400
|
-
locale,
|
|
1401
|
-
currency,
|
|
1402
|
-
assets,
|
|
1403
|
-
styles: definition.site.styles,
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
const page = storefrontPageFor(definition, pageId)
|
|
1407
|
-
if (!page) return null
|
|
1408
|
-
|
|
1409
|
-
return (
|
|
1410
|
-
<>
|
|
1411
|
-
<StorefrontCustomStyle
|
|
1412
|
-
css={storefrontPageCss(page, definition.site.styles)}
|
|
1413
|
-
/>
|
|
1414
|
-
{page.sections
|
|
1415
|
-
.filter((section) => section.enabled)
|
|
1416
|
-
.map((section) => (
|
|
1417
|
-
<Fragment key={section.id}>
|
|
1418
|
-
{renderStorefrontSection(section, registry, context)}
|
|
1419
|
-
</Fragment>
|
|
1420
|
-
))}
|
|
1421
|
-
</>
|
|
1422
|
-
)
|
|
1423
|
-
}
|
|
1424
|
-
|
|
1425
|
-
export function DefinitionShell({
|
|
1426
|
-
definition,
|
|
1427
|
-
children,
|
|
1428
|
-
showChrome = true,
|
|
1429
|
-
className,
|
|
1430
|
-
}: DefinitionShellProps) {
|
|
1431
|
-
const content = showChrome ? (
|
|
1432
|
-
<StorefrontChrome definition={definition}>{children}</StorefrontChrome>
|
|
1433
|
-
) : (
|
|
1434
|
-
children
|
|
1435
|
-
)
|
|
1436
|
-
|
|
1437
|
-
return (
|
|
1438
|
-
<div
|
|
1439
|
-
className={cn(
|
|
1440
|
-
'storefront-definition min-h-full bg-background text-foreground antialiased',
|
|
1441
|
-
className,
|
|
1442
|
-
)}
|
|
1443
|
-
style={themeStyle(definition.site.theme)}
|
|
1444
|
-
data-storefront-definition-version={definition.version}
|
|
1445
|
-
data-storefront-definition-revision={definition.revision}
|
|
1446
|
-
>
|
|
1447
|
-
<StorefrontCustomStyle
|
|
1448
|
-
css={scopeStorefrontCss(definition.site.css, STOREFRONT_SITE_CSS_SCOPE)}
|
|
1449
|
-
/>
|
|
1450
|
-
{content}
|
|
1451
|
-
</div>
|
|
1452
|
-
)
|
|
1453
|
-
}
|
|
1454
|
-
|
|
1455
|
-
export function DefinitionRenderer({
|
|
1456
|
-
definition,
|
|
1457
|
-
pageId,
|
|
1458
|
-
data,
|
|
1459
|
-
locale,
|
|
1460
|
-
currency,
|
|
1461
|
-
registry,
|
|
1462
|
-
showChrome = true,
|
|
1463
|
-
className,
|
|
1464
|
-
}: DefinitionRendererProps) {
|
|
1465
|
-
return (
|
|
1466
|
-
<DefinitionShell
|
|
1467
|
-
definition={definition}
|
|
1468
|
-
showChrome={showChrome}
|
|
1469
|
-
className={className}
|
|
1470
|
-
>
|
|
1471
|
-
<DefinitionSections
|
|
1472
|
-
definition={definition}
|
|
1473
|
-
pageId={pageId}
|
|
1474
|
-
data={data}
|
|
1475
|
-
locale={locale}
|
|
1476
|
-
currency={currency}
|
|
1477
|
-
registry={registry}
|
|
1478
|
-
/>
|
|
1479
|
-
</DefinitionShell>
|
|
1480
|
-
)
|
|
1481
|
-
}
|