@vtex/faststore 0.0.8 → 0.0.10
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/CHANGELOG.md +59 -0
- package/dist/actions.json +42 -0
- package/dist/catalog.json +731 -16
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/components.d.ts +9 -4
- package/dist/components.js +11 -6
- package/dist/content-types.json +300 -0
- package/dist/core/dist/define-component.d.ts +6 -1
- package/dist/docs/account-entry-point.md +4 -4
- package/dist/docs/account-menu.md +2 -2
- package/dist/docs/account-saved-cards.md +301 -0
- package/dist/docs/binding-switcher.md +154 -0
- package/dist/docs/card-entry.md +3 -3
- package/dist/docs/card-summary.md +175 -0
- package/dist/docs/cart-destination.md +238 -0
- package/dist/docs/cart-item.md +21 -3
- package/dist/docs/cart-totalizer.md +1 -1
- package/dist/docs/cart.md +118 -11
- package/dist/docs/footer-social.md +1 -1
- package/dist/docs/free-shipping-progress.md +13 -9
- package/dist/docs/incentives.md +2 -2
- package/dist/docs/logo.md +1 -1
- package/dist/docs/payment-method-badges.md +2 -2
- package/dist/docs/payment-methods.md +1 -1
- package/dist/docs/product-information.md +2 -2
- package/dist/docs/product-shelf.md +7 -0
- package/dist/docs/select.md +3 -4
- package/dist/docs/sign-in-redirect.md +1 -1
- package/dist/docs/toast.md +30 -12
- package/dist/index.d.ts +2 -1
- package/dist/internal/client.d.ts +4 -1
- package/dist/internal/client.js +4 -1
- package/dist/internal/server.d.ts +4 -2
- package/dist/internal/server.js +4 -2
- package/dist/matter/account-saved-cards/account-saved-cards.d.ts +383 -0
- package/dist/matter/account-saved-cards/account-saved-cards.js +956 -0
- package/dist/matter/account-saved-cards/account-saved-cards.js.map +1 -0
- package/dist/matter/billing-address/billing-address.js.map +1 -1
- package/dist/matter/cart/cart-namespace.d.ts +5 -1
- package/dist/matter/cart/cart-namespace.js +5 -1
- package/dist/matter/cart/cart-namespace.js.map +1 -1
- package/dist/matter/cart/cart.d.ts +34 -4
- package/dist/matter/cart/cart.js +238 -28
- package/dist/matter/cart/cart.js.map +1 -1
- package/dist/matter/cart-destination/cart-destination.d.ts +222 -0
- package/dist/matter/cart-destination/cart-destination.js +613 -0
- package/dist/matter/cart-destination/cart-destination.js.map +1 -0
- package/dist/matter/cart-item/cart-item.d.ts +4 -2
- package/dist/matter/cart-item/cart-item.js +75 -16
- package/dist/matter/cart-item/cart-item.js.map +1 -1
- package/dist/matter/cart-totalizer/cart-totalizer.js +3 -2
- package/dist/matter/cart-totalizer/cart-totalizer.js.map +1 -1
- package/dist/matter/free-shipping-progress/free-shipping-progress.d.ts +6 -3
- package/dist/matter/free-shipping-progress/free-shipping-progress.js +76 -33
- package/dist/matter/free-shipping-progress/free-shipping-progress.js.map +1 -1
- package/dist/matter/pricing/pricing.js.map +1 -1
- package/dist/matter/product-shelf/product-shelf.d.ts +7 -0
- package/dist/matter/product-shelf/product-shelf.js +115 -1
- package/dist/matter/product-shelf/product-shelf.js.map +1 -1
- package/dist/matter/shipping-address/shipping-address.js.map +1 -1
- package/dist/matter/shipping-simulator/shipping-simulator.d.ts +1 -1
- package/dist/matter/shipping-simulator/shipping-simulator.js +6 -2
- package/dist/matter/shipping-simulator/shipping-simulator.js.map +1 -1
- package/dist/particles/binding-switcher/binding-switcher.d.ts +38 -0
- package/dist/particles/binding-switcher/binding-switcher.js +183 -0
- package/dist/particles/binding-switcher/binding-switcher.js.map +1 -0
- package/dist/particles/card-summary/card-network.d.ts +17 -0
- package/dist/particles/card-summary/card-network.js +25 -0
- package/dist/particles/card-summary/card-network.js.map +1 -0
- package/dist/particles/card-summary/card-summary.d.ts +83 -0
- package/dist/particles/card-summary/card-summary.js +188 -0
- package/dist/particles/card-summary/card-summary.js.map +1 -0
- package/dist/particles/footer-social/footer-social.d.ts +1 -1
- package/dist/particles/footer-social/footer-social.js +1 -1
- package/dist/particles/footer-social/footer-social.js.map +1 -1
- package/dist/particles/incentives/incentives.d.ts +1 -1
- package/dist/particles/incentives/incentives.js +1 -1
- package/dist/particles/incentives/incentives.js.map +1 -1
- package/dist/particles/payment-methods/payment-methods.d.ts +1 -1
- package/dist/particles/payment-methods/payment-methods.js +1 -1
- package/dist/particles/payment-methods/payment-methods.js.map +1 -1
- package/dist/particles/toast/toast.d.ts +20 -6
- package/dist/particles/toast/toast.js +49 -7
- package/dist/particles/toast/toast.js.map +1 -1
- package/dist/schema.graphql +298 -34
- package/dist/sdk/actions/index.d.ts +3 -1
- package/dist/sdk/actions/index.js +3 -1
- package/dist/sdk/actions/locale-action-provider.d.ts +16 -0
- package/dist/sdk/actions/locale-action-provider.js +20 -0
- package/dist/sdk/actions/locale-action-provider.js.map +1 -0
- package/dist/sdk/actions/saved-card-action-provider.d.ts +12 -0
- package/dist/sdk/actions/saved-card-action-provider.js +16 -0
- package/dist/sdk/actions/saved-card-action-provider.js.map +1 -0
- package/dist/sdk/actions/types.d.ts +6 -2
- package/dist/sdk/cache/storefront-cache.js +2 -1
- package/dist/sdk/cache/storefront-cache.js.map +1 -1
- package/dist/sdk/cart/operations.d.ts +2 -1
- package/dist/sdk/cart/operations.js +55 -1
- package/dist/sdk/cart/operations.js.map +1 -1
- package/dist/sdk/cart/optimistic.d.ts +30 -7
- package/dist/sdk/cart/optimistic.js +78 -41
- package/dist/sdk/cart/optimistic.js.map +1 -1
- package/dist/sdk/cart/use-cart-actions.d.ts +7 -0
- package/dist/sdk/cart/use-cart-actions.js +61 -15
- package/dist/sdk/cart/use-cart-actions.js.map +1 -1
- package/dist/sdk/cart/use-cart-sync-sales-channel.js +70 -0
- package/dist/sdk/cart/use-cart-sync-sales-channel.js.map +1 -0
- package/dist/sdk/generated/gql.js +15 -11
- package/dist/sdk/generated/gql.js.map +1 -1
- package/dist/sdk/generated/graphql.d.ts +177 -46
- package/dist/sdk/generated/graphql.js +2314 -93
- package/dist/sdk/generated/graphql.js.map +1 -1
- package/dist/sdk/i18n/messages/en-US.js +19 -0
- package/dist/sdk/i18n/messages/en-US.json +19 -0
- package/dist/sdk/i18n/messages/en-US.json.d.ts +19 -0
- package/dist/sdk/i18n/messages/es-ES.js +19 -0
- package/dist/sdk/i18n/messages/es-ES.json +19 -0
- package/dist/sdk/i18n/messages/pt-BR.js +19 -0
- package/dist/sdk/i18n/messages/pt-BR.json +19 -0
- package/dist/sdk/i18n/provider.js +1 -1
- package/dist/sdk/i18n/provider.js.map +1 -1
- package/dist/sdk/locale/binding-switch-provider.d.ts +13 -0
- package/dist/sdk/locale/binding-switch-provider.js +20 -0
- package/dist/sdk/locale/binding-switch-provider.js.map +1 -0
- package/dist/sdk/locale/use-locale-actions.d.ts +16 -0
- package/dist/sdk/locale/use-locale-actions.js +70 -0
- package/dist/sdk/locale/use-locale-actions.js.map +1 -0
- package/dist/sdk/navigation/binding-switch.d.ts +39 -0
- package/dist/sdk/navigation/binding-switch.js +129 -0
- package/dist/sdk/navigation/binding-switch.js.map +1 -0
- package/dist/sdk/navigation/decode-route-params.d.ts +11 -0
- package/dist/sdk/navigation/decode-route-params.js +32 -0
- package/dist/sdk/navigation/decode-route-params.js.map +1 -0
- package/dist/sdk/saved-cards/operations.d.ts +16 -0
- package/dist/sdk/saved-cards/operations.js +41 -0
- package/dist/sdk/saved-cards/operations.js.map +1 -0
- package/dist/sdk/saved-cards/use-saved-card-actions.d.ts +26 -0
- package/dist/sdk/saved-cards/use-saved-card-actions.js +86 -0
- package/dist/sdk/saved-cards/use-saved-card-actions.js.map +1 -0
- package/dist/sdk/seo/page-alternates.d.ts +5 -3
- package/dist/sdk/seo/page-alternates.js +7 -12
- package/dist/sdk/seo/page-alternates.js.map +1 -1
- package/dist/sdk/shipping/shipping-operations.d.ts +1 -1
- package/dist/sdk/shipping/shipping-operations.js +13 -0
- package/dist/sdk/shipping/shipping-operations.js.map +1 -1
- package/dist/sdk/state/cart.d.ts +9 -2
- package/dist/sdk/state/cart.js +9 -2
- package/dist/sdk/state/cart.js.map +1 -1
- package/package.json +2 -2
- package/src/styles/tailwind.css +1 -2
|
@@ -127,6 +127,7 @@ export type ProductShelfRootProps = ComponentPropsWithRef<'section'> & Partial<P
|
|
|
127
127
|
$content?: ContentValue<typeof ProductShelfContent>;
|
|
128
128
|
$data?: ProductShelfData | null;
|
|
129
129
|
};
|
|
130
|
+
export type ProductShelfOnMountRootProps = Omit<ProductShelfRootProps, '$content'>;
|
|
130
131
|
export type ProductShelfItemsProps = ComponentPropsWithRef<'div'>;
|
|
131
132
|
export type ProductShelfHeadingProps = ComponentPropsWithRef<'h2'>;
|
|
132
133
|
export declare function useProductShelf(): ProductShelfContextValue;
|
|
@@ -137,12 +138,18 @@ export declare const PRODUCT_SHELF_SORT_MAP: Record<ProductShelfSort, {
|
|
|
137
138
|
}>;
|
|
138
139
|
export declare function transformProducts(data: ProductShelfData): ProductShelfItem[];
|
|
139
140
|
export declare const ProductShelfRootDefinition: ComponentDefinition<ProductShelfRootProps>;
|
|
141
|
+
export declare const ProductShelfOnMountRootDefinition: ComponentDefinition<ProductShelfOnMountRootProps>;
|
|
140
142
|
export declare const ProductShelfItemsDefinition: ComponentDefinition<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>>;
|
|
141
143
|
export declare const ProductShelfHeadingDefinition: ComponentDefinition<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>>;
|
|
142
144
|
export declare function ProductShelfRoot({ first, itemsPerPage, sort, searchTerm, categoryId, brandId, collectionId, availableOnly, usePriceWithTaxes, taxesLabel, products, $content, $data: $dataProp, className, children, ...props }: ProductShelfRootProps): import("react/jsx-runtime").JSX.Element | null;
|
|
143
145
|
export declare namespace ProductShelfRoot {
|
|
144
146
|
var Skeleton: typeof ProductShelfSkeleton;
|
|
145
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* `ProductShelfRoot` for a `'lazy-on-mount'` page element: the generator wraps it in a
|
|
150
|
+
* `HotDataProvider` whose `null` fallback reaches the inner root through the data override.
|
|
151
|
+
*/
|
|
152
|
+
export declare function ProductShelfOnMountRoot(props: Readonly<ProductShelfOnMountRootProps>): import("react/jsx-runtime").JSX.Element;
|
|
146
153
|
export declare function ProductShelfHeading({ className, children, ...props }: ProductShelfHeadingProps): import("react/jsx-runtime").JSX.Element | null;
|
|
147
154
|
export declare function ProductShelfItems({ children, ...props }: ProductShelfItemsProps): import("react/jsx-runtime").JSX.Element;
|
|
148
155
|
export declare function ProductShelfSkeleton({ className, ...props }: ComponentPropsWithRef<'div'>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -325,6 +325,113 @@ const ProductShelfRootDefinition = defineComponent({
|
|
|
325
325
|
},
|
|
326
326
|
guideline: "Data-owning root of a facet-sourced product shelf. Owns a products(first:, orderBy:, filter:) fragment whose variables bind to this element: the merchant fills them in the CMS (content model product-shelf) and the props of the same name are the defaults. Receives $data lazily. Compose as: ProductShelfRoot > (ProductShelfHeading + CarouselRoot > (ProductShelfItems + CarouselPrevious + CarouselNext + CarouselBullets)). Compose ProductShelfHeading with the title as its children to render a heading; set the source with categoryId / brandId / collectionId / searchTerm and tune first, sort, availableOnly. Renders a skeleton while loading and nothing when the result is empty. Bind itemsPerPage to the CarouselRoot so paging matches the shelf config. See product-shelf.md."
|
|
327
327
|
});
|
|
328
|
+
const ProductShelfOnMountRootDefinition = defineComponent({
|
|
329
|
+
name: "ProductShelfOnMountRoot",
|
|
330
|
+
importPath: "@vtex/faststore/components",
|
|
331
|
+
component: ProductShelfOnMountRoot,
|
|
332
|
+
fragment,
|
|
333
|
+
fetchStrategy: "lazy-on-mount",
|
|
334
|
+
variables: { orderBy: {
|
|
335
|
+
from: "sort",
|
|
336
|
+
values: {
|
|
337
|
+
relevance: {
|
|
338
|
+
field: "RELEVANCE",
|
|
339
|
+
direction: "DESC"
|
|
340
|
+
},
|
|
341
|
+
price_asc: {
|
|
342
|
+
field: "PRICE",
|
|
343
|
+
direction: "ASC"
|
|
344
|
+
},
|
|
345
|
+
price_desc: {
|
|
346
|
+
field: "PRICE",
|
|
347
|
+
direction: "DESC"
|
|
348
|
+
},
|
|
349
|
+
name_asc: {
|
|
350
|
+
field: "TITLE",
|
|
351
|
+
direction: "ASC"
|
|
352
|
+
},
|
|
353
|
+
name_desc: {
|
|
354
|
+
field: "TITLE",
|
|
355
|
+
direction: "DESC"
|
|
356
|
+
},
|
|
357
|
+
release: {
|
|
358
|
+
field: "RELEASE_DATE",
|
|
359
|
+
direction: "DESC"
|
|
360
|
+
},
|
|
361
|
+
orders: {
|
|
362
|
+
field: "BEST_SELLING",
|
|
363
|
+
direction: "DESC"
|
|
364
|
+
},
|
|
365
|
+
discount: {
|
|
366
|
+
field: "RELEVANCE",
|
|
367
|
+
direction: "DESC"
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
} },
|
|
371
|
+
editor: {
|
|
372
|
+
label: "Product Shelf Root (on mount)",
|
|
373
|
+
category: "commerce"
|
|
374
|
+
},
|
|
375
|
+
providesContext: "product-shelf",
|
|
376
|
+
schema: {
|
|
377
|
+
itemsPerPage: {
|
|
378
|
+
type: "number",
|
|
379
|
+
default: 5,
|
|
380
|
+
label: "Items Per Page"
|
|
381
|
+
},
|
|
382
|
+
usePriceWithTaxes: {
|
|
383
|
+
type: "boolean",
|
|
384
|
+
default: false,
|
|
385
|
+
label: "Use Price With Taxes"
|
|
386
|
+
},
|
|
387
|
+
taxesLabel: {
|
|
388
|
+
type: "string",
|
|
389
|
+
default: "Tax included",
|
|
390
|
+
label: "Taxes Label"
|
|
391
|
+
},
|
|
392
|
+
first: {
|
|
393
|
+
type: "number",
|
|
394
|
+
default: 5,
|
|
395
|
+
label: "Number of Products"
|
|
396
|
+
},
|
|
397
|
+
sort: {
|
|
398
|
+
type: [
|
|
399
|
+
"relevance",
|
|
400
|
+
"price_asc",
|
|
401
|
+
"price_desc",
|
|
402
|
+
"name_asc",
|
|
403
|
+
"name_desc",
|
|
404
|
+
"release",
|
|
405
|
+
"orders",
|
|
406
|
+
"discount"
|
|
407
|
+
],
|
|
408
|
+
default: "relevance",
|
|
409
|
+
label: "Sort"
|
|
410
|
+
},
|
|
411
|
+
searchTerm: {
|
|
412
|
+
type: "string",
|
|
413
|
+
label: "Search Term"
|
|
414
|
+
},
|
|
415
|
+
categoryId: {
|
|
416
|
+
type: "string",
|
|
417
|
+
label: "Category ID"
|
|
418
|
+
},
|
|
419
|
+
brandId: {
|
|
420
|
+
type: "string",
|
|
421
|
+
label: "Brand ID"
|
|
422
|
+
},
|
|
423
|
+
collectionId: {
|
|
424
|
+
type: "string",
|
|
425
|
+
label: "Collection ID"
|
|
426
|
+
},
|
|
427
|
+
availableOnly: {
|
|
428
|
+
type: "boolean",
|
|
429
|
+
default: false,
|
|
430
|
+
label: "Available Only"
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
guideline: "ProductShelfRoot variant that fetches its products on the client when it mounts, in its own request: the products never enter the page query and are absent from the server HTML. Renders nothing while loading, when the request fails and when the result is empty (no skeleton). Meant for a conditional branch such as CartEmpty, never above the fold. Configured by props only (first, sort, availableOnly, categoryId, brandId, collectionId, searchTerm, itemsPerPage). Compose as: ProductShelfOnMountRoot > (ProductShelfHeading + CarouselRoot > (ProductShelfItems + CarouselPrevious + CarouselNext)). See product-shelf.md."
|
|
434
|
+
});
|
|
328
435
|
const ProductShelfItemsDefinition = defineComponent({
|
|
329
436
|
name: "ProductShelfItems",
|
|
330
437
|
importPath: "@vtex/faststore/components",
|
|
@@ -424,6 +531,13 @@ function ProductShelfRoot({ first = 5, itemsPerPage = 5, sort = "relevance", sea
|
|
|
424
531
|
})
|
|
425
532
|
});
|
|
426
533
|
}
|
|
534
|
+
/**
|
|
535
|
+
* `ProductShelfRoot` for a `'lazy-on-mount'` page element: the generator wraps it in a
|
|
536
|
+
* `HotDataProvider` whose `null` fallback reaches the inner root through the data override.
|
|
537
|
+
*/
|
|
538
|
+
function ProductShelfOnMountRoot(props) {
|
|
539
|
+
return /* @__PURE__ */ jsx(ProductShelfRoot, { ...props });
|
|
540
|
+
}
|
|
427
541
|
function ProductShelfHeading({ className, children, ...props }) {
|
|
428
542
|
const { headingId, registerHeading } = useProductShelfHeading();
|
|
429
543
|
const hasContent = children != null && children !== "";
|
|
@@ -473,6 +587,6 @@ const ProductShelf = {
|
|
|
473
587
|
Skeleton: ProductShelfSkeleton
|
|
474
588
|
};
|
|
475
589
|
//#endregion
|
|
476
|
-
export { PRODUCT_SHELF_SORT_MAP, ProductShelf, ProductShelfContent, ProductShelfHeading, ProductShelfHeadingDefinition, ProductShelfItems, ProductShelfItemsDefinition, ProductShelfRoot, ProductShelfRootDefinition, ProductShelfSkeleton, transformProducts, useProductShelf, useProductShelfItem };
|
|
590
|
+
export { PRODUCT_SHELF_SORT_MAP, ProductShelf, ProductShelfContent, ProductShelfHeading, ProductShelfHeadingDefinition, ProductShelfItems, ProductShelfItemsDefinition, ProductShelfOnMountRoot, ProductShelfOnMountRootDefinition, ProductShelfRoot, ProductShelfRootDefinition, ProductShelfSkeleton, transformProducts, useProductShelf, useProductShelfItem };
|
|
477
591
|
|
|
478
592
|
//# sourceMappingURL=product-shelf.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"product-shelf.js","names":[],"sources":["../../../src/matter/product-shelf/product-shelf.tsx"],"sourcesContent":["'use client';\n\nimport { type ContentValue, defineComponent, defineContentType } from '@vtex/fs-core';\nimport {\n type ComponentPropsWithRef,\n createContext,\n useCallback,\n useContext,\n useEffect,\n useId,\n useMemo,\n useState,\n} from 'react';\nimport { CarouselTrack } from '../../particles/carousel/carousel.js';\nimport { Text } from '../../particles/text/text.js';\nimport { useDataOverride } from '../../sdk/data/data-override-context.js';\nimport { isEmptyContentValue } from '../../sdk/data/empty-content-value.js';\nimport {\n ProductListConfigContext,\n type ProductListItem,\n ProductListItemContext,\n} from '../product-list/product-list.js';\n\n// ---------------------------------------------------------------------------\n// Content model — the merchant-facing fields that drive the query. Each one is\n// also a schema prop of the same name on the root: the CMS value wins per\n// field, and the prop is the developer default (the only source in a store\n// with no CMS binding). No field is required, so an empty value relaxes the\n// filter or takes the prop instead of omitting the shelf.\n// ---------------------------------------------------------------------------\n\nexport const ProductShelfContent = defineContentType({\n id: 'product-shelf',\n title: 'Product Shelf',\n description: 'Which products a shelf shows: how many, in which order, and from which facet.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n first: { type: 'integer', title: 'Number of products', minimum: 1, maximum: 100 },\n sort: {\n type: 'string',\n enum: ['relevance', 'price_asc', 'price_desc', 'name_asc', 'name_desc', 'release', 'orders', 'discount'],\n title: 'Sort',\n },\n categoryId: { type: 'string', title: 'Category ID' },\n brandId: { type: 'string', title: 'Brand ID' },\n collectionId: { type: 'string', title: 'Collection ID' },\n searchTerm: { type: 'string', title: 'Search term' },\n availableOnly: { type: 'boolean', title: 'Available only' },\n },\n },\n});\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment — product connection from the catalog module. Every\n// argument is a variable the compiler binds from the element's configuration\n// (its content slice at request time, else its prop at compile time), one\n// fragment instance per element. A variable left unbound inside `filter`\n// omits that key, so an unset facet relaxes the filter rather than sending\n// null; `$orderBy` reads `sort` through the `values` table in the definition.\n// ---------------------------------------------------------------------------\n\nconst fragment = /* GraphQL */ `\n fragment ProductShelfData on Query {\n products(\n first: $first\n orderBy: $orderBy\n filter: {\n categoryIds: $categoryId\n brandIds: $brandId\n collectionId: $collectionId\n searchTerm: $searchTerm\n availableOnly: $availableOnly\n }\n ) {\n edges {\n node {\n id\n title\n slug\n routeSlug\n images(first: 1) {\n edges {\n node {\n url\n altText\n }\n }\n }\n variants(first: 1) {\n edges {\n node {\n availableForSale\n quantityAvailable\n price {\n amount\n currencyCode\n }\n compareAtPrice {\n amount\n currencyCode\n }\n sellers {\n isDefault\n offer {\n price {\n amount\n currencyCode\n }\n listPrice {\n amount\n currencyCode\n }\n spotPrice {\n amount\n currencyCode\n }\n tax {\n amount\n currencyCode\n }\n taxPercentage\n availableQuantity\n }\n }\n }\n }\n }\n }\n }\n totalCount\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Raw data types (shape of the fragment result)\n// ---------------------------------------------------------------------------\n\ntype RawMoney = { amount: number; currencyCode: string };\n\ntype RawOffer = {\n price: RawMoney;\n listPrice: RawMoney;\n spotPrice: RawMoney;\n tax: RawMoney;\n taxPercentage: number | null;\n availableQuantity: number;\n};\n\ntype RawVariantNode = {\n availableForSale: boolean;\n quantityAvailable: number | null;\n price: RawMoney;\n compareAtPrice: RawMoney | null;\n sellers: Array<{ isDefault: boolean; offer: RawOffer | null }>;\n};\n\ntype RawProductNode = {\n id: string;\n title: string;\n slug: string;\n routeSlug: string;\n images: { edges: Array<{ node: { url: string; altText: string | null } }> };\n variants: { edges: Array<{ node: RawVariantNode }> };\n};\n\n// The `$data` the shelf receives is the unwrapped top-level fragment field:\n// HotDataProvider forwards `data['products']` (the connection value), not the\n// `{ products: ... }` wrapper — same contract as PricingRoot's productBySlug.\nexport type ProductShelfData = {\n edges: Array<{ node: RawProductNode }>;\n totalCount: number;\n};\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n// The per-item value object the card parts (T3–T7) read. It is the widened\n// ProductListItem, so the existing ProductList.Card* leaves render unchanged.\nexport type ProductShelfItem = ProductListItem;\n\nexport type ProductShelfSort =\n | 'relevance'\n | 'price_asc'\n | 'price_desc'\n | 'name_asc'\n | 'name_desc'\n | 'release'\n | 'orders'\n | 'discount';\n\nexport type ProductSortField = 'TITLE' | 'PRICE' | 'RELEASE_DATE' | 'RELEVANCE' | 'BEST_SELLING';\n\nexport type SortDirection = 'ASC' | 'DESC';\n\n/**\n * The query configuration the shelf resolved for this element: the content slice when the\n * merchant filled a field, else the prop. The query has already applied these values; the\n * context republishes them for parts that describe the shelf (a \"sorted by\" label, a facet link).\n */\nexport type ProductShelfConfig = {\n first: number;\n sort: ProductShelfSort;\n searchTerm?: string;\n categoryId?: string;\n brandId?: string;\n collectionId?: string;\n availableOnly: boolean;\n};\n\nexport type ProductShelfContextValue = {\n itemsPerPage: number;\n config: ProductShelfConfig;\n count: number;\n};\n\nexport type ProductShelfRootProps = ComponentPropsWithRef<'section'> &\n Partial<ProductShelfConfig> & {\n itemsPerPage?: number;\n /** When true, card prices render tax-inclusive (from each item's tax data). Default false. */\n usePriceWithTaxes?: boolean;\n /** Text for the tax label part when tax display is on. Default `'Tax included'`. */\n taxesLabel?: string;\n products?: ProductShelfItem[];\n /** Injected by the compiler, sliced from the page's or layout's composite content entry. */\n $content?: ContentValue<typeof ProductShelfContent>;\n $data?: ProductShelfData | null;\n };\n\nexport type ProductShelfItemsProps = ComponentPropsWithRef<'div'>;\n\nexport type ProductShelfHeadingProps = ComponentPropsWithRef<'h2'>;\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\nconst ProductShelfProductsContext = createContext<ProductShelfItem[]>([]);\n\nconst ProductShelfContext = createContext<ProductShelfContextValue | null>(null);\n\n// Wires the section ⇄ heading `aria-labelledby` link without the root having to\n// know its descendants: `headingId` is the shared id, and a composed\n// ProductShelfHeading registers its presence so the section points at it only\n// when it exists (never a dangling idref). `registerHeading` is ref-counted and\n// returns its unregister, so the heading can be placed anywhere.\ntype ProductShelfHeadingContextValue = { headingId: string; registerHeading: () => () => void };\n\nconst ProductShelfHeadingContext = createContext<ProductShelfHeadingContextValue | null>(null);\n\nexport function useProductShelf(): ProductShelfContextValue {\n const ctx = useContext(ProductShelfContext);\n if (!ctx) {\n throw new Error('useProductShelf must be used inside ProductShelfRoot.');\n }\n return ctx;\n}\n\nfunction useProductShelfHeading(): ProductShelfHeadingContextValue {\n const ctx = useContext(ProductShelfHeadingContext);\n if (!ctx) {\n throw new Error('ProductShelfHeading must be used inside ProductShelfRoot.');\n }\n return ctx;\n}\n\nexport function useProductShelfItem(): ProductShelfItem {\n const item = useContext(ProductListItemContext);\n if (!item) {\n throw new Error('Product card components must be used inside ProductShelfItems.');\n }\n return item;\n}\n\n// ---------------------------------------------------------------------------\n// `sort` → `$orderBy` (ProductSortingInput). The same table is the `values`\n// block of the definition, so the compiler and the SDK helper apply it; the\n// contract test pins the two to each other.\n// ---------------------------------------------------------------------------\n\nexport const PRODUCT_SHELF_SORT_MAP: Record<ProductShelfSort, { field: ProductSortField; direction: SortDirection }> = {\n relevance: { field: 'RELEVANCE', direction: 'DESC' },\n price_asc: { field: 'PRICE', direction: 'ASC' },\n price_desc: { field: 'PRICE', direction: 'DESC' },\n name_asc: { field: 'TITLE', direction: 'ASC' },\n name_desc: { field: 'TITLE', direction: 'DESC' },\n release: { field: 'RELEASE_DATE', direction: 'DESC' },\n orders: { field: 'BEST_SELLING', direction: 'DESC' },\n // No direct ProductSortField for legacy \"discount\"; fall back to relevance.\n discount: { field: 'RELEVANCE', direction: 'DESC' },\n};\n\n// ---------------------------------------------------------------------------\n// Transform — raw fragment slice → per-item value objects\n// ---------------------------------------------------------------------------\n\nexport function transformProducts(data: ProductShelfData): ProductShelfItem[] {\n return data.edges.map(({ node }): ProductShelfItem => {\n const image = node.images.edges[0]?.node;\n const variant = node.variants.edges[0]?.node;\n const seller = variant?.sellers.find((s) => s.isDefault) ?? variant?.sellers[0];\n const offer = seller?.offer ?? null;\n\n const price = offer?.price.amount ?? variant?.price.amount ?? 0;\n const listPrice = offer?.listPrice.amount ?? variant?.compareAtPrice?.amount;\n const spotPrice = offer?.spotPrice.amount;\n const currency = offer?.price.currencyCode ?? variant?.price.currencyCode;\n\n return {\n id: node.id,\n name: node.title,\n slug: node.slug,\n routeSlug: node.routeSlug,\n image: { src: image?.url ?? '', alt: image?.altText ?? node.title },\n price,\n ...(listPrice !== undefined ? { listPrice } : {}),\n ...(spotPrice !== undefined ? { spotPrice } : {}),\n ...(offer ? { tax: offer.tax.amount, taxPercentage: offer.taxPercentage } : {}),\n ...(variant ? { availableForSale: variant.availableForSale } : {}),\n ...(variant?.quantityAvailable != null ? { availableQuantity: variant.quantityAvailable } : {}),\n ...(currency ? { currency } : {}),\n };\n });\n}\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const ProductShelfRootDefinition = defineComponent<ProductShelfRootProps>({\n name: 'ProductShelfRoot',\n importPath: '@vtex/faststore/components',\n component: ProductShelfRoot,\n fragment,\n fetchStrategy: 'lazy',\n skeleton: ProductShelfSkeleton,\n content: ProductShelfContent,\n // The `values` table is authored as a literal: the Catalog Build serializes the block as JSON and\n // rejects identifiers. `PRODUCT_SHELF_SORT_MAP` is pinned equal to it by the contract test.\n variables: {\n orderBy: {\n from: 'sort',\n values: {\n relevance: { field: 'RELEVANCE', direction: 'DESC' },\n price_asc: { field: 'PRICE', direction: 'ASC' },\n price_desc: { field: 'PRICE', direction: 'DESC' },\n name_asc: { field: 'TITLE', direction: 'ASC' },\n name_desc: { field: 'TITLE', direction: 'DESC' },\n release: { field: 'RELEASE_DATE', direction: 'DESC' },\n orders: { field: 'BEST_SELLING', direction: 'DESC' },\n discount: { field: 'RELEVANCE', direction: 'DESC' },\n },\n },\n },\n editor: { label: 'Product Shelf Root', category: 'commerce' },\n providesContext: 'product-shelf',\n schema: {\n itemsPerPage: { type: 'number', default: 5, label: 'Items Per Page' },\n usePriceWithTaxes: { type: 'boolean', default: false, label: 'Use Price With Taxes' },\n taxesLabel: { type: 'string', default: 'Tax included', label: 'Taxes Label' },\n // The content fields again, as props: the developer's defaults, and the only source in a\n // store with no CMS binding (example stores, Storybook, tests).\n first: { type: 'number', default: 5, label: 'Number of Products' },\n sort: {\n type: ['relevance', 'price_asc', 'price_desc', 'name_asc', 'name_desc', 'release', 'orders', 'discount'] as const,\n default: 'relevance',\n label: 'Sort',\n },\n searchTerm: { type: 'string', label: 'Search Term' },\n categoryId: { type: 'string', label: 'Category ID' },\n brandId: { type: 'string', label: 'Brand ID' },\n collectionId: { type: 'string', label: 'Collection ID' },\n availableOnly: { type: 'boolean', default: false, label: 'Available Only' },\n },\n guideline:\n 'Data-owning root of a facet-sourced product shelf. Owns a products(first:, orderBy:, filter:) fragment whose variables bind to this element: the merchant fills them in the CMS (content model product-shelf) and the props of the same name are the defaults. Receives $data lazily. Compose as: ProductShelfRoot > (ProductShelfHeading + CarouselRoot > (ProductShelfItems + CarouselPrevious + CarouselNext + CarouselBullets)). Compose ProductShelfHeading with the title as its children to render a heading; set the source with categoryId / brandId / collectionId / searchTerm and tune first, sort, availableOnly. Renders a skeleton while loading and nothing when the result is empty. Bind itemsPerPage to the CarouselRoot so paging matches the shelf config. See product-shelf.md.',\n});\n\nexport const ProductShelfItemsDefinition = defineComponent<ProductShelfItemsProps>({\n name: 'ProductShelfItems',\n importPath: '@vtex/faststore/components',\n component: ProductShelfItems,\n editor: { label: 'Product Shelf Items', category: 'commerce' },\n requiresContext: 'product-shelf',\n schema: {},\n guideline:\n 'Repeats the shelf products into the carousel track, publishing one product per slide through the per-item context the ProductCard* parts read. Place directly inside CarouselRoot (it renders the CarouselTrack). Put the card template (ProductCardImage, ProductCardName, ProductCardPrice, …) as its children.',\n});\n\nexport const ProductShelfHeadingDefinition = defineComponent<ProductShelfHeadingProps>({\n name: 'ProductShelfHeading',\n importPath: '@vtex/faststore/components',\n component: ProductShelfHeading,\n editor: { label: 'Product Shelf Heading', category: 'commerce' },\n requiresContext: 'product-shelf',\n schema: {},\n guideline:\n \"Renders its children as the shelf title (an <h2>) and provides the section's accessible label. Author the title text as its children (props.children). Place it inside ProductShelfRoot where the title should sit — usually above CarouselRoot. Renders nothing when it has no children.\",\n});\n\n// ---------------------------------------------------------------------------\n// Implementations\n// ---------------------------------------------------------------------------\n\n/** The content value when the merchant filled the field, else the prop (the shared emptiness rule). */\nfunction contentOrProp<T>(content: T | undefined, prop: T): T {\n if (content === undefined || isEmptyContentValue(content)) return prop;\n return content;\n}\n\nexport function ProductShelfRoot({\n first = 5,\n itemsPerPage = 5,\n sort = 'relevance',\n searchTerm,\n categoryId,\n brandId,\n collectionId,\n availableOnly = false,\n usePriceWithTaxes = false,\n taxesLabel = 'Tax included',\n products,\n $content,\n $data: $dataProp,\n className,\n children,\n ...props\n}: ProductShelfRootProps) {\n const $data = useDataOverride<ProductShelfData | null | undefined>($dataProp);\n // The query already applied these values (the compiler binds the same slice and props to the\n // fragment variables); the context only republishes what this element resolved to.\n const shelfConfig = useMemo<ProductShelfConfig>(\n () => ({\n first: contentOrProp($content?.first, first),\n sort: contentOrProp($content?.sort, sort),\n searchTerm: contentOrProp($content?.searchTerm, searchTerm),\n categoryId: contentOrProp($content?.categoryId, categoryId),\n brandId: contentOrProp($content?.brandId, brandId),\n collectionId: contentOrProp($content?.collectionId, collectionId),\n availableOnly: contentOrProp($content?.availableOnly, availableOnly),\n }),\n [$content, first, sort, searchTerm, categoryId, brandId, collectionId, availableOnly],\n );\n const headingId = useId();\n // Ref-counted so a composed ProductShelfHeading flips `hasHeading` wherever it is placed; the\n // section then labels itself only while a heading is mounted, never via a dangling idref.\n const [headingCount, setHeadingCount] = useState(0);\n const registerHeading = useCallback(() => {\n setHeadingCount((count) => count + 1);\n return () => setHeadingCount((count) => count - 1);\n }, []);\n const headingContext = useMemo(() => ({ headingId, registerHeading }), [headingId, registerHeading]);\n const config = useMemo(() => ({ usePriceWithTaxes, taxesLabel }), [usePriceWithTaxes, taxesLabel]);\n const items = useMemo<ProductShelfItem[]>(() => {\n if (products !== undefined) return products;\n return $data ? transformProducts($data) : [];\n }, [products, $data]);\n const shelf = useMemo<ProductShelfContextValue>(\n () => ({ itemsPerPage, config: shelfConfig, count: items.length }),\n [itemsPerPage, shelfConfig, items.length],\n );\n const hasHeading = headingCount > 0;\n\n if (products === undefined && $data === undefined) {\n // Lazy source not resolved yet — show the skeleton (the runtime also renders it).\n return <ProductShelfSkeleton className={className} />;\n }\n\n // Empty result renders nothing (legacy parity) rather than an empty carousel.\n if (items.length === 0) {\n return null;\n }\n\n return (\n <ProductListConfigContext value={config}>\n <ProductShelfProductsContext value={items}>\n <ProductShelfContext value={shelf}>\n <ProductShelfHeadingContext value={headingContext}>\n <section\n {...props}\n className={className}\n data-fs-product-shelf=\"\"\n aria-labelledby={hasHeading ? headingId : undefined}\n >\n {children}\n </section>\n </ProductShelfHeadingContext>\n </ProductShelfContext>\n </ProductShelfProductsContext>\n </ProductListConfigContext>\n );\n}\n\nexport function ProductShelfHeading({ className, children, ...props }: ProductShelfHeadingProps) {\n const { headingId, registerHeading } = useProductShelfHeading();\n const hasContent = children != null && children !== '';\n\n // Announce presence to the root so its section wires `aria-labelledby` to this heading; the\n // registration (and its unregister) is scoped to when the heading actually renders content.\n useEffect(() => {\n if (!hasContent) {\n return;\n }\n return registerHeading();\n }, [hasContent, registerHeading]);\n\n if (!hasContent) {\n return null;\n }\n\n return (\n <Text {...props} as=\"h2\" id={headingId} className={className} data-fs-product-shelf-heading=\"\">\n {children}\n </Text>\n );\n}\n\nexport function ProductShelfItems({ children, ...props }: ProductShelfItemsProps) {\n const items = useContext(ProductShelfProductsContext);\n\n return (\n <CarouselTrack {...props} data-fs-product-shelf-items=\"\">\n {items.map((item) => (\n <ProductListItemContext key={item.id} value={item}>\n {children}\n </ProductListItemContext>\n ))}\n </CarouselTrack>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Skeleton\n// ---------------------------------------------------------------------------\n\nexport function ProductShelfSkeleton({ className, ...props }: ComponentPropsWithRef<'div'>) {\n return (\n <div {...props} className={className} data-fs-product-shelf-skeleton=\"\" aria-hidden=\"true\">\n <span data-fs-product-shelf-skeleton-item=\"\" />\n <span data-fs-product-shelf-skeleton-item=\"\" />\n <span data-fs-product-shelf-skeleton-item=\"\" />\n </div>\n );\n}\n\nProductShelfRoot.Skeleton = ProductShelfSkeleton;\n\n// ---------------------------------------------------------------------------\n// Namespace export\n// ---------------------------------------------------------------------------\n\nexport const ProductShelf = {\n Root: ProductShelfRoot,\n Heading: ProductShelfHeading,\n Items: ProductShelfItems,\n Skeleton: ProductShelfSkeleton,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"product-shelf.js","names":[],"sources":["../../../src/matter/product-shelf/product-shelf.tsx"],"sourcesContent":["'use client';\n\nimport { type ContentValue, defineComponent, defineContentType } from '@vtex/fs-core';\nimport {\n type ComponentPropsWithRef,\n createContext,\n useCallback,\n useContext,\n useEffect,\n useId,\n useMemo,\n useState,\n} from 'react';\nimport { CarouselTrack } from '../../particles/carousel/carousel.js';\nimport { Text } from '../../particles/text/text.js';\nimport { useDataOverride } from '../../sdk/data/data-override-context.js';\nimport { isEmptyContentValue } from '../../sdk/data/empty-content-value.js';\nimport {\n ProductListConfigContext,\n type ProductListItem,\n ProductListItemContext,\n} from '../product-list/product-list.js';\n\n// ---------------------------------------------------------------------------\n// Content model — the merchant-facing fields that drive the query. Each one is\n// also a schema prop of the same name on the root: the CMS value wins per\n// field, and the prop is the developer default (the only source in a store\n// with no CMS binding). No field is required, so an empty value relaxes the\n// filter or takes the prop instead of omitting the shelf.\n// ---------------------------------------------------------------------------\n\nexport const ProductShelfContent = defineContentType({\n id: 'product-shelf',\n title: 'Product Shelf',\n description: 'Which products a shelf shows: how many, in which order, and from which facet.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n first: { type: 'integer', title: 'Number of products', minimum: 1, maximum: 100 },\n sort: {\n type: 'string',\n enum: ['relevance', 'price_asc', 'price_desc', 'name_asc', 'name_desc', 'release', 'orders', 'discount'],\n title: 'Sort',\n },\n categoryId: { type: 'string', title: 'Category ID' },\n brandId: { type: 'string', title: 'Brand ID' },\n collectionId: { type: 'string', title: 'Collection ID' },\n searchTerm: { type: 'string', title: 'Search term' },\n availableOnly: { type: 'boolean', title: 'Available only' },\n },\n },\n});\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment — product connection from the catalog module. Every\n// argument is a variable the compiler binds from the element's configuration\n// (its content slice at request time, else its prop at compile time), one\n// fragment instance per element. A variable left unbound inside `filter`\n// omits that key, so an unset facet relaxes the filter rather than sending\n// null; `$orderBy` reads `sort` through the `values` table in the definition.\n// ---------------------------------------------------------------------------\n\nconst fragment = /* GraphQL */ `\n fragment ProductShelfData on Query {\n products(\n first: $first\n orderBy: $orderBy\n filter: {\n categoryIds: $categoryId\n brandIds: $brandId\n collectionId: $collectionId\n searchTerm: $searchTerm\n availableOnly: $availableOnly\n }\n ) {\n edges {\n node {\n id\n title\n slug\n routeSlug\n images(first: 1) {\n edges {\n node {\n url\n altText\n }\n }\n }\n variants(first: 1) {\n edges {\n node {\n availableForSale\n quantityAvailable\n price {\n amount\n currencyCode\n }\n compareAtPrice {\n amount\n currencyCode\n }\n sellers {\n isDefault\n offer {\n price {\n amount\n currencyCode\n }\n listPrice {\n amount\n currencyCode\n }\n spotPrice {\n amount\n currencyCode\n }\n tax {\n amount\n currencyCode\n }\n taxPercentage\n availableQuantity\n }\n }\n }\n }\n }\n }\n }\n totalCount\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Raw data types (shape of the fragment result)\n// ---------------------------------------------------------------------------\n\ntype RawMoney = { amount: number; currencyCode: string };\n\ntype RawOffer = {\n price: RawMoney;\n listPrice: RawMoney;\n spotPrice: RawMoney;\n tax: RawMoney;\n taxPercentage: number | null;\n availableQuantity: number;\n};\n\ntype RawVariantNode = {\n availableForSale: boolean;\n quantityAvailable: number | null;\n price: RawMoney;\n compareAtPrice: RawMoney | null;\n sellers: Array<{ isDefault: boolean; offer: RawOffer | null }>;\n};\n\ntype RawProductNode = {\n id: string;\n title: string;\n slug: string;\n routeSlug: string;\n images: { edges: Array<{ node: { url: string; altText: string | null } }> };\n variants: { edges: Array<{ node: RawVariantNode }> };\n};\n\n// The `$data` the shelf receives is the unwrapped top-level fragment field:\n// HotDataProvider forwards `data['products']` (the connection value), not the\n// `{ products: ... }` wrapper — same contract as PricingRoot's productBySlug.\nexport type ProductShelfData = {\n edges: Array<{ node: RawProductNode }>;\n totalCount: number;\n};\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n// The per-item value object the card parts (T3–T7) read. It is the widened\n// ProductListItem, so the existing ProductList.Card* leaves render unchanged.\nexport type ProductShelfItem = ProductListItem;\n\nexport type ProductShelfSort =\n | 'relevance'\n | 'price_asc'\n | 'price_desc'\n | 'name_asc'\n | 'name_desc'\n | 'release'\n | 'orders'\n | 'discount';\n\nexport type ProductSortField = 'TITLE' | 'PRICE' | 'RELEASE_DATE' | 'RELEVANCE' | 'BEST_SELLING';\n\nexport type SortDirection = 'ASC' | 'DESC';\n\n/**\n * The query configuration the shelf resolved for this element: the content slice when the\n * merchant filled a field, else the prop. The query has already applied these values; the\n * context republishes them for parts that describe the shelf (a \"sorted by\" label, a facet link).\n */\nexport type ProductShelfConfig = {\n first: number;\n sort: ProductShelfSort;\n searchTerm?: string;\n categoryId?: string;\n brandId?: string;\n collectionId?: string;\n availableOnly: boolean;\n};\n\nexport type ProductShelfContextValue = {\n itemsPerPage: number;\n config: ProductShelfConfig;\n count: number;\n};\n\nexport type ProductShelfRootProps = ComponentPropsWithRef<'section'> &\n Partial<ProductShelfConfig> & {\n itemsPerPage?: number;\n /** When true, card prices render tax-inclusive (from each item's tax data). Default false. */\n usePriceWithTaxes?: boolean;\n /** Text for the tax label part when tax display is on. Default `'Tax included'`. */\n taxesLabel?: string;\n products?: ProductShelfItem[];\n /** Injected by the compiler, sliced from the page's or layout's composite content entry. */\n $content?: ContentValue<typeof ProductShelfContent>;\n $data?: ProductShelfData | null;\n };\n\nexport type ProductShelfOnMountRootProps = Omit<ProductShelfRootProps, '$content'>;\n\nexport type ProductShelfItemsProps = ComponentPropsWithRef<'div'>;\n\nexport type ProductShelfHeadingProps = ComponentPropsWithRef<'h2'>;\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\nconst ProductShelfProductsContext = createContext<ProductShelfItem[]>([]);\n\nconst ProductShelfContext = createContext<ProductShelfContextValue | null>(null);\n\n// Wires the section ⇄ heading `aria-labelledby` link without the root having to\n// know its descendants: `headingId` is the shared id, and a composed\n// ProductShelfHeading registers its presence so the section points at it only\n// when it exists (never a dangling idref). `registerHeading` is ref-counted and\n// returns its unregister, so the heading can be placed anywhere.\ntype ProductShelfHeadingContextValue = { headingId: string; registerHeading: () => () => void };\n\nconst ProductShelfHeadingContext = createContext<ProductShelfHeadingContextValue | null>(null);\n\nexport function useProductShelf(): ProductShelfContextValue {\n const ctx = useContext(ProductShelfContext);\n if (!ctx) {\n throw new Error('useProductShelf must be used inside ProductShelfRoot.');\n }\n return ctx;\n}\n\nfunction useProductShelfHeading(): ProductShelfHeadingContextValue {\n const ctx = useContext(ProductShelfHeadingContext);\n if (!ctx) {\n throw new Error('ProductShelfHeading must be used inside ProductShelfRoot.');\n }\n return ctx;\n}\n\nexport function useProductShelfItem(): ProductShelfItem {\n const item = useContext(ProductListItemContext);\n if (!item) {\n throw new Error('Product card components must be used inside ProductShelfItems.');\n }\n return item;\n}\n\n// ---------------------------------------------------------------------------\n// `sort` → `$orderBy` (ProductSortingInput). The same table is the `values`\n// block of the definition, so the compiler and the SDK helper apply it; the\n// contract test pins the two to each other.\n// ---------------------------------------------------------------------------\n\nexport const PRODUCT_SHELF_SORT_MAP: Record<ProductShelfSort, { field: ProductSortField; direction: SortDirection }> = {\n relevance: { field: 'RELEVANCE', direction: 'DESC' },\n price_asc: { field: 'PRICE', direction: 'ASC' },\n price_desc: { field: 'PRICE', direction: 'DESC' },\n name_asc: { field: 'TITLE', direction: 'ASC' },\n name_desc: { field: 'TITLE', direction: 'DESC' },\n release: { field: 'RELEASE_DATE', direction: 'DESC' },\n orders: { field: 'BEST_SELLING', direction: 'DESC' },\n // No direct ProductSortField for legacy \"discount\"; fall back to relevance.\n discount: { field: 'RELEVANCE', direction: 'DESC' },\n};\n\n// ---------------------------------------------------------------------------\n// Transform — raw fragment slice → per-item value objects\n// ---------------------------------------------------------------------------\n\nexport function transformProducts(data: ProductShelfData): ProductShelfItem[] {\n return data.edges.map(({ node }): ProductShelfItem => {\n const image = node.images.edges[0]?.node;\n const variant = node.variants.edges[0]?.node;\n const seller = variant?.sellers.find((s) => s.isDefault) ?? variant?.sellers[0];\n const offer = seller?.offer ?? null;\n\n const price = offer?.price.amount ?? variant?.price.amount ?? 0;\n const listPrice = offer?.listPrice.amount ?? variant?.compareAtPrice?.amount;\n const spotPrice = offer?.spotPrice.amount;\n const currency = offer?.price.currencyCode ?? variant?.price.currencyCode;\n\n return {\n id: node.id,\n name: node.title,\n slug: node.slug,\n routeSlug: node.routeSlug,\n image: { src: image?.url ?? '', alt: image?.altText ?? node.title },\n price,\n ...(listPrice !== undefined ? { listPrice } : {}),\n ...(spotPrice !== undefined ? { spotPrice } : {}),\n ...(offer ? { tax: offer.tax.amount, taxPercentage: offer.taxPercentage } : {}),\n ...(variant ? { availableForSale: variant.availableForSale } : {}),\n ...(variant?.quantityAvailable != null ? { availableQuantity: variant.quantityAvailable } : {}),\n ...(currency ? { currency } : {}),\n };\n });\n}\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const ProductShelfRootDefinition = defineComponent<ProductShelfRootProps>({\n name: 'ProductShelfRoot',\n importPath: '@vtex/faststore/components',\n component: ProductShelfRoot,\n fragment,\n fetchStrategy: 'lazy',\n skeleton: ProductShelfSkeleton,\n content: ProductShelfContent,\n // The `values` table is authored as a literal: the Catalog Build serializes the block as JSON and\n // rejects identifiers. `PRODUCT_SHELF_SORT_MAP` is pinned equal to it by the contract test.\n variables: {\n orderBy: {\n from: 'sort',\n values: {\n relevance: { field: 'RELEVANCE', direction: 'DESC' },\n price_asc: { field: 'PRICE', direction: 'ASC' },\n price_desc: { field: 'PRICE', direction: 'DESC' },\n name_asc: { field: 'TITLE', direction: 'ASC' },\n name_desc: { field: 'TITLE', direction: 'DESC' },\n release: { field: 'RELEASE_DATE', direction: 'DESC' },\n orders: { field: 'BEST_SELLING', direction: 'DESC' },\n discount: { field: 'RELEVANCE', direction: 'DESC' },\n },\n },\n },\n editor: { label: 'Product Shelf Root', category: 'commerce' },\n providesContext: 'product-shelf',\n schema: {\n itemsPerPage: { type: 'number', default: 5, label: 'Items Per Page' },\n usePriceWithTaxes: { type: 'boolean', default: false, label: 'Use Price With Taxes' },\n taxesLabel: { type: 'string', default: 'Tax included', label: 'Taxes Label' },\n // The content fields again, as props: the developer's defaults, and the only source in a\n // store with no CMS binding (example stores, Storybook, tests).\n first: { type: 'number', default: 5, label: 'Number of Products' },\n sort: {\n type: ['relevance', 'price_asc', 'price_desc', 'name_asc', 'name_desc', 'release', 'orders', 'discount'] as const,\n default: 'relevance',\n label: 'Sort',\n },\n searchTerm: { type: 'string', label: 'Search Term' },\n categoryId: { type: 'string', label: 'Category ID' },\n brandId: { type: 'string', label: 'Brand ID' },\n collectionId: { type: 'string', label: 'Collection ID' },\n availableOnly: { type: 'boolean', default: false, label: 'Available Only' },\n },\n guideline:\n 'Data-owning root of a facet-sourced product shelf. Owns a products(first:, orderBy:, filter:) fragment whose variables bind to this element: the merchant fills them in the CMS (content model product-shelf) and the props of the same name are the defaults. Receives $data lazily. Compose as: ProductShelfRoot > (ProductShelfHeading + CarouselRoot > (ProductShelfItems + CarouselPrevious + CarouselNext + CarouselBullets)). Compose ProductShelfHeading with the title as its children to render a heading; set the source with categoryId / brandId / collectionId / searchTerm and tune first, sort, availableOnly. Renders a skeleton while loading and nothing when the result is empty. Bind itemsPerPage to the CarouselRoot so paging matches the shelf config. See product-shelf.md.',\n});\n\nexport const ProductShelfOnMountRootDefinition = defineComponent<ProductShelfOnMountRootProps>({\n name: 'ProductShelfOnMountRoot',\n importPath: '@vtex/faststore/components',\n component: ProductShelfOnMountRoot,\n fragment,\n fetchStrategy: 'lazy-on-mount',\n // Repeats the ProductShelfRoot table as a literal (the Catalog Build rejects identifiers inside\n // `variables`); the contract test pins it equal to ProductShelfRootDefinition's.\n variables: {\n orderBy: {\n from: 'sort',\n values: {\n relevance: { field: 'RELEVANCE', direction: 'DESC' },\n price_asc: { field: 'PRICE', direction: 'ASC' },\n price_desc: { field: 'PRICE', direction: 'DESC' },\n name_asc: { field: 'TITLE', direction: 'ASC' },\n name_desc: { field: 'TITLE', direction: 'DESC' },\n release: { field: 'RELEASE_DATE', direction: 'DESC' },\n orders: { field: 'BEST_SELLING', direction: 'DESC' },\n discount: { field: 'RELEVANCE', direction: 'DESC' },\n },\n },\n },\n editor: { label: 'Product Shelf Root (on mount)', category: 'commerce' },\n providesContext: 'product-shelf',\n schema: {\n itemsPerPage: { type: 'number', default: 5, label: 'Items Per Page' },\n usePriceWithTaxes: { type: 'boolean', default: false, label: 'Use Price With Taxes' },\n taxesLabel: { type: 'string', default: 'Tax included', label: 'Taxes Label' },\n first: { type: 'number', default: 5, label: 'Number of Products' },\n sort: {\n type: ['relevance', 'price_asc', 'price_desc', 'name_asc', 'name_desc', 'release', 'orders', 'discount'] as const,\n default: 'relevance',\n label: 'Sort',\n },\n searchTerm: { type: 'string', label: 'Search Term' },\n categoryId: { type: 'string', label: 'Category ID' },\n brandId: { type: 'string', label: 'Brand ID' },\n collectionId: { type: 'string', label: 'Collection ID' },\n availableOnly: { type: 'boolean', default: false, label: 'Available Only' },\n },\n guideline:\n 'ProductShelfRoot variant that fetches its products on the client when it mounts, in its own request: the products never enter the page query and are absent from the server HTML. Renders nothing while loading, when the request fails and when the result is empty (no skeleton). Meant for a conditional branch such as CartEmpty, never above the fold. Configured by props only (first, sort, availableOnly, categoryId, brandId, collectionId, searchTerm, itemsPerPage). Compose as: ProductShelfOnMountRoot > (ProductShelfHeading + CarouselRoot > (ProductShelfItems + CarouselPrevious + CarouselNext)). See product-shelf.md.',\n});\n\nexport const ProductShelfItemsDefinition = defineComponent<ProductShelfItemsProps>({\n name: 'ProductShelfItems',\n importPath: '@vtex/faststore/components',\n component: ProductShelfItems,\n editor: { label: 'Product Shelf Items', category: 'commerce' },\n requiresContext: 'product-shelf',\n schema: {},\n guideline:\n 'Repeats the shelf products into the carousel track, publishing one product per slide through the per-item context the ProductCard* parts read. Place directly inside CarouselRoot (it renders the CarouselTrack). Put the card template (ProductCardImage, ProductCardName, ProductCardPrice, …) as its children.',\n});\n\nexport const ProductShelfHeadingDefinition = defineComponent<ProductShelfHeadingProps>({\n name: 'ProductShelfHeading',\n importPath: '@vtex/faststore/components',\n component: ProductShelfHeading,\n editor: { label: 'Product Shelf Heading', category: 'commerce' },\n requiresContext: 'product-shelf',\n schema: {},\n guideline:\n \"Renders its children as the shelf title (an <h2>) and provides the section's accessible label. Author the title text as its children (props.children). Place it inside ProductShelfRoot where the title should sit — usually above CarouselRoot. Renders nothing when it has no children.\",\n});\n\n// ---------------------------------------------------------------------------\n// Implementations\n// ---------------------------------------------------------------------------\n\n/** The content value when the merchant filled the field, else the prop (the shared emptiness rule). */\nfunction contentOrProp<T>(content: T | undefined, prop: T): T {\n if (content === undefined || isEmptyContentValue(content)) return prop;\n return content;\n}\n\nexport function ProductShelfRoot({\n first = 5,\n itemsPerPage = 5,\n sort = 'relevance',\n searchTerm,\n categoryId,\n brandId,\n collectionId,\n availableOnly = false,\n usePriceWithTaxes = false,\n taxesLabel = 'Tax included',\n products,\n $content,\n $data: $dataProp,\n className,\n children,\n ...props\n}: ProductShelfRootProps) {\n const $data = useDataOverride<ProductShelfData | null | undefined>($dataProp);\n // The query already applied these values (the compiler binds the same slice and props to the\n // fragment variables); the context only republishes what this element resolved to.\n const shelfConfig = useMemo<ProductShelfConfig>(\n () => ({\n first: contentOrProp($content?.first, first),\n sort: contentOrProp($content?.sort, sort),\n searchTerm: contentOrProp($content?.searchTerm, searchTerm),\n categoryId: contentOrProp($content?.categoryId, categoryId),\n brandId: contentOrProp($content?.brandId, brandId),\n collectionId: contentOrProp($content?.collectionId, collectionId),\n availableOnly: contentOrProp($content?.availableOnly, availableOnly),\n }),\n [$content, first, sort, searchTerm, categoryId, brandId, collectionId, availableOnly],\n );\n const headingId = useId();\n // Ref-counted so a composed ProductShelfHeading flips `hasHeading` wherever it is placed; the\n // section then labels itself only while a heading is mounted, never via a dangling idref.\n const [headingCount, setHeadingCount] = useState(0);\n const registerHeading = useCallback(() => {\n setHeadingCount((count) => count + 1);\n return () => setHeadingCount((count) => count - 1);\n }, []);\n const headingContext = useMemo(() => ({ headingId, registerHeading }), [headingId, registerHeading]);\n const config = useMemo(() => ({ usePriceWithTaxes, taxesLabel }), [usePriceWithTaxes, taxesLabel]);\n const items = useMemo<ProductShelfItem[]>(() => {\n if (products !== undefined) return products;\n return $data ? transformProducts($data) : [];\n }, [products, $data]);\n const shelf = useMemo<ProductShelfContextValue>(\n () => ({ itemsPerPage, config: shelfConfig, count: items.length }),\n [itemsPerPage, shelfConfig, items.length],\n );\n const hasHeading = headingCount > 0;\n\n if (products === undefined && $data === undefined) {\n // Lazy source not resolved yet — show the skeleton (the runtime also renders it).\n return <ProductShelfSkeleton className={className} />;\n }\n\n // Empty result renders nothing (legacy parity) rather than an empty carousel.\n if (items.length === 0) {\n return null;\n }\n\n return (\n <ProductListConfigContext value={config}>\n <ProductShelfProductsContext value={items}>\n <ProductShelfContext value={shelf}>\n <ProductShelfHeadingContext value={headingContext}>\n <section\n {...props}\n className={className}\n data-fs-product-shelf=\"\"\n aria-labelledby={hasHeading ? headingId : undefined}\n >\n {children}\n </section>\n </ProductShelfHeadingContext>\n </ProductShelfContext>\n </ProductShelfProductsContext>\n </ProductListConfigContext>\n );\n}\n\n/**\n * `ProductShelfRoot` for a `'lazy-on-mount'` page element: the generator wraps it in a\n * `HotDataProvider` whose `null` fallback reaches the inner root through the data override.\n */\nexport function ProductShelfOnMountRoot(props: Readonly<ProductShelfOnMountRootProps>) {\n return <ProductShelfRoot {...props} />;\n}\n\nexport function ProductShelfHeading({ className, children, ...props }: ProductShelfHeadingProps) {\n const { headingId, registerHeading } = useProductShelfHeading();\n const hasContent = children != null && children !== '';\n\n // Announce presence to the root so its section wires `aria-labelledby` to this heading; the\n // registration (and its unregister) is scoped to when the heading actually renders content.\n useEffect(() => {\n if (!hasContent) {\n return;\n }\n return registerHeading();\n }, [hasContent, registerHeading]);\n\n if (!hasContent) {\n return null;\n }\n\n return (\n <Text {...props} as=\"h2\" id={headingId} className={className} data-fs-product-shelf-heading=\"\">\n {children}\n </Text>\n );\n}\n\nexport function ProductShelfItems({ children, ...props }: ProductShelfItemsProps) {\n const items = useContext(ProductShelfProductsContext);\n\n return (\n <CarouselTrack {...props} data-fs-product-shelf-items=\"\">\n {items.map((item) => (\n <ProductListItemContext key={item.id} value={item}>\n {children}\n </ProductListItemContext>\n ))}\n </CarouselTrack>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Skeleton\n// ---------------------------------------------------------------------------\n\nexport function ProductShelfSkeleton({ className, ...props }: ComponentPropsWithRef<'div'>) {\n return (\n <div {...props} className={className} data-fs-product-shelf-skeleton=\"\" aria-hidden=\"true\">\n <span data-fs-product-shelf-skeleton-item=\"\" />\n <span data-fs-product-shelf-skeleton-item=\"\" />\n <span data-fs-product-shelf-skeleton-item=\"\" />\n </div>\n );\n}\n\nProductShelfRoot.Skeleton = ProductShelfSkeleton;\n\n// ---------------------------------------------------------------------------\n// Namespace export\n// ---------------------------------------------------------------------------\n\nexport const ProductShelf = {\n Root: ProductShelfRoot,\n Heading: ProductShelfHeading,\n Items: ProductShelfItems,\n Skeleton: ProductShelfSkeleton,\n};\n"],"mappings":"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"}
|