@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.
Files changed (151) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/dist/actions.json +42 -0
  3. package/dist/catalog.json +731 -16
  4. package/dist/cli/index.js +1 -1
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/components.d.ts +9 -4
  7. package/dist/components.js +11 -6
  8. package/dist/content-types.json +300 -0
  9. package/dist/core/dist/define-component.d.ts +6 -1
  10. package/dist/docs/account-entry-point.md +4 -4
  11. package/dist/docs/account-menu.md +2 -2
  12. package/dist/docs/account-saved-cards.md +301 -0
  13. package/dist/docs/binding-switcher.md +154 -0
  14. package/dist/docs/card-entry.md +3 -3
  15. package/dist/docs/card-summary.md +175 -0
  16. package/dist/docs/cart-destination.md +238 -0
  17. package/dist/docs/cart-item.md +21 -3
  18. package/dist/docs/cart-totalizer.md +1 -1
  19. package/dist/docs/cart.md +118 -11
  20. package/dist/docs/footer-social.md +1 -1
  21. package/dist/docs/free-shipping-progress.md +13 -9
  22. package/dist/docs/incentives.md +2 -2
  23. package/dist/docs/logo.md +1 -1
  24. package/dist/docs/payment-method-badges.md +2 -2
  25. package/dist/docs/payment-methods.md +1 -1
  26. package/dist/docs/product-information.md +2 -2
  27. package/dist/docs/product-shelf.md +7 -0
  28. package/dist/docs/select.md +3 -4
  29. package/dist/docs/sign-in-redirect.md +1 -1
  30. package/dist/docs/toast.md +30 -12
  31. package/dist/index.d.ts +2 -1
  32. package/dist/internal/client.d.ts +4 -1
  33. package/dist/internal/client.js +4 -1
  34. package/dist/internal/server.d.ts +4 -2
  35. package/dist/internal/server.js +4 -2
  36. package/dist/matter/account-saved-cards/account-saved-cards.d.ts +383 -0
  37. package/dist/matter/account-saved-cards/account-saved-cards.js +956 -0
  38. package/dist/matter/account-saved-cards/account-saved-cards.js.map +1 -0
  39. package/dist/matter/billing-address/billing-address.js.map +1 -1
  40. package/dist/matter/cart/cart-namespace.d.ts +5 -1
  41. package/dist/matter/cart/cart-namespace.js +5 -1
  42. package/dist/matter/cart/cart-namespace.js.map +1 -1
  43. package/dist/matter/cart/cart.d.ts +34 -4
  44. package/dist/matter/cart/cart.js +238 -28
  45. package/dist/matter/cart/cart.js.map +1 -1
  46. package/dist/matter/cart-destination/cart-destination.d.ts +222 -0
  47. package/dist/matter/cart-destination/cart-destination.js +613 -0
  48. package/dist/matter/cart-destination/cart-destination.js.map +1 -0
  49. package/dist/matter/cart-item/cart-item.d.ts +4 -2
  50. package/dist/matter/cart-item/cart-item.js +75 -16
  51. package/dist/matter/cart-item/cart-item.js.map +1 -1
  52. package/dist/matter/cart-totalizer/cart-totalizer.js +3 -2
  53. package/dist/matter/cart-totalizer/cart-totalizer.js.map +1 -1
  54. package/dist/matter/free-shipping-progress/free-shipping-progress.d.ts +6 -3
  55. package/dist/matter/free-shipping-progress/free-shipping-progress.js +76 -33
  56. package/dist/matter/free-shipping-progress/free-shipping-progress.js.map +1 -1
  57. package/dist/matter/pricing/pricing.js.map +1 -1
  58. package/dist/matter/product-shelf/product-shelf.d.ts +7 -0
  59. package/dist/matter/product-shelf/product-shelf.js +115 -1
  60. package/dist/matter/product-shelf/product-shelf.js.map +1 -1
  61. package/dist/matter/shipping-address/shipping-address.js.map +1 -1
  62. package/dist/matter/shipping-simulator/shipping-simulator.d.ts +1 -1
  63. package/dist/matter/shipping-simulator/shipping-simulator.js +6 -2
  64. package/dist/matter/shipping-simulator/shipping-simulator.js.map +1 -1
  65. package/dist/particles/binding-switcher/binding-switcher.d.ts +38 -0
  66. package/dist/particles/binding-switcher/binding-switcher.js +183 -0
  67. package/dist/particles/binding-switcher/binding-switcher.js.map +1 -0
  68. package/dist/particles/card-summary/card-network.d.ts +17 -0
  69. package/dist/particles/card-summary/card-network.js +25 -0
  70. package/dist/particles/card-summary/card-network.js.map +1 -0
  71. package/dist/particles/card-summary/card-summary.d.ts +83 -0
  72. package/dist/particles/card-summary/card-summary.js +188 -0
  73. package/dist/particles/card-summary/card-summary.js.map +1 -0
  74. package/dist/particles/footer-social/footer-social.d.ts +1 -1
  75. package/dist/particles/footer-social/footer-social.js +1 -1
  76. package/dist/particles/footer-social/footer-social.js.map +1 -1
  77. package/dist/particles/incentives/incentives.d.ts +1 -1
  78. package/dist/particles/incentives/incentives.js +1 -1
  79. package/dist/particles/incentives/incentives.js.map +1 -1
  80. package/dist/particles/payment-methods/payment-methods.d.ts +1 -1
  81. package/dist/particles/payment-methods/payment-methods.js +1 -1
  82. package/dist/particles/payment-methods/payment-methods.js.map +1 -1
  83. package/dist/particles/toast/toast.d.ts +20 -6
  84. package/dist/particles/toast/toast.js +49 -7
  85. package/dist/particles/toast/toast.js.map +1 -1
  86. package/dist/schema.graphql +298 -34
  87. package/dist/sdk/actions/index.d.ts +3 -1
  88. package/dist/sdk/actions/index.js +3 -1
  89. package/dist/sdk/actions/locale-action-provider.d.ts +16 -0
  90. package/dist/sdk/actions/locale-action-provider.js +20 -0
  91. package/dist/sdk/actions/locale-action-provider.js.map +1 -0
  92. package/dist/sdk/actions/saved-card-action-provider.d.ts +12 -0
  93. package/dist/sdk/actions/saved-card-action-provider.js +16 -0
  94. package/dist/sdk/actions/saved-card-action-provider.js.map +1 -0
  95. package/dist/sdk/actions/types.d.ts +6 -2
  96. package/dist/sdk/cache/storefront-cache.js +2 -1
  97. package/dist/sdk/cache/storefront-cache.js.map +1 -1
  98. package/dist/sdk/cart/operations.d.ts +2 -1
  99. package/dist/sdk/cart/operations.js +55 -1
  100. package/dist/sdk/cart/operations.js.map +1 -1
  101. package/dist/sdk/cart/optimistic.d.ts +30 -7
  102. package/dist/sdk/cart/optimistic.js +78 -41
  103. package/dist/sdk/cart/optimistic.js.map +1 -1
  104. package/dist/sdk/cart/use-cart-actions.d.ts +7 -0
  105. package/dist/sdk/cart/use-cart-actions.js +61 -15
  106. package/dist/sdk/cart/use-cart-actions.js.map +1 -1
  107. package/dist/sdk/cart/use-cart-sync-sales-channel.js +70 -0
  108. package/dist/sdk/cart/use-cart-sync-sales-channel.js.map +1 -0
  109. package/dist/sdk/generated/gql.js +15 -11
  110. package/dist/sdk/generated/gql.js.map +1 -1
  111. package/dist/sdk/generated/graphql.d.ts +177 -46
  112. package/dist/sdk/generated/graphql.js +2314 -93
  113. package/dist/sdk/generated/graphql.js.map +1 -1
  114. package/dist/sdk/i18n/messages/en-US.js +19 -0
  115. package/dist/sdk/i18n/messages/en-US.json +19 -0
  116. package/dist/sdk/i18n/messages/en-US.json.d.ts +19 -0
  117. package/dist/sdk/i18n/messages/es-ES.js +19 -0
  118. package/dist/sdk/i18n/messages/es-ES.json +19 -0
  119. package/dist/sdk/i18n/messages/pt-BR.js +19 -0
  120. package/dist/sdk/i18n/messages/pt-BR.json +19 -0
  121. package/dist/sdk/i18n/provider.js +1 -1
  122. package/dist/sdk/i18n/provider.js.map +1 -1
  123. package/dist/sdk/locale/binding-switch-provider.d.ts +13 -0
  124. package/dist/sdk/locale/binding-switch-provider.js +20 -0
  125. package/dist/sdk/locale/binding-switch-provider.js.map +1 -0
  126. package/dist/sdk/locale/use-locale-actions.d.ts +16 -0
  127. package/dist/sdk/locale/use-locale-actions.js +70 -0
  128. package/dist/sdk/locale/use-locale-actions.js.map +1 -0
  129. package/dist/sdk/navigation/binding-switch.d.ts +39 -0
  130. package/dist/sdk/navigation/binding-switch.js +129 -0
  131. package/dist/sdk/navigation/binding-switch.js.map +1 -0
  132. package/dist/sdk/navigation/decode-route-params.d.ts +11 -0
  133. package/dist/sdk/navigation/decode-route-params.js +32 -0
  134. package/dist/sdk/navigation/decode-route-params.js.map +1 -0
  135. package/dist/sdk/saved-cards/operations.d.ts +16 -0
  136. package/dist/sdk/saved-cards/operations.js +41 -0
  137. package/dist/sdk/saved-cards/operations.js.map +1 -0
  138. package/dist/sdk/saved-cards/use-saved-card-actions.d.ts +26 -0
  139. package/dist/sdk/saved-cards/use-saved-card-actions.js +86 -0
  140. package/dist/sdk/saved-cards/use-saved-card-actions.js.map +1 -0
  141. package/dist/sdk/seo/page-alternates.d.ts +5 -3
  142. package/dist/sdk/seo/page-alternates.js +7 -12
  143. package/dist/sdk/seo/page-alternates.js.map +1 -1
  144. package/dist/sdk/shipping/shipping-operations.d.ts +1 -1
  145. package/dist/sdk/shipping/shipping-operations.js +13 -0
  146. package/dist/sdk/shipping/shipping-operations.js.map +1 -1
  147. package/dist/sdk/state/cart.d.ts +9 -2
  148. package/dist/sdk/state/cart.js +9 -2
  149. package/dist/sdk/state/cart.js.map +1 -1
  150. package/package.json +2 -2
  151. package/src/styles/tailwind.css +1 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cart-destination.js","names":[],"sources":["../../../src/matter/cart-destination/cart-destination.tsx"],"sourcesContent":["'use client';\n\nimport { type ContentValue, defineComponent, defineContentType } from '@vtex/fs-core';\nimport {\n type ComponentPropsWithRef,\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { isPostalCodeComplete, PostalCodeField } from '../../particles/postal-code-field/postal-code-field.js';\nimport { useActionsOverride } from '../../sdk/actions/actions-override-context.js';\nimport type { ComponentActions } from '../../sdk/actions/types.js';\nimport { awaitCartWrites } from '../../sdk/cart/write-gate.js';\nimport { useDataOverride } from '../../sdk/data/data-override-context.js';\nimport { ReactiveRootContext } from '../../sdk/data/page-reactive-data-root.js';\nimport type { ShippingAddressUpdateErrorEntity } from '../../sdk/shipping/shipping-operations.js';\nimport { getCountryDescriptor } from '../shipping-address/country-descriptors.js';\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment — the destination only, never a name or a street\n// ---------------------------------------------------------------------------\n\nconst fragment = /* GraphQL */ `\n fragment CartDestinationData on Query {\n shipping {\n __typename\n id\n address {\n __typename\n id\n postalCode\n country\n city\n state\n }\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Content models — one per copy-bearing part\n// ---------------------------------------------------------------------------\n\nexport const CartDestinationValueContent = defineContentType({\n id: 'cart-destination-value',\n title: 'Cart Destination Value',\n description: 'Copy that precedes the destination in read mode.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n prefix: { type: 'string', title: 'Prefix' },\n },\n },\n});\n\nexport const CartDestinationChangeContent = defineContentType({\n id: 'cart-destination-change',\n title: 'Cart Destination Change',\n description: 'Copy for the control that opens edit mode.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n label: { type: 'string', title: 'Button label' },\n },\n },\n});\n\nexport const CartDestinationSubmitContent = defineContentType({\n id: 'cart-destination-submit',\n title: 'Cart Destination Submit',\n description: 'Copy for the control that writes the destination.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n label: { type: 'string', title: 'Button label' },\n pendingLabel: { type: 'string', title: 'Pending label' },\n },\n },\n});\n\nexport const CartDestinationCancelContent = defineContentType({\n id: 'cart-destination-cancel',\n title: 'Cart Destination Cancel',\n description: 'Copy for the control that closes edit mode without writing.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n label: { type: 'string', title: 'Button label' },\n },\n },\n});\n\nexport const CartDestinationErrorContent = defineContentType({\n id: 'cart-destination-error',\n title: 'Cart Destination Error',\n description: 'Message shown when the destination write fails.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n genericMessage: { type: 'string', title: 'Failure message' },\n },\n },\n});\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * The `shipping` slice forwarded by the reactive runtime: the value of the `shipping` top-level field\n * of `CartDestinationData on Query`, `null` while it has not resolved.\n */\nexport type CartDestinationEntity = {\n __typename?: 'Shipping';\n id?: string;\n address?: {\n __typename?: 'Address';\n id?: string | null;\n postalCode?: string | null;\n country?: string | null;\n city?: string | null;\n state?: string | null;\n } | null;\n};\n\nexport type CartDestinationRootProps = ComponentPropsWithRef<'div'> & {\n /** Country used while the cart has no address yet. */\n defaultCountry?: string;\n $data?: CartDestinationEntity | null;\n /** Injected by the compiler from `actions: { shipping: ['setAddress'] }`. */\n $actions?: ComponentActions;\n children?: ReactNode;\n};\n\nexport type CartDestinationReadProps = ComponentPropsWithRef<'div'>;\n\n/** Children are omitted: what this renders is the cart's destination, never authored content. */\nexport type CartDestinationValueProps = Omit<ComponentPropsWithRef<'span'>, 'children'> & {\n prefix?: string;\n /** Merchant-authored copy; wins over the prop when present. */\n $content?: ContentValue<typeof CartDestinationValueContent>;\n};\n\nexport type CartDestinationUnsetProps = ComponentPropsWithRef<'div'>;\n\nexport type CartDestinationChangeProps = ComponentPropsWithRef<'button'> & {\n label?: string;\n $content?: ContentValue<typeof CartDestinationChangeContent>;\n};\n\nexport type CartDestinationFormProps = ComponentPropsWithRef<'form'>;\n\nexport type CartDestinationPostalCodeControlProps = {\n className?: string;\n /** Mask template; defaults to the one of the cart's country. */\n mask?: string;\n label?: string;\n placeholder?: string;\n /** The field's own message for a draft that is incomplete or fails the country's pattern. */\n invalidMessage?: string;\n};\n\nexport type CartDestinationSubmitProps = ComponentPropsWithRef<'button'> & {\n label?: string;\n pendingLabel?: string;\n $content?: ContentValue<typeof CartDestinationSubmitContent>;\n};\n\nexport type CartDestinationCancelProps = ComponentPropsWithRef<'button'> & {\n label?: string;\n $content?: ContentValue<typeof CartDestinationCancelContent>;\n};\n\n/** Children are omitted: what this renders is the write's own error, never authored content. */\nexport type CartDestinationErrorProps = Omit<ComponentPropsWithRef<'div'>, 'children'> & {\n genericMessage?: string;\n $content?: ContentValue<typeof CartDestinationErrorContent>;\n};\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\nexport type CartDestinationContextValue = Readonly<{\n /** The cart's postal code, from `$data` only; null until a destination is set. */\n postalCode: string | null;\n city: string | null;\n state: string | null;\n /** The address country, else the root's `defaultCountry`. */\n country: string;\n /** The root query is in flight and no address is cached yet. */\n loading: boolean;\n /** Edit mode. Local to the root, `false` on mount. */\n editing: boolean;\n startEditing: () => void;\n /** Closes edit mode and discards the draft and the errors; writes nothing. */\n cancel: () => void;\n draft: string;\n setDraft: (value: string) => void;\n /** The draft is complete, valid, different from the current one, and no write is pending. */\n canSubmit: boolean;\n pending: boolean;\n errors: readonly ShippingAddressUpdateErrorEntity[];\n /** The last submit was refused locally. */\n invalid: boolean;\n submit: () => void;\n /**\n * The postal code control reports the mask it renders (`null` on unmount), so the root validates the\n * draft against the same one. Without a control the root uses the country's mask.\n */\n registerMask: (mask: string | null) => void;\n}>;\n\nconst CART_DESTINATION_CONTEXT_UNSET = Symbol('CartDestinationContextUnset');\n\nconst CartDestinationContextInternal = createContext<\n CartDestinationContextValue | typeof CART_DESTINATION_CONTEXT_UNSET\n>(CART_DESTINATION_CONTEXT_UNSET);\n\n/**\n * Read the destination and the edit-mode state from the nearest `CartDestinationRoot`. Throws outside\n * one: a misplaced part is a composition bug the author should see at once.\n */\nexport function useCartDestination(): CartDestinationContextValue {\n const ctx = useContext(CartDestinationContextInternal);\n if (ctx === CART_DESTINATION_CONTEXT_UNSET) {\n throw new Error('CartDestination components must be used inside a <CartDestinationRoot> subtree.');\n }\n return ctx;\n}\n\n/** Whether a `CartDestinationForm` encloses the subtree, so `CartDestinationSubmit` knows its form owner. */\nconst CartDestinationFormContext = createContext(false);\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_COUNTRY = 'BRA';\n\n/** Mask per country; the field's own default (the Brazilian CEP) covers any country not listed. */\nconst DEFAULT_MASK = '99999-999';\n\nconst COUNTRY_MASKS: Readonly<Record<string, string>> = {\n BRA: DEFAULT_MASK,\n USA: '99999|99999-9999',\n};\n\nfunction maskFor(country: string): string {\n return COUNTRY_MASKS[country] ?? DEFAULT_MASK;\n}\n\n/** Letters and digits only, so `01310100` and `01310-100` compare equal. */\nfunction normalizePostalCode(value: string): string {\n return value.replaceAll(/[^0-9A-Za-z]/g, '').toUpperCase();\n}\n\nfunction isDraftValid(draft: string, country: string, mask: string): boolean {\n if (!isPostalCodeComplete(draft, mask)) return false;\n const pattern = getCountryDescriptor(country)?.fields.find((field) => field.name === 'postalCode')?.rule?.pattern;\n if (!pattern) return true;\n const regex = pattern instanceof RegExp ? pattern : pattern.value;\n return regex.test(draft);\n}\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const CartDestinationRootDefinition = defineComponent<CartDestinationRootProps>({\n name: 'CartDestinationRoot',\n importPath: '@vtex/faststore/components',\n component: CartDestinationRoot,\n fragment,\n fetchStrategy: 'reactive',\n providesContext: 'cartDestination',\n actions: { shipping: ['setAddress'] },\n editor: { label: 'Cart Destination Root', category: 'content' },\n schema: {\n defaultCountry: { type: 'string', default: 'BRA', label: 'Default country (ISO alpha-3)' },\n },\n guideline:\n 'Where the cart is delivered, with a postal-code-only form that writes the cart destination so the free-shipping bar follows. Compose CartDestinationRead (CartDestinationValue, CartDestinationUnset, CartDestinationChange) for read mode and CartDestinationForm (PostalCodeControl, Submit, Cancel, Error) for edit mode. Starts in read mode; the store cannot open edit mode from outside. See cart-destination.md.',\n});\n\nexport const CartDestinationReadDefinition = defineComponent<CartDestinationReadProps>({\n name: 'CartDestinationRead',\n importPath: '@vtex/faststore/components',\n component: CartDestinationRead,\n requiresContext: 'cartDestination',\n editor: { label: 'Cart Destination Read', category: 'layout' },\n schema: {},\n guideline:\n 'Wraps what read mode shows: the value, the invitation when no destination is set, and the change control. Renders nothing while edit mode is open. Must be inside CartDestinationRoot.',\n});\n\nexport const CartDestinationValueDefinition = defineComponent<CartDestinationValueProps>({\n name: 'CartDestinationValue',\n importPath: '@vtex/faststore/components',\n component: CartDestinationValue,\n content: CartDestinationValueContent,\n requiresContext: 'cartDestination',\n editor: { label: 'Cart Destination Value', category: 'content' },\n schema: {\n prefix: { type: 'string', label: 'Prefix' },\n },\n guideline:\n 'The cart destination as postal code, city and state, each in its own element. Renders nothing while loading or when no destination is set. Must be inside CartDestinationRoot.',\n});\n\nexport const CartDestinationUnsetDefinition = defineComponent<CartDestinationUnsetProps>({\n name: 'CartDestinationUnset',\n importPath: '@vtex/faststore/components',\n component: CartDestinationUnset,\n requiresContext: 'cartDestination',\n editor: { label: 'Cart Destination Unset', category: 'content' },\n schema: {},\n guideline:\n 'Renders its children only once the destination resolved and none is set. Place the store-authored invitation (for example a Text reading \"Informe seu CEP\") inside it, within CartDestinationRead. Must be inside CartDestinationRoot.',\n});\n\nexport const CartDestinationChangeDefinition = defineComponent<CartDestinationChangeProps>({\n name: 'CartDestinationChange',\n importPath: '@vtex/faststore/components',\n component: CartDestinationChange,\n content: CartDestinationChangeContent,\n requiresContext: 'cartDestination',\n editor: { label: 'Cart Destination Change', category: 'action' },\n schema: {\n label: { type: 'string', label: 'Button label' },\n },\n guideline:\n 'Button that opens edit mode, styled as a link by the store. Place it inside CartDestinationRead. Must be inside CartDestinationRoot.',\n});\n\nexport const CartDestinationFormDefinition = defineComponent<CartDestinationFormProps>({\n name: 'CartDestinationForm',\n importPath: '@vtex/faststore/components',\n component: CartDestinationForm,\n requiresContext: 'cartDestination',\n editor: { label: 'Cart Destination Form', category: 'action' },\n schema: {},\n guideline:\n 'Edit mode: renders only while editing and routes Enter to the submit. Holds the postal code control, Submit, Cancel and Error, and any help text the store places. Must be inside CartDestinationRoot.',\n});\n\nexport const CartDestinationPostalCodeControlDefinition = defineComponent<CartDestinationPostalCodeControlProps>({\n name: 'CartDestinationPostalCodeControl',\n importPath: '@vtex/faststore/components',\n component: CartDestinationPostalCodeControl,\n requiresContext: 'cartDestination',\n editor: { label: 'Cart Destination Postal Code Control', category: 'content' },\n schema: {\n mask: { type: 'string', label: 'Mask pattern' },\n label: { type: 'string', label: 'Label' },\n placeholder: { type: 'string', label: 'Placeholder' },\n invalidMessage: { type: 'string', label: 'Invalid-value message' },\n },\n guideline:\n 'The postal code input of edit mode, empty each time edit mode opens and focused on open. The mask follows the cart country unless set. Must be inside CartDestinationForm.',\n});\n\nexport const CartDestinationSubmitDefinition = defineComponent<CartDestinationSubmitProps>({\n name: 'CartDestinationSubmit',\n importPath: '@vtex/faststore/components',\n component: CartDestinationSubmit,\n content: CartDestinationSubmitContent,\n requiresContext: 'cartDestination',\n editor: { label: 'Cart Destination Submit', category: 'action' },\n schema: {\n label: { type: 'string', label: 'Button label' },\n pendingLabel: { type: 'string', label: 'Pending label' },\n },\n guideline:\n 'Writes the typed postal code. Disabled until it is complete for the country, valid and different from the current one, and while a write is pending (aria-busy). Must be inside CartDestinationForm.',\n});\n\nexport const CartDestinationCancelDefinition = defineComponent<CartDestinationCancelProps>({\n name: 'CartDestinationCancel',\n importPath: '@vtex/faststore/components',\n component: CartDestinationCancel,\n content: CartDestinationCancelContent,\n requiresContext: 'cartDestination',\n editor: { label: 'Cart Destination Cancel', category: 'action' },\n schema: {\n label: { type: 'string', label: 'Button label' },\n },\n guideline:\n 'Closes edit mode, discards what was typed and writes nothing. Disabled while a write is pending. Must be inside CartDestinationForm.',\n});\n\nexport const CartDestinationErrorDefinition = defineComponent<CartDestinationErrorProps>({\n name: 'CartDestinationError',\n importPath: '@vtex/faststore/components',\n component: CartDestinationError,\n content: CartDestinationErrorContent,\n requiresContext: 'cartDestination',\n editor: { label: 'Cart Destination Error', category: 'content' },\n schema: {\n genericMessage: { type: 'string', label: 'Failure message' },\n },\n guideline:\n 'Announces why the destination write failed, as an alert, and renders nothing otherwise. Author one message covering a refused postal code and a network failure: both reach the client as the same error. Must be inside CartDestinationForm.',\n});\n\n// ---------------------------------------------------------------------------\n// Implementations\n// ---------------------------------------------------------------------------\n\n/**\n * Reads the cart destination reactively and publishes it, the edit-mode flag, the draft and the write\n * state as `cartDestination` context.\n *\n * The destination is never copied into state: the Value renders only what `$data` resolves, and a\n * successful write updates it solely by the mutation payload normalizing into the Apollo cache. A\n * rejected write therefore needs no rollback.\n */\nexport function CartDestinationRoot({\n defaultCountry = DEFAULT_COUNTRY,\n $data: $dataProp,\n $actions: $actionsProp,\n className,\n children,\n ...props\n}: CartDestinationRootProps) {\n const $data = useDataOverride($dataProp);\n const $actions = useActionsOverride($actionsProp);\n const address = $data?.address ?? null;\n const { loading: rootLoading } = useContext(ReactiveRootContext);\n\n const postalCode = address?.postalCode ?? null;\n // A null destination is ambiguous alone: an unresolved read and a cart with none look the same, so\n // the root query's in-flight flag tells \"loading\" from \"unset\".\n const loading = rootLoading && postalCode == null;\n const country = address?.country ?? defaultCountry;\n\n const [editing, setEditing] = useState(false);\n const [draft, setDraft] = useState('');\n const [pending, setPending] = useState(false);\n const [errors, setErrors] = useState<readonly ShippingAddressUpdateErrorEntity[]>([]);\n const [invalid, setInvalid] = useState(false);\n // State is batched, so two activations in one tick would both pass a `pending` check.\n const inFlightRef = useRef(false);\n const rootRef = useRef<HTMLDivElement>(null);\n const wasEditingRef = useRef(false);\n\n /**\n * Opening edit mode moves focus to the field; leaving it moves focus back to the change control,\n * which only mounts once read mode renders again, hence an effect on the flag rather than a call at\n * the event.\n */\n useEffect(() => {\n if (wasEditingRef.current === editing) return;\n wasEditingRef.current = editing;\n const selector = editing ? '[data-fs-postal-code-field-input]' : '[data-fs-cart-destination-change]';\n rootRef.current?.querySelector<HTMLElement>(selector)?.focus();\n }, [editing]);\n\n const [registeredMask, setRegisteredMask] = useState<string | null>(null);\n const mask = registeredMask ?? maskFor(country);\n const canSubmit =\n !pending &&\n isDraftValid(draft, country, mask) &&\n normalizePostalCode(draft) !== normalizePostalCode(postalCode ?? '');\n\n const startEditing = useCallback(() => setEditing(true), []);\n\n const cancel = useCallback(() => {\n setEditing(false);\n setDraft('');\n setErrors([]);\n setInvalid(false);\n }, []);\n\n /** Typing is the shopper's answer to a refusal, so the message and the invalid flag go with it. */\n const updateDraft = useCallback((value: string) => {\n setDraft(value);\n setErrors((current) => (current.length === 0 ? current : []));\n setInvalid(false);\n }, []);\n\n const submit = useCallback(() => {\n if (inFlightRef.current) return;\n const setAddress = $actions?.shipping?.setAddress;\n if (!setAddress) return;\n if (!canSubmit) {\n setInvalid(true);\n return;\n }\n\n inFlightRef.current = true;\n setPending(true);\n setErrors([]);\n setInvalid(false);\n\n // The resolver builds `logisticsInfo` from the order form it reads, so a line write still in\n // flight has to land first.\n void awaitCartWrites()\n .then(() => setAddress({ postalCode: draft, country }))\n .then((writeErrors) => {\n if (writeErrors.length > 0) {\n // A refusal keeps edit mode open so the message has somewhere to show.\n setErrors(writeErrors);\n return;\n }\n setEditing(false);\n setDraft('');\n })\n .finally(() => {\n inFlightRef.current = false;\n setPending(false);\n });\n }, [$actions, canSubmit, draft, country]);\n\n const value: CartDestinationContextValue = useMemo(\n () => ({\n postalCode,\n city: address?.city ?? null,\n state: address?.state ?? null,\n country,\n loading,\n editing,\n startEditing,\n cancel,\n draft,\n setDraft: updateDraft,\n canSubmit,\n pending,\n errors,\n invalid,\n submit,\n registerMask: setRegisteredMask,\n }),\n [\n postalCode,\n address?.city,\n address?.state,\n country,\n loading,\n editing,\n startEditing,\n cancel,\n draft,\n updateDraft,\n canSubmit,\n pending,\n errors,\n invalid,\n submit,\n ],\n );\n\n return (\n <CartDestinationContextInternal value={value}>\n <div\n {...props}\n ref={rootRef}\n className={className}\n data-fs-cart-destination=\"\"\n {...(pending ? { 'data-fs-cart-destination-pending': '' } : {})}\n {...(editing ? { 'data-fs-cart-destination-editing': '' } : {})}\n >\n {children}\n </div>\n </CartDestinationContextInternal>\n );\n}\n\nexport function CartDestinationRead({ className, children, ...props }: CartDestinationReadProps) {\n const { editing } = useCartDestination();\n if (editing) return null;\n\n return (\n <div {...props} className={className} data-fs-cart-destination-read=\"\">\n {children}\n </div>\n );\n}\n\nfunction pickAuthored(content: string | undefined, prop: string | undefined): string | null {\n if (content?.trim()) return content;\n if (prop?.trim()) return prop;\n return null;\n}\n\nexport function CartDestinationValue({ prefix, $content, className, ...props }: CartDestinationValueProps) {\n const { postalCode, city, state, loading } = useCartDestination();\n if (loading || postalCode == null) return null;\n\n const resolvedPrefix = pickAuthored($content?.prefix, prefix);\n\n return (\n <span {...props} className={className} data-fs-cart-destination-value=\"\">\n {resolvedPrefix ? <span data-fs-cart-destination-value-prefix=\"\">{resolvedPrefix}</span> : null}\n {/* Plain text nodes between the pieces: they keep the accessible name readable and a flex\n container strips them, so a store's `gap` still governs the visible spacing. */}\n {resolvedPrefix ? ' ' : null}\n <span data-fs-cart-destination-value-postal-code=\"\">{postalCode}</span>\n {city || state ? ' · ' : null}\n {city ? <span data-fs-cart-destination-value-city=\"\">{city}</span> : null}\n {city && state ? ', ' : null}\n {state ? <span data-fs-cart-destination-value-state=\"\">{state}</span> : null}\n </span>\n );\n}\n\nexport function CartDestinationUnset({ className, children, ...props }: CartDestinationUnsetProps) {\n const { postalCode, loading } = useCartDestination();\n if (loading || postalCode != null) return null;\n\n return (\n <div {...props} className={className} data-fs-cart-destination-unset=\"\">\n {children}\n </div>\n );\n}\n\nexport function CartDestinationChange({ label, $content, className, children, ...props }: CartDestinationChangeProps) {\n const { editing, startEditing } = useCartDestination();\n const resolvedLabel = pickAuthored($content?.label, label);\n\n return (\n <button\n {...props}\n type=\"button\"\n onClick={startEditing}\n aria-expanded={editing}\n className={className}\n data-fs-cart-destination-change=\"\"\n >\n {children ?? resolvedLabel}\n </button>\n );\n}\n\nexport function CartDestinationForm({ className, children, ...props }: CartDestinationFormProps) {\n const { editing, submit } = useCartDestination();\n if (!editing) return null;\n\n return (\n <CartDestinationFormContext value={true}>\n <form // react-doctor-disable-line no-prevent-default -- submit() is the only handler, so a native submit would reload the page\n {...props}\n className={className}\n data-fs-cart-destination-form=\"\"\n // Submit is disabled for an invalid draft, so the browser skips implicit submission on Enter\n // and the field would never be flagged; the key is routed to submit() here instead.\n onKeyDown={(event) => {\n if (event.key !== 'Enter' || !(event.target instanceof HTMLInputElement)) return;\n event.preventDefault();\n submit();\n }}\n onSubmit={(event) => {\n event.preventDefault();\n submit();\n }}\n >\n {children}\n </form>\n </CartDestinationFormContext>\n );\n}\n\nexport function CartDestinationPostalCodeControl({\n className,\n mask,\n label,\n placeholder,\n invalidMessage,\n}: Readonly<CartDestinationPostalCodeControlProps>) {\n const { country, draft, setDraft, errors, invalid, pending, registerMask } = useCartDestination();\n const rootRef = useRef<HTMLDivElement>(null);\n const refused = errors.length > 0;\n const effectiveMask = mask ?? maskFor(country);\n\n useEffect(() => {\n registerMask(effectiveMask);\n return () => registerMask(null);\n }, [effectiveMask, registerMask]);\n\n /**\n * Disabling the input while the write is in flight drops focus to the body, so a refusal otherwise\n * lands with nothing focused. Waits for `pending` to clear: focusing a disabled input is a no-op.\n */\n useEffect(() => {\n if (!refused || pending) return;\n rootRef.current?.querySelector<HTMLInputElement>('[data-fs-postal-code-field-input]')?.focus();\n }, [refused, pending]);\n\n return (\n <PostalCodeField\n ref={rootRef}\n className={className}\n data-fs-cart-destination-postal-code-control=\"\"\n mask={effectiveMask}\n label={label}\n placeholder={placeholder}\n // The field's own copy describes an incomplete value; while a refusal stands, the message is\n // `CartDestinationError`'s.\n errorMessage={refused ? undefined : invalidMessage}\n value={draft}\n onValueChange={setDraft}\n invalid={invalid || refused}\n disabled={pending}\n />\n );\n}\n\nexport function CartDestinationSubmit({\n label,\n pendingLabel,\n $content,\n className,\n children,\n ...props\n}: CartDestinationSubmitProps) {\n const { canSubmit, pending, submit } = useCartDestination();\n const inForm = useContext(CartDestinationFormContext);\n\n const resolvedLabel = pickAuthored($content?.label, label);\n const resolvedPendingLabel = pickAuthored($content?.pendingLabel, pendingLabel);\n\n return (\n <button\n {...props}\n // The form routes native submission to the same submit(); alone there is no form to submit.\n type={inForm ? 'submit' : 'button'}\n onClick={inForm ? props.onClick : submit}\n disabled={!canSubmit || props.disabled}\n aria-busy={pending || undefined}\n className={className}\n data-fs-cart-destination-submit=\"\"\n {...(pending ? { 'data-fs-cart-destination-submit-pending': '' } : {})}\n >\n {children ?? (pending ? (resolvedPendingLabel ?? resolvedLabel) : resolvedLabel)}\n </button>\n );\n}\n\nexport function CartDestinationCancel({ label, $content, className, children, ...props }: CartDestinationCancelProps) {\n const { cancel, pending } = useCartDestination();\n const resolvedLabel = pickAuthored($content?.label, label);\n\n return (\n <button\n {...props}\n type=\"button\"\n onClick={cancel}\n disabled={pending || props.disabled}\n className={className}\n data-fs-cart-destination-cancel=\"\"\n >\n {children ?? resolvedLabel}\n </button>\n );\n}\n\nexport function CartDestinationError({ genericMessage, $content, className, ...props }: CartDestinationErrorProps) {\n const { errors } = useCartDestination();\n if (errors.length === 0) return null;\n\n // A refused postal code and a network failure share one error code, so one authored message covers\n // both; the server's own message is the fallback when none is authored.\n const authored = pickAuthored($content?.genericMessage, genericMessage);\n\n return (\n <div {...props} role=\"alert\" className={className} data-fs-cart-destination-error=\"\">\n {authored ?? errors[0]?.message}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Namespace export\n// ---------------------------------------------------------------------------\n\nexport const CartDestination = {\n Root: CartDestinationRoot,\n Read: CartDestinationRead,\n Value: CartDestinationValue,\n Unset: CartDestinationUnset,\n Change: CartDestinationChange,\n Form: CartDestinationForm,\n PostalCodeControl: CartDestinationPostalCodeControl,\n Submit: CartDestinationSubmit,\n Cancel: CartDestinationCancel,\n Error: CartDestinationError,\n};\n"],"mappings":";;;;;;;;;;;;;AA2BA,MAAM,WAAyB;;;;;;;;;;;;;;;;AAqB/B,MAAa,8BAA8B,kBAAkB;CAC3D,IAAI;CACJ,OAAO;CACP,aAAa;CACb,QAAQ;EACN,MAAM;EACN,sBAAsB;EACtB,YAAY,EACV,QAAQ;GAAE,MAAM;GAAU,OAAO;EAAS,EAC5C;CACF;AACF,CAAC;AAED,MAAa,+BAA+B,kBAAkB;CAC5D,IAAI;CACJ,OAAO;CACP,aAAa;CACb,QAAQ;EACN,MAAM;EACN,sBAAsB;EACtB,YAAY,EACV,OAAO;GAAE,MAAM;GAAU,OAAO;EAAe,EACjD;CACF;AACF,CAAC;AAED,MAAa,+BAA+B,kBAAkB;CAC5D,IAAI;CACJ,OAAO;CACP,aAAa;CACb,QAAQ;EACN,MAAM;EACN,sBAAsB;EACtB,YAAY;GACV,OAAO;IAAE,MAAM;IAAU,OAAO;GAAe;GAC/C,cAAc;IAAE,MAAM;IAAU,OAAO;GAAgB;EACzD;CACF;AACF,CAAC;AAED,MAAa,+BAA+B,kBAAkB;CAC5D,IAAI;CACJ,OAAO;CACP,aAAa;CACb,QAAQ;EACN,MAAM;EACN,sBAAsB;EACtB,YAAY,EACV,OAAO;GAAE,MAAM;GAAU,OAAO;EAAe,EACjD;CACF;AACF,CAAC;AAED,MAAa,8BAA8B,kBAAkB;CAC3D,IAAI;CACJ,OAAO;CACP,aAAa;CACb,QAAQ;EACN,MAAM;EACN,sBAAsB;EACtB,YAAY,EACV,gBAAgB;GAAE,MAAM;GAAU,OAAO;EAAkB,EAC7D;CACF;AACF,CAAC;AA+GD,MAAM,iCAAiC,OAAO,6BAA6B;AAE3E,MAAM,iCAAiC,cAErC,8BAA8B;;;;;AAMhC,SAAgB,qBAAkD;CAChE,MAAM,MAAM,WAAW,8BAA8B;CACrD,IAAI,QAAQ,gCACV,MAAM,IAAI,MAAM,iFAAiF;CAEnG,OAAO;AACT;;AAGA,MAAM,6BAA6B,cAAc,KAAK;AAMtD,MAAM,kBAAkB;;AAGxB,MAAM,eAAe;AAErB,MAAM,gBAAkD;CACtD,KAAK;CACL,KAAK;AACP;AAEA,SAAS,QAAQ,SAAyB;CACxC,OAAO,cAAc,YAAY;AACnC;;AAGA,SAAS,oBAAoB,OAAuB;CAClD,OAAO,MAAM,WAAW,iBAAiB,EAAE,CAAC,CAAC,YAAY;AAC3D;AAEA,SAAS,aAAa,OAAe,SAAiB,MAAuB;CAC3E,IAAI,CAAC,qBAAqB,OAAO,IAAI,GAAG,OAAO;CAC/C,MAAM,UAAU,qBAAqB,OAAO,CAAC,EAAE,OAAO,MAAM,UAAU,MAAM,SAAS,YAAY,CAAC,EAAE,MAAM;CAC1G,IAAI,CAAC,SAAS,OAAO;CAErB,QADc,mBAAmB,SAAS,UAAU,QAAQ,MAAA,CAC/C,KAAK,KAAK;AACzB;AAMA,MAAa,gCAAgC,gBAA0C;CACrF,MAAM;CACN,YAAY;CACZ,WAAW;CACX;CACA,eAAe;CACf,iBAAiB;CACjB,SAAS,EAAE,UAAU,CAAC,YAAY,EAAE;CACpC,QAAQ;EAAE,OAAO;EAAyB,UAAU;CAAU;CAC9D,QAAQ,EACN,gBAAgB;EAAE,MAAM;EAAU,SAAS;EAAO,OAAO;CAAgC,EAC3F;CACA,WACE;AACJ,CAAC;AAED,MAAa,gCAAgC,gBAA0C;CACrF,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAAyB,UAAU;CAAS;CAC7D,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,MAAa,iCAAiC,gBAA2C;CACvF,MAAM;CACN,YAAY;CACZ,WAAW;CACX,SAAS;CACT,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAA0B,UAAU;CAAU;CAC/D,QAAQ,EACN,QAAQ;EAAE,MAAM;EAAU,OAAO;CAAS,EAC5C;CACA,WACE;AACJ,CAAC;AAED,MAAa,iCAAiC,gBAA2C;CACvF,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAA0B,UAAU;CAAU;CAC/D,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,MAAa,kCAAkC,gBAA4C;CACzF,MAAM;CACN,YAAY;CACZ,WAAW;CACX,SAAS;CACT,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAA2B,UAAU;CAAS;CAC/D,QAAQ,EACN,OAAO;EAAE,MAAM;EAAU,OAAO;CAAe,EACjD;CACA,WACE;AACJ,CAAC;AAED,MAAa,gCAAgC,gBAA0C;CACrF,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAAyB,UAAU;CAAS;CAC7D,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,MAAa,6CAA6C,gBAAuD;CAC/G,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAAwC,UAAU;CAAU;CAC7E,QAAQ;EACN,MAAM;GAAE,MAAM;GAAU,OAAO;EAAe;EAC9C,OAAO;GAAE,MAAM;GAAU,OAAO;EAAQ;EACxC,aAAa;GAAE,MAAM;GAAU,OAAO;EAAc;EACpD,gBAAgB;GAAE,MAAM;GAAU,OAAO;EAAwB;CACnE;CACA,WACE;AACJ,CAAC;AAED,MAAa,kCAAkC,gBAA4C;CACzF,MAAM;CACN,YAAY;CACZ,WAAW;CACX,SAAS;CACT,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAA2B,UAAU;CAAS;CAC/D,QAAQ;EACN,OAAO;GAAE,MAAM;GAAU,OAAO;EAAe;EAC/C,cAAc;GAAE,MAAM;GAAU,OAAO;EAAgB;CACzD;CACA,WACE;AACJ,CAAC;AAED,MAAa,kCAAkC,gBAA4C;CACzF,MAAM;CACN,YAAY;CACZ,WAAW;CACX,SAAS;CACT,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAA2B,UAAU;CAAS;CAC/D,QAAQ,EACN,OAAO;EAAE,MAAM;EAAU,OAAO;CAAe,EACjD;CACA,WACE;AACJ,CAAC;AAED,MAAa,iCAAiC,gBAA2C;CACvF,MAAM;CACN,YAAY;CACZ,WAAW;CACX,SAAS;CACT,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAA0B,UAAU;CAAU;CAC/D,QAAQ,EACN,gBAAgB;EAAE,MAAM;EAAU,OAAO;CAAkB,EAC7D;CACA,WACE;AACJ,CAAC;;;;;;;;;AAcD,SAAgB,oBAAoB,EAClC,iBAAiB,iBACjB,OAAO,WACP,UAAU,cACV,WACA,UACA,GAAG,SACwB;CAC3B,MAAM,QAAQ,gBAAgB,SAAS;CACvC,MAAM,WAAW,mBAAmB,YAAY;CAChD,MAAM,UAAU,OAAO,WAAW;CAClC,MAAM,EAAE,SAAS,gBAAgB,WAAW,mBAAmB;CAE/D,MAAM,aAAa,SAAS,cAAc;CAG1C,MAAM,UAAU,eAAe,cAAc;CAC7C,MAAM,UAAU,SAAS,WAAW;CAEpC,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;CACrC,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,CAAC,QAAQ,aAAa,SAAsD,CAAC,CAAC;CACpF,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAE5C,MAAM,cAAc,OAAO,KAAK;CAChC,MAAM,UAAU,OAAuB,IAAI;CAC3C,MAAM,gBAAgB,OAAO,KAAK;;;;;;CAOlC,gBAAgB;EACd,IAAI,cAAc,YAAY,SAAS;EACvC,cAAc,UAAU;EACxB,MAAM,WAAW,UAAU,sCAAsC;EACjE,QAAQ,SAAS,cAA2B,QAAQ,CAAC,EAAE,MAAM;CAC/D,GAAG,CAAC,OAAO,CAAC;CAEZ,MAAM,CAAC,gBAAgB,qBAAqB,SAAwB,IAAI;CACxE,MAAM,OAAO,kBAAkB,QAAQ,OAAO;CAC9C,MAAM,YACJ,CAAC,WACD,aAAa,OAAO,SAAS,IAAI,KACjC,oBAAoB,KAAK,MAAM,oBAAoB,cAAc,EAAE;CAErE,MAAM,eAAe,kBAAkB,WAAW,IAAI,GAAG,CAAC,CAAC;CAE3D,MAAM,SAAS,kBAAkB;EAC/B,WAAW,KAAK;EAChB,SAAS,EAAE;EACX,UAAU,CAAC,CAAC;EACZ,WAAW,KAAK;CAClB,GAAG,CAAC,CAAC;;CAGL,MAAM,cAAc,aAAa,UAAkB;EACjD,SAAS,KAAK;EACd,WAAW,YAAa,QAAQ,WAAW,IAAI,UAAU,CAAC,CAAE;EAC5D,WAAW,KAAK;CAClB,GAAG,CAAC,CAAC;CAEL,MAAM,SAAS,kBAAkB;EAC/B,IAAI,YAAY,SAAS;EACzB,MAAM,aAAa,UAAU,UAAU;EACvC,IAAI,CAAC,YAAY;EACjB,IAAI,CAAC,WAAW;GACd,WAAW,IAAI;GACf;EACF;EAEA,YAAY,UAAU;EACtB,WAAW,IAAI;EACf,UAAU,CAAC,CAAC;EACZ,WAAW,KAAK;EAIhB,gBAAqB,CAAC,CACnB,WAAW,WAAW;GAAE,YAAY;GAAO;EAAQ,CAAC,CAAC,CAAC,CACtD,MAAM,gBAAgB;GACrB,IAAI,YAAY,SAAS,GAAG;IAE1B,UAAU,WAAW;IACrB;GACF;GACA,WAAW,KAAK;GAChB,SAAS,EAAE;EACb,CAAC,CAAC,CACD,cAAc;GACb,YAAY,UAAU;GACtB,WAAW,KAAK;EAClB,CAAC;CACL,GAAG;EAAC;EAAU;EAAW;EAAO;CAAO,CAAC;CAExC,MAAM,QAAqC,eAClC;EACL;EACA,MAAM,SAAS,QAAQ;EACvB,OAAO,SAAS,SAAS;EACzB;EACA;EACA;EACA;EACA;EACA;EACA,UAAU;EACV;EACA;EACA;EACA;EACA;EACA,cAAc;CAChB,IACA;EACE;EACA,SAAS;EACT,SAAS;EACT;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CACF;CAEA,OACE,oBAAC,gCAAD;EAAuC;EACrC,UAAA,oBAAC,OAAD;GACE,GAAI;GACJ,KAAK;GACM;GACX,4BAAyB;GACzB,GAAK,UAAU,EAAE,oCAAoC,GAAG,IAAI,CAAC;GAC7D,GAAK,UAAU,EAAE,oCAAoC,GAAG,IAAI,CAAC;GAE5D;EACE,CAAA;CACyB,CAAA;AAEpC;AAEA,SAAgB,oBAAoB,EAAE,WAAW,UAAU,GAAG,SAAmC;CAC/F,MAAM,EAAE,YAAY,mBAAmB;CACvC,IAAI,SAAS,OAAO;CAEpB,OACE,oBAAC,OAAD;EAAK,GAAI;EAAkB;EAAW,iCAA8B;EACjE;CACE,CAAA;AAET;AAEA,SAAS,aAAa,SAA6B,MAAyC;CAC1F,IAAI,SAAS,KAAK,GAAG,OAAO;CAC5B,IAAI,MAAM,KAAK,GAAG,OAAO;CACzB,OAAO;AACT;AAEA,SAAgB,qBAAqB,EAAE,QAAQ,UAAU,WAAW,GAAG,SAAoC;CACzG,MAAM,EAAE,YAAY,MAAM,OAAO,YAAY,mBAAmB;CAChE,IAAI,WAAW,cAAc,MAAM,OAAO;CAE1C,MAAM,iBAAiB,aAAa,UAAU,QAAQ,MAAM;CAE5D,OACE,qBAAC,QAAD;EAAM,GAAI;EAAkB;EAAW,kCAA+B;EAAtE,UAAA;GACG,iBAAiB,oBAAC,QAAD;IAAM,yCAAsC;IAAI,UAAA;GAAqB,CAAA,IAAI;GAG1F,iBAAiB,MAAM;GACxB,oBAAC,QAAD;IAAM,8CAA2C;IAAI,UAAA;GAAiB,CAAA;GACrE,QAAQ,QAAQ,QAAQ;GACxB,OAAO,oBAAC,QAAD;IAAM,uCAAoC;IAAI,UAAA;GAAW,CAAA,IAAI;GACpE,QAAQ,QAAQ,OAAO;GACvB,QAAQ,oBAAC,QAAD;IAAM,wCAAqC;IAAI,UAAA;GAAY,CAAA,IAAI;EACpE;;AAEV;AAEA,SAAgB,qBAAqB,EAAE,WAAW,UAAU,GAAG,SAAoC;CACjG,MAAM,EAAE,YAAY,YAAY,mBAAmB;CACnD,IAAI,WAAW,cAAc,MAAM,OAAO;CAE1C,OACE,oBAAC,OAAD;EAAK,GAAI;EAAkB;EAAW,kCAA+B;EAClE;CACE,CAAA;AAET;AAEA,SAAgB,sBAAsB,EAAE,OAAO,UAAU,WAAW,UAAU,GAAG,SAAqC;CACpH,MAAM,EAAE,SAAS,iBAAiB,mBAAmB;CACrD,MAAM,gBAAgB,aAAa,UAAU,OAAO,KAAK;CAEzD,OACE,oBAAC,UAAD;EACE,GAAI;EACJ,MAAK;EACL,SAAS;EACT,iBAAe;EACJ;EACX,mCAAgC;EAE/B,UAAA,YAAY;CACP,CAAA;AAEZ;AAEA,SAAgB,oBAAoB,EAAE,WAAW,UAAU,GAAG,SAAmC;CAC/F,MAAM,EAAE,SAAS,WAAW,mBAAmB;CAC/C,IAAI,CAAC,SAAS,OAAO;CAErB,OACE,oBAAC,4BAAD;EAA4B,OAAO;EACjC,UAAA,oBAAC,QAAD;GACE,GAAI;GACO;GACX,iCAA8B;GAG9B,YAAY,UAAU;IACpB,IAAI,MAAM,QAAQ,WAAW,EAAE,MAAM,kBAAkB,mBAAmB;IAC1E,MAAM,eAAe;IACrB,OAAO;GACT;GACA,WAAW,UAAU;IACnB,MAAM,eAAe;IACrB,OAAO;GACT;GAEC;EACG,CAAA;CACoB,CAAA;AAEhC;AAEA,SAAgB,iCAAiC,EAC/C,WACA,MACA,OACA,aACA,kBACkD;CAClD,MAAM,EAAE,SAAS,OAAO,UAAU,QAAQ,SAAS,SAAS,iBAAiB,mBAAmB;CAChG,MAAM,UAAU,OAAuB,IAAI;CAC3C,MAAM,UAAU,OAAO,SAAS;CAChC,MAAM,gBAAgB,QAAQ,QAAQ,OAAO;CAE7C,gBAAgB;EACd,aAAa,aAAa;EAC1B,aAAa,aAAa,IAAI;CAChC,GAAG,CAAC,eAAe,YAAY,CAAC;;;;;CAMhC,gBAAgB;EACd,IAAI,CAAC,WAAW,SAAS;EACzB,QAAQ,SAAS,cAAgC,mCAAmC,CAAC,EAAE,MAAM;CAC/F,GAAG,CAAC,SAAS,OAAO,CAAC;CAErB,OACE,oBAAC,iBAAD;EACE,KAAK;EACM;EACX,gDAA6C;EAC7C,MAAM;EACC;EACM;EAGb,cAAc,UAAU,KAAA,IAAY;EACpC,OAAO;EACP,eAAe;EACf,SAAS,WAAW;EACpB,UAAU;CACX,CAAA;AAEL;AAEA,SAAgB,sBAAsB,EACpC,OACA,cACA,UACA,WACA,UACA,GAAG,SAC0B;CAC7B,MAAM,EAAE,WAAW,SAAS,WAAW,mBAAmB;CAC1D,MAAM,SAAS,WAAW,0BAA0B;CAEpD,MAAM,gBAAgB,aAAa,UAAU,OAAO,KAAK;CACzD,MAAM,uBAAuB,aAAa,UAAU,cAAc,YAAY;CAE9E,OACE,oBAAC,UAAD;EACE,GAAI;EAEJ,MAAM,SAAS,WAAW;EAC1B,SAAS,SAAS,MAAM,UAAU;EAClC,UAAU,CAAC,aAAa,MAAM;EAC9B,aAAW,WAAW,KAAA;EACX;EACX,mCAAgC;EAChC,GAAK,UAAU,EAAE,2CAA2C,GAAG,IAAI,CAAC;EAEnE,UAAA,aAAa,UAAW,wBAAwB,gBAAiB;CAC5D,CAAA;AAEZ;AAEA,SAAgB,sBAAsB,EAAE,OAAO,UAAU,WAAW,UAAU,GAAG,SAAqC;CACpH,MAAM,EAAE,QAAQ,YAAY,mBAAmB;CAC/C,MAAM,gBAAgB,aAAa,UAAU,OAAO,KAAK;CAEzD,OACE,oBAAC,UAAD;EACE,GAAI;EACJ,MAAK;EACL,SAAS;EACT,UAAU,WAAW,MAAM;EAChB;EACX,mCAAgC;EAE/B,UAAA,YAAY;CACP,CAAA;AAEZ;AAEA,SAAgB,qBAAqB,EAAE,gBAAgB,UAAU,WAAW,GAAG,SAAoC;CACjH,MAAM,EAAE,WAAW,mBAAmB;CACtC,IAAI,OAAO,WAAW,GAAG,OAAO;CAIhC,MAAM,WAAW,aAAa,UAAU,gBAAgB,cAAc;CAEtE,OACE,oBAAC,OAAD;EAAK,GAAI;EAAO,MAAK;EAAmB;EAAW,kCAA+B;EAC/E,UAAA,YAAY,OAAO,EAAE,EAAE;CACrB,CAAA;AAET;AAMA,MAAa,kBAAkB;CAC7B,MAAM;CACN,MAAM;CACN,OAAO;CACP,OAAO;CACP,QAAQ;CACR,MAAM;CACN,mBAAmB;CACnB,QAAQ;CACR,QAAQ;CACR,OAAO;AACT"}
@@ -57,6 +57,8 @@ export type CartItemQuantityProps = Omit<ComponentPropsWithRef<'div'>, 'defaultV
57
57
  };
58
58
  export type CartItemQuantityValueProps = ComponentPropsWithRef<'span'>;
59
59
  export type CartItemRemoveProps = ComponentPropsWithRef<'button'> & {
60
+ /** Title of the toast raised after a removal that returns no errors. */
61
+ removedMessage?: string;
60
62
  /** Injected by the compiler from `actions: { cart: ['removeItem'] }`; the cart write surface. */
61
63
  $actions?: ComponentActions;
62
64
  };
@@ -82,7 +84,7 @@ export declare const CartItemAvailableDefinition: ComponentDefinition<import("re
82
84
  export declare const CartItemUnavailableDefinition: ComponentDefinition<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>>;
83
85
  export declare const CartItemGiftDefinition: ComponentDefinition<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>>;
84
86
  export declare function CartItemRoot({ $data: $dataProp, className, children, ...props }: CartItemRootProps): import("react/jsx-runtime").JSX.Element;
85
- export declare function CartItemImage({ className, objectFit, ...props }: CartItemImageProps): import("react/jsx-runtime").JSX.Element | null;
87
+ export declare function CartItemImage({ className, objectFit, ref, onError, ...props }: CartItemImageProps): import("react/jsx-runtime").JSX.Element;
86
88
  export declare function CartItemProductLink({ className, children, ...props }: Readonly<CartItemProductLinkProps>): string | number | bigint | boolean | import("react/jsx-runtime").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null | undefined;
87
89
  export declare function CartItemName({ className, children, ...props }: CartItemNameProps): import("react/jsx-runtime").JSX.Element;
88
90
  export declare function CartItemVariant({ className, children, ...props }: CartItemVariantProps): import("react/jsx-runtime").JSX.Element | null;
@@ -95,7 +97,7 @@ export declare function CartItemOwnTotal({ className, children, ...props }: Cart
95
97
  export declare function CartItemAggregateTotal({ className, children, ...props }: CartItemAggregateTotalProps): import("react/jsx-runtime").JSX.Element;
96
98
  export declare function CartItemQuantity({ className, children, min, max, step, removeOnDecrement, validationMessage, $data: $dataProp, $actions: $actionsProp, ...props }: CartItemQuantityProps): import("react/jsx-runtime").JSX.Element | null;
97
99
  export declare function CartItemQuantityValue({ className, children, ...props }: CartItemQuantityValueProps): import("react/jsx-runtime").JSX.Element;
98
- export declare function CartItemRemove({ className, children, onClick, $actions: $actionsProp, ...props }: CartItemRemoveProps): import("react/jsx-runtime").JSX.Element | null;
100
+ export declare function CartItemRemove({ className, children, onClick, disabled, removedMessage, $actions: $actionsProp, ...props }: CartItemRemoveProps): import("react/jsx-runtime").JSX.Element | null;
99
101
  export declare function CartItemAvailable({ className, children, ...props }: CartItemAvailableProps): import("react/jsx-runtime").JSX.Element | null;
100
102
  export declare function CartItemUnavailable({ className, children, ...props }: CartItemUnavailableProps): import("react/jsx-runtime").JSX.Element | null;
101
103
  export declare function CartItemGift({ className, children, ...props }: CartItemGiftProps): import("react/jsx-runtime").JSX.Element | null;
@@ -7,8 +7,10 @@ import { graphql } from "../../sdk/generated/gql.js";
7
7
  import { useActionsOverride } from "../../sdk/actions/actions-override-context.js";
8
8
  import { useDataOverride } from "../../sdk/data/data-override-context.js";
9
9
  import { NavigationLink } from "../../particles/navigation-link/navigation-link.js";
10
- import { formatPrice } from "../cart/cart.js";
11
- import { createContext, useContext, useState } from "react";
10
+ import { loggableErrorName } from "../../utils/loggable-error-name.js";
11
+ import { useOptionalToast } from "../../particles/toast/toast.js";
12
+ import { formatPrice, isFiniteAmount } from "../cart/cart.js";
13
+ import { createContext, useCallback, useContext, useState } from "react";
12
14
  import { jsx, jsxs } from "react/jsx-runtime";
13
15
  import { NumberField } from "@base-ui/react/number-field";
14
16
  //#region src/matter/cart-item/cart-item.tsx
@@ -18,6 +20,7 @@ const fragment = graphql(`
18
20
  items {
19
21
  __typename
20
22
  id
23
+ lineId
21
24
  quantity
22
25
  price {
23
26
  amount
@@ -121,7 +124,7 @@ const CartItemImageDefinition = defineComponent({
121
124
  default: "contain",
122
125
  label: "Object Fit"
123
126
  } },
124
- guideline: "Renders the product image from the cart item. Must be inside CartItemRoot."
127
+ guideline: "Renders the product image from the cart item. When the line has no image, or the image fails to load, it renders a decorative placeholder span in the same slot (same className and style, hidden from assistive technology) carrying data-fs-cart-item-image-broken for a swatch to style. The placeholder keeps the inline-level display of the image (inline-block) so width and height apply, and adds no color, border or background. Must be inside CartItemRoot."
125
128
  });
126
129
  const CartItemProductLinkDefinition = defineComponent({
127
130
  name: "CartItemProductLink",
@@ -133,7 +136,7 @@ const CartItemProductLinkDefinition = defineComponent({
133
136
  category: "commerce"
134
137
  },
135
138
  schema: {},
136
- guideline: "Links the cart line image to its product page, using the product URL the line already carries: place it inside CartItemRoot wrapping CartItemImage. Whether a line links is decided by placing this element, so there is no prop to toggle it — a checkout line simply omits it. Renders its children unwrapped when the line has no image or no usable product URL, so it never leaves an empty or unsafe link. Navigation is same-tab, and the leaf adds no fields to the page query. Must be inside CartItemRoot. See cart-item.md."
139
+ guideline: "Links the cart line image to its product page, using the product URL the line already carries: place it inside CartItemRoot wrapping CartItemImage. Whether a line links is decided by placing this element, so there is no prop to toggle it — a checkout line simply omits it. Renders its children unwrapped when the line has no image or no usable product URL, so it never leaves an empty or unsafe link. The link names itself with an aria-label taken from the image alt text (or the variant name), so it stays named when the image shows the placeholder; an aria-label passed to the link wins. Navigation is same-tab, and the leaf adds no fields to the page query. Must be inside CartItemRoot. See cart-item.md."
137
140
  });
138
141
  const CartItemNameDefinition = defineComponent({
139
142
  name: "CartItemName",
@@ -312,9 +315,13 @@ const CartItemRemoveDefinition = defineComponent({
312
315
  label: "Cart Item Remove",
313
316
  category: "commerce"
314
317
  },
315
- schema: {},
318
+ schema: { removedMessage: {
319
+ type: "string",
320
+ default: "Item removed",
321
+ label: "Removed message"
322
+ } },
316
323
  actions: { cart: ["removeItem"] },
317
- guideline: "Button that removes the current line item from the cart. Must be inside CartItemRoot. Also composes inside CartItemUnavailable as a remove-on-alert control. See cart-item.md."
324
+ guideline: "Button that removes the current line item from the cart. Disabled while the removal is in flight; when the removal returns no errors it raises a toast titled removedMessage (needs a ToastProvider with a ToastList in the layout; with none, no toast shows and removal still works). Must be inside CartItemRoot. Also composes inside CartItemUnavailable as a remove-on-alert control. See cart-item.md."
318
325
  });
319
326
  const CartItemAvailableDefinition = defineComponent({
320
327
  name: "CartItemAvailable",
@@ -365,12 +372,39 @@ function CartItemRoot({ $data: $dataProp, className, children, ...props }) {
365
372
  })
366
373
  });
367
374
  }
368
- function CartItemImage({ className, objectFit = "contain", ...props }) {
375
+ function CartItemImage({ className, objectFit = "contain", ref, onError, ...props }) {
369
376
  const item = useCartItem();
370
377
  const image = item.variant.image;
371
- if (!image) return null;
378
+ const url = image?.url ?? "";
379
+ const [failedUrl, setFailedUrl] = useState(null);
380
+ if (failedUrl !== null && failedUrl !== url) setFailedUrl(null);
381
+ const setImageRef = useCallback((node) => {
382
+ if (node?.complete && node.naturalWidth === 0) setFailedUrl(url);
383
+ if (typeof ref === "function") return ref(node);
384
+ if (ref) {
385
+ ref.current = node;
386
+ return () => {
387
+ ref.current = null;
388
+ };
389
+ }
390
+ }, [ref, url]);
391
+ if (!image || !url.trim() || failedUrl === url) return /* @__PURE__ */ jsx("span", {
392
+ className,
393
+ style: {
394
+ display: "inline-block",
395
+ ...props.style
396
+ },
397
+ "aria-hidden": "true",
398
+ "data-fs-cart-item-image": "",
399
+ "data-fs-cart-item-image-broken": ""
400
+ });
401
+ function handleError(event) {
402
+ setFailedUrl(url);
403
+ onError?.(event);
404
+ }
372
405
  return /* @__PURE__ */ jsx("img", {
373
406
  ...props,
407
+ ref: setImageRef,
374
408
  src: image.url,
375
409
  alt: image.altText ?? item.variant.name,
376
410
  className,
@@ -378,6 +412,7 @@ function CartItemImage({ className, objectFit = "contain", ...props }) {
378
412
  objectFit,
379
413
  ...props.style
380
414
  },
415
+ onError: handleError,
381
416
  "data-fs-cart-item-image": "",
382
417
  "data-fs-image": ""
383
418
  });
@@ -390,8 +425,10 @@ function CartItemProductLink({ className, children, ...props }) {
390
425
  return children;
391
426
  }
392
427
  const image = item.variant.image;
393
- if (!image || !(image.altText ?? item.variant.name ?? "").trim()) return children;
428
+ const name = (image?.altText ?? item.variant.name ?? "").trim();
429
+ if (!image || !name) return children;
394
430
  return /* @__PURE__ */ jsx(NavigationLink, {
431
+ "aria-label": name,
395
432
  ...props,
396
433
  href: destination.href,
397
434
  className,
@@ -437,19 +474,21 @@ function CartItemPrice({ className, children, ...props }) {
437
474
  className,
438
475
  "data-fs-cart-item-price": "",
439
476
  "data-fs-text": "",
440
- children: children ?? formatPrice(item.sellingPrice.amount, locale, currency)
477
+ children: children ?? formatPrice(Number(item.sellingPrice.amount), locale, currency)
441
478
  });
442
479
  }
443
480
  function CartItemListPrice({ className, children, ...props }) {
444
481
  const item = useCartItem();
445
482
  const { locale, currency } = useStorefrontContext();
446
- if (Number(item.listPrice.amount) <= Number(item.sellingPrice.amount)) return null;
483
+ if (!isFiniteAmount(item.listPrice.amount) || !isFiniteAmount(item.sellingPrice.amount)) return null;
484
+ const listPrice = Number(item.listPrice.amount);
485
+ if (listPrice <= Number(item.sellingPrice.amount)) return null;
447
486
  return /* @__PURE__ */ jsx("span", {
448
487
  ...props,
449
488
  className,
450
489
  "data-fs-cart-item-list-price": "",
451
490
  "data-fs-text": "",
452
- children: children ?? formatPrice(item.listPrice.amount, locale, currency)
491
+ children: children ?? formatPrice(listPrice, locale, currency)
453
492
  });
454
493
  }
455
494
  function CartItemDiscountPercentage({ template = "-{percentage}%", className, children, ...props }) {
@@ -474,7 +513,7 @@ function CartItemLineTotal({ className, children, ...props }) {
474
513
  ...props,
475
514
  className,
476
515
  "data-fs-cart-item-line-total": "",
477
- children: children ?? formatPrice(item.sellingPrice.amount * item.quantity, locale, currency)
516
+ children: children ?? formatPrice(Number(item.sellingPrice.amount) * item.quantity, locale, currency)
478
517
  });
479
518
  }
480
519
  function CartItemOwnTotal({ className, children, ...props }) {
@@ -614,18 +653,38 @@ function CartItemQuantityValue({ className, children, ...props }) {
614
653
  children: children ?? String(item.quantity)
615
654
  });
616
655
  }
617
- function CartItemRemove({ className, children, onClick, $actions: $actionsProp, ...props }) {
656
+ function CartItemRemove({ className, children, onClick, disabled, removedMessage = "Item removed", $actions: $actionsProp, ...props }) {
618
657
  const $actions = useActionsOverride($actionsProp);
619
658
  const item = useCartItem();
659
+ const toast = useOptionalToast();
660
+ const [pending, setPending] = useState(false);
620
661
  if (item.gift) return null;
621
- function handleClick(e) {
622
- $actions?.cart?.removeItem?.({ id: item.id });
662
+ async function handleClick(e) {
663
+ if (pending) return;
664
+ const removeItem = $actions?.cart?.removeItem;
665
+ if (typeof removeItem !== "function") {
666
+ onClick?.(e);
667
+ return;
668
+ }
669
+ setPending(true);
623
670
  onClick?.(e);
671
+ try {
672
+ const userErrors = await removeItem({ id: item.id });
673
+ if (Array.isArray(userErrors) && userErrors.length === 0) toast?.add({
674
+ title: removedMessage,
675
+ type: "success"
676
+ });
677
+ } catch (error) {
678
+ console.error("[CartItemRemove] removeItem failed", loggableErrorName(error));
679
+ } finally {
680
+ setPending(false);
681
+ }
624
682
  }
625
683
  return /* @__PURE__ */ jsx("button", {
626
684
  ...props,
627
685
  type: "button",
628
686
  className,
687
+ disabled: pending || disabled,
629
688
  onClick: handleClick,
630
689
  "data-fs-cart-item-remove": "",
631
690
  "data-fs-button": "",
@@ -1 +1 @@
1
- {"version":3,"file":"cart-item.js","names":["BaseNumberField"],"sources":["../../../src/matter/cart-item/cart-item.tsx"],"sourcesContent":["'use client';\n\nimport { NumberField as BaseNumberField } from '@base-ui/react/number-field';\nimport { defineComponent } from '@vtex/fs-core';\nimport { type ComponentPropsWithRef, createContext, type ReactNode, useContext, useState } from 'react';\nimport { NavigationLink, type NavigationLinkProps } from '../../particles/navigation-link/navigation-link.js';\nimport { useActionsOverride } from '../../sdk/actions/actions-override-context.js';\nimport type { ComponentActions } from '../../sdk/actions/types.js';\nimport type { CartItemEntity } from '../../sdk/cart/operations.js';\nimport { useStorefrontContext } from '../../sdk/context/storefront-context.js';\nimport { useDataOverride } from '../../sdk/data/data-override-context.js';\nimport { graphql } from '../../sdk/generated/gql.js';\nimport { resolveSafeHref } from '../../utils/safe-href.js';\nimport { formatPrice } from '../cart/cart.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type CartItemRootProps = ComponentPropsWithRef<'div'> & {\n /**\n * Per-line slice delivered by the `reactive-list-item` runtime (`ReactiveListItemDataProvider`) —\n * the resolved `CartItem` entity read from the normalized cache via `useFragment(CartItem:<id>)`.\n * Injected through the iteration context in a compiled page; the `$data` prop is retained for test\n * injection.\n */\n $data?: CartItemEntity | null;\n};\n\nexport type CartItemImageProps = ComponentPropsWithRef<'img'> & {\n objectFit?: 'contain' | 'cover' | 'fill' | 'none';\n};\n\n// The destination is the line's own product page, so `href` is not authorable. `$content` goes with it\n// because the leaf declares no content model: a product URL is commerce data, not copy. `target` goes\n// too, so the same-tab rule is enforced by the type rather than merely absent from the schema — an\n// image-only link cannot carry the \"(opens in new tab)\" cue a `_blank` destination needs.\nexport type CartItemProductLinkProps = Omit<NavigationLinkProps, 'href' | '$content' | 'target'>;\n\nexport type CartItemNameProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemVariantProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemSkuProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemPriceProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemListPriceProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemDiscountPercentageProps = ComponentPropsWithRef<'span'> & {\n template?: string;\n};\n\nexport type CartItemLineTotalProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemOwnTotalProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemAggregateTotalProps = ComponentPropsWithRef<'span'>;\n\n/**\n * Per-line slice delivered by the `reactive-list-item` runtime: the value the wrapper publishes\n * through `DataOverrideContext`, read via `useDataOverride`. Only the field the stepper converts with\n * is typed; the resolved cache slice carries the full fragment selection.\n */\nexport type CartItemQuantitySlice = {\n variant: { unitMultiplier: number } | null;\n};\n\nexport type CartItemQuantityProps = Omit<ComponentPropsWithRef<'div'>, 'defaultValue' | 'value'> & {\n /** Minimum, in purchasable units — not in the unit shown to the shopper. */\n min?: number;\n /** Maximum, in purchasable units. Unbounded when unset. */\n max?: number;\n /** Step, in purchasable units: one step moves the display by one unit multiplier. */\n step?: number;\n removeOnDecrement?: boolean;\n /** Snap-notice template; `{value}` is replaced with the corrected displayed amount. */\n validationMessage?: string;\n children?: ReactNode;\n /** Per-line slice from the reactive-list-item runtime; injected/overridden by the wrapper. */\n $data?: CartItemQuantitySlice | null;\n /** Injected by the compiler from `actions: { cart: [...] }`; the cart write surface. */\n $actions?: ComponentActions;\n};\n\nexport type CartItemQuantityValueProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemRemoveProps = ComponentPropsWithRef<'button'> & {\n /** Injected by the compiler from `actions: { cart: ['removeItem'] }`; the cart write surface. */\n $actions?: ComponentActions;\n};\n\nexport type CartItemAvailableProps = ComponentPropsWithRef<'div'>;\n\nexport type CartItemUnavailableProps = ComponentPropsWithRef<'div'>;\n\nexport type CartItemGiftProps = ComponentPropsWithRef<'span'>;\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment — typed via @graphql-codegen client-preset (T7)\n//\n// CartItemRoot owns the core-scalar slice of each cart line (ADR-0012 Option B). Fields are inlined\n// (no `...` spread) so the compiler's list-field derivation finds the iterated `items` list by its\n// direct child field and re-roots this fragment to `CartItemRootData_CartItem on CartItem` for the\n// per-line `useFragment` subscription. `variant.availability` stays here — it is a cross-cutting core\n// scalar (CartCheckoutGuard, CartItemAvailable/Unavailable read it), not an optional display leaf.\n// `gift` stays here for the same reason: CartItemGift, CartItemRemove's suppression, and the row's\n// own state hook all read it, so one selection serves three consumers.\n//\n// Every field selected here MUST also be selected by the mutation fragment (`CartItemFragment` in\n// sdk/cart/operations.ts). ReactiveListItemDataProvider resolves `complete ? data : fallbackData`,\n// so a field present here but missing there makes every line incomplete after a cart write and\n// degrades the whole list to the cart-level slice, which carries no product or prices.\n// ---------------------------------------------------------------------------\n\nconst fragment = graphql(`\n fragment CartItemRootData on Query {\n cart {\n items {\n __typename\n id\n quantity\n price {\n amount\n currencyCode\n }\n listPrice {\n amount\n currencyCode\n }\n sellingPrice {\n amount\n currencyCode\n }\n gift\n ownTotal {\n ...MoneyFragment\n }\n total {\n ...MoneyFragment\n }\n variant {\n __typename\n variantId\n sku\n name\n image {\n url\n altText\n }\n availability\n }\n product {\n __typename\n productId\n title\n detailUrl\n categories {\n categoryId\n name\n }\n brand {\n brandId\n name\n }\n }\n }\n }\n }\n`);\n\n// ADR-0011: the unit multiplier is optional per-line data owned by the stepper that converts with\n// it, not by the core root — a cart line showing a read-only quantity never fetches it. Fields are\n// inlined (no `...` spread) so the derived per-item fragment is self-contained for `useFragment`, and\n// it drills the identical cart { items { ... } } path as the other per-item matters so it merges\n// un-aliased (ADR-0012).\nconst cartItemQuantityFragment = graphql(`\n fragment CartItemQuantityData on Query {\n cart {\n items {\n __typename\n id\n variant {\n __typename\n unitMultiplier\n }\n }\n }\n }\n`);\n\n// ---------------------------------------------------------------------------\n// Cart item context — provided by CartItemRoot with its resolved per-line slice\n// ---------------------------------------------------------------------------\n\nconst CartItemContext = createContext<CartItemEntity | null>(null);\n\nfunction useCartItem(): CartItemEntity {\n const item = useContext(CartItemContext);\n if (!item) {\n throw new Error('Cart item components must be used inside a <CartItemRoot> subtree.');\n }\n return item;\n}\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const CartItemRootDefinition = defineComponent<CartItemRootProps>({\n name: 'CartItemRoot',\n importPath: '@vtex/faststore/components',\n component: CartItemRoot,\n fragment,\n fetchStrategy: 'reactive-list-item',\n // Placement: requires CartList's `cart-item` iteration context and provides `cart-item-data` to the\n // core particles below. The two tokens differ, so it does not self-satisfy — the context-constraints\n // walk checks `requiresContext` before pushing this node's own `providesContext`.\n requiresContext: 'cart-item',\n providesContext: 'cart-item-data',\n editor: { label: 'Cart Item Root', category: 'commerce' },\n schema: {},\n guideline:\n 'Per-line data provider for the cart line-item template: place it directly inside CartList, wrapping the CartItem* particles (CartItemImage, CartItemName, CartItemPrice, the CartItemQuantity stepper, CartItemRemove, …). It owns the core per-item fields and delivers each line its own slice, so the core particles must sit inside it. Must be inside CartList. See cart-item.md.',\n});\n\nexport const CartItemImageDefinition = defineComponent<CartItemImageProps>({\n name: 'CartItemImage',\n importPath: '@vtex/faststore/components',\n component: CartItemImage,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Image', category: 'commerce' },\n schema: {\n objectFit: { type: ['contain', 'cover', 'fill', 'none'] as const, default: 'contain', label: 'Object Fit' },\n },\n guideline: 'Renders the product image from the cart item. Must be inside CartItemRoot.',\n});\n\nexport const CartItemProductLinkDefinition = defineComponent<CartItemProductLinkProps>({\n name: 'CartItemProductLink',\n importPath: '@vtex/faststore/components',\n component: CartItemProductLink,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Product Link', category: 'commerce' },\n schema: {},\n guideline:\n 'Links the cart line image to its product page, using the product URL the line already carries: place it inside CartItemRoot wrapping CartItemImage. Whether a line links is decided by placing this element, so there is no prop to toggle it — a checkout line simply omits it. Renders its children unwrapped when the line has no image or no usable product URL, so it never leaves an empty or unsafe link. Navigation is same-tab, and the leaf adds no fields to the page query. Must be inside CartItemRoot. See cart-item.md.',\n});\n\nexport const CartItemNameDefinition = defineComponent<CartItemNameProps>({\n name: 'CartItemName',\n importPath: '@vtex/faststore/components',\n component: CartItemName,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Name', category: 'commerce' },\n schema: {},\n guideline: 'Renders the product name from the cart item. Must be inside CartItemRoot.',\n});\n\nexport const CartItemVariantDefinition = defineComponent<CartItemVariantProps>({\n name: 'CartItemVariant',\n importPath: '@vtex/faststore/components',\n component: CartItemVariant,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Variant', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the selected variant name of the cart item (e.g. \"Midnight Black\"); renders null when the variant has no name. Use as a subtitle under CartItemName, typically alongside CartItemSku. Must be inside CartItemRoot.',\n});\n\nexport const CartItemSkuDefinition = defineComponent<CartItemSkuProps>({\n name: 'CartItemSku',\n importPath: '@vtex/faststore/components',\n component: CartItemSku,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item SKU', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the SKU code of the cart item variant (e.g. \"WNC-400X\"); renders null when the variant has no SKU. Use as a subtitle under CartItemName, typically alongside CartItemVariant. Must be inside CartItemRoot.',\n});\n\nexport const CartItemPriceDefinition = defineComponent<CartItemPriceProps>({\n name: 'CartItemPrice',\n importPath: '@vtex/faststore/components',\n component: CartItemPrice,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Price', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the selling price of the cart item, formatted from the active storefront context. Must be inside CartItemRoot.',\n});\n\nexport const CartItemListPriceDefinition = defineComponent<CartItemListPriceProps>({\n name: 'CartItemListPrice',\n importPath: '@vtex/faststore/components',\n component: CartItemListPrice,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item List Price', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the original list price of the cart item (strikethrough); renders null when no discount is active. Use alongside CartItemPrice to show promotional pricing. Must be inside CartItemRoot.',\n});\n\nexport const CartItemDiscountPercentageDefinition = defineComponent<CartItemDiscountPercentageProps>({\n name: 'CartItemDiscountPercentage',\n importPath: '@vtex/faststore/components',\n component: CartItemDiscountPercentage,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Discount %', category: 'commerce' },\n schema: {\n template: { type: 'string', default: '-{percentage}%', label: 'Template' },\n },\n guideline:\n 'Per-line discount badge showing the integer listPrice-vs-sellingPrice percentage (e.g. \"-20%\"); use the template prop to customize the format ({percentage} is the integer percent). Renders null when the line has no discount (sellingPrice >= listPrice), so it only appears on promotional lines. Place alongside CartItemPrice/CartItemListPrice. Must be inside CartItemRoot.',\n});\n\nexport const CartItemLineTotalDefinition = defineComponent<CartItemLineTotalProps>({\n name: 'CartItemLineTotal',\n importPath: '@vtex/faststore/components',\n component: CartItemLineTotal,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Line Total', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the per-line total (selling price × quantity) formatted from the active storefront context. Must be inside CartItemRoot.',\n});\n\nexport const CartItemOwnTotalDefinition = defineComponent<CartItemOwnTotalProps>({\n name: 'CartItemOwnTotal',\n importPath: '@vtex/faststore/components',\n component: CartItemOwnTotal,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Own Total', category: 'commerce' },\n schema: {},\n guideline:\n \"Renders the line's own total (excludes bundle/composition children), formatted from the active storefront context. Equals CartItemAggregateTotal on lines with no compositions. Must be inside CartItemRoot. See cart-item.md.\",\n});\n\nexport const CartItemAggregateTotalDefinition = defineComponent<CartItemAggregateTotalProps>({\n name: 'CartItemAggregateTotal',\n importPath: '@vtex/faststore/components',\n component: CartItemAggregateTotal,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Aggregate Total', category: 'commerce' },\n schema: {},\n guideline:\n \"Renders the line's aggregate total including all nested composition children, formatted from the active storefront context — the price the shopper actually pays for a bundled line. Equals CartItemOwnTotal on lines with no compositions. Must be inside CartItemRoot. See cart-item.md.\",\n});\n\nexport const CartItemQuantityDefinition = defineComponent<CartItemQuantityProps>({\n name: 'CartItemQuantity',\n importPath: '@vtex/faststore/components',\n component: CartItemQuantity,\n fragment: cartItemQuantityFragment,\n fetchStrategy: 'reactive-list-item',\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Quantity', category: 'commerce' },\n schema: {\n min: { type: 'number', default: 1, label: 'Min' },\n max: { type: 'number', label: 'Max' },\n step: { type: 'number', default: 1, label: 'Step' },\n removeOnDecrement: { type: 'boolean', default: false, label: 'Remove On Decrement' },\n validationMessage: { type: 'string', default: 'Quantity adjusted to {value}.', label: 'Validation Message' },\n },\n actions: { cart: ['increaseQuantity', 'decreaseQuantity', 'removeItem'] },\n guideline:\n \"Quantity stepper for a cart item, shown in the unit the merchant sells in: the displayed amount is the line's quantity times its unit multiplier, so a SKU sold in 500 g portions reads 1.5 rather than 3. min, max, and step are configured in purchasable units and converted for display, so one template serves SKUs with different multipliers. A typed amount that is out of bounds or off the multiplier grid snaps to the nearest valid value on commit and renders validationMessage. Set removeOnDecrement=true to remove the item when quantity reaches the minimum. Renders nothing on a gift line — a gift's quantity is promotion-controlled and cannot be stepped or removed, so use CartItemQuantityValue to show a gift's fixed quantity. Must be inside CartItemRoot. See cart-item.md.\",\n});\n\nexport const CartItemQuantityValueDefinition = defineComponent<CartItemQuantityValueProps>({\n name: 'CartItemQuantityValue',\n importPath: '@vtex/faststore/components',\n component: CartItemQuantityValue,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Quantity Value', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the read-only quantity count of the cart item as text (e.g. a \"Qty\" badge). Distinct from the editable CartItemQuantity stepper. Must be inside CartItemRoot.',\n});\n\nexport const CartItemRemoveDefinition = defineComponent<CartItemRemoveProps>({\n name: 'CartItemRemove',\n importPath: '@vtex/faststore/components',\n component: CartItemRemove,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Remove', category: 'commerce' },\n schema: {},\n actions: { cart: ['removeItem'] },\n guideline:\n 'Button that removes the current line item from the cart. Must be inside CartItemRoot. Also composes inside CartItemUnavailable as a remove-on-alert control. See cart-item.md.',\n});\n\nexport const CartItemAvailableDefinition = defineComponent<CartItemAvailableProps>({\n name: 'CartItemAvailable',\n importPath: '@vtex/faststore/components',\n component: CartItemAvailable,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Available', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders its children only when the current cart item is in stock (availability AVAILABLE). Wrap price, quantity stepper, and line total so they hide for unavailable items. Must be inside CartItemRoot.',\n});\n\nexport const CartItemUnavailableDefinition = defineComponent<CartItemUnavailableProps>({\n name: 'CartItemUnavailable',\n importPath: '@vtex/faststore/components',\n component: CartItemUnavailable,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Unavailable', category: 'commerce' },\n schema: {},\n guideline:\n 'Status badge shown only when the current cart item is NOT available. Renders a built-in English label per state (OUT_OF_STOCK → \"Out of stock\", UNAVAILABLE → \"Cannot be delivered\", otherwise \"Unavailable\"); pass children to override the text — children REPLACE the label, so a remove-on-alert composition nests an explicit label plus a CartItemRemove. Emits data-fs-cart-item-unavailable-state for per-state styling. Must be inside CartItemRoot. See cart-item.md.',\n});\n\nexport const CartItemGiftDefinition = defineComponent<CartItemGiftProps>({\n name: 'CartItemGift',\n importPath: '@vtex/faststore/components',\n component: CartItemGift,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Gift', category: 'commerce' },\n schema: {},\n guideline:\n 'Free marker for a promotional gift line: renders only when the current cart item is a gift, showing a built-in \"Free\" label; pass children to override the text — children REPLACE the label. Place it beside CartItemPrice/CartItemListPrice, whose struck-through list price it complements. It is an inline marker, not a wrapper for a gift-only subtree. On a gift line CartItemRemove suppresses itself and CartItemRoot marks the row with data-fs-cart-item-gift-line, so neither needs composing differently. Must be inside CartItemRoot. See cart-item.md.',\n});\n\n// ---------------------------------------------------------------------------\n// Implementations\n// ---------------------------------------------------------------------------\n\nexport function CartItemRoot({ $data: $dataProp, className, children, ...props }: CartItemRootProps) {\n // Read the resolved per-line slice through the DataOverrideContext seam the reactive-list-item\n // wrapper publishes (or via $data in tests), then re-provide it to the core particles — the same\n // \"read my slice, then provide React context to descendants\" shape CartRoot uses for the cart.\n const slice = useDataOverride<CartItemEntity | null | undefined>($dataProp) ?? null;\n\n return (\n <CartItemContext value={slice}>\n <div\n {...props}\n className={className}\n data-fs-cart-item-root=\"\"\n {...(slice?.gift ? { 'data-fs-cart-item-gift-line': '' } : {})}\n >\n {children}\n </div>\n </CartItemContext>\n );\n}\n\nexport function CartItemImage({ className, objectFit = 'contain', ...props }: CartItemImageProps) {\n const item = useCartItem();\n const image = item.variant.image;\n\n if (!image) return null;\n\n return (\n // biome-ignore lint/performance/noImgElement: Cart item image URL is dynamic; matter stays Next-agnostic.\n <img\n {...props}\n src={image.url}\n alt={image.altText ?? item.variant.name}\n className={className}\n style={{ objectFit, ...props.style }}\n data-fs-cart-item-image=\"\"\n data-fs-image=\"\"\n />\n );\n}\n\nexport function CartItemProductLink({ className, children, ...props }: Readonly<CartItemProductLinkProps>) {\n const item = useCartItem();\n // The particle gates only CMS-authored destinations, so a platform URL arriving on the `href` prop\n // is this leaf's own to validate. `detailUrl` is `String!`, which excludes null but not an empty or\n // hostile value. Either way the leaf degrades to a passthrough rather than self-hiding the way the\n // data-bearing leaves do: returning null would take the image with it.\n const destination = resolveSafeHref(item.product.detailUrl);\n\n if (!destination.safe) {\n // A blank URL is ordinary, so it stays quiet. Anything else was actively refused, and the reason\n // is logged the way the particle logs a refused authored destination — never the value itself.\n if (destination.reason !== 'blank') {\n console.warn('[CartItemProductLink] product URL rejected', { reason: destination.reason });\n }\n return children;\n }\n\n // The anchor is named by the wrapped image's alt text (`altText ?? variant.name`). With no image\n // there is nothing to render or name; with a blank alt the anchor would be a focus stop with no\n // accessible name. Either way, degrade to the unwrapped children rather than emit an unnamed link.\n const image = item.variant.image;\n if (!image || !(image.altText ?? item.variant.name ?? '').trim()) return children;\n\n return (\n <NavigationLink {...props} href={destination.href} className={className} data-fs-cart-item-product-link=\"\">\n {children}\n </NavigationLink>\n );\n}\n\nexport function CartItemName({ className, children, ...props }: CartItemNameProps) {\n const item = useCartItem();\n\n return (\n <span {...props} className={className} data-fs-cart-item-name=\"\" data-fs-text=\"\">\n {children ?? item.product.title}\n </span>\n );\n}\n\nexport function CartItemVariant({ className, children, ...props }: CartItemVariantProps) {\n const item = useCartItem();\n const { name } = item.variant;\n if (!name) return null;\n\n return (\n <span {...props} className={className} data-fs-cart-item-variant=\"\">\n {children ?? name}\n </span>\n );\n}\n\nexport function CartItemSku({ className, children, ...props }: CartItemSkuProps) {\n const item = useCartItem();\n const { sku } = item.variant;\n if (!sku) return null;\n\n return (\n <span {...props} className={className} data-fs-cart-item-sku=\"\">\n {children ?? sku}\n </span>\n );\n}\n\nexport function CartItemPrice({ className, children, ...props }: CartItemPriceProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n return (\n <span {...props} className={className} data-fs-cart-item-price=\"\" data-fs-text=\"\">\n {children ?? formatPrice(item.sellingPrice.amount, locale, currency)}\n </span>\n );\n}\n\nexport function CartItemListPrice({ className, children, ...props }: CartItemListPriceProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n // `Decimal` arrives as a string (\"100.00\"), whatever the generated type says, and strings compare\n // character by character: \"100.00\" <= \"90.00\" holds. Compare the numbers, as the discount leaf does.\n if (Number(item.listPrice.amount) <= Number(item.sellingPrice.amount)) {\n return null;\n }\n\n return (\n <span {...props} className={className} data-fs-cart-item-list-price=\"\" data-fs-text=\"\">\n {children ?? formatPrice(item.listPrice.amount, locale, currency)}\n </span>\n );\n}\n\nexport function CartItemDiscountPercentage({\n template = '-{percentage}%',\n className,\n children,\n ...props\n}: CartItemDiscountPercentageProps) {\n const item = useCartItem();\n\n const listPrice = Number(item.listPrice.amount);\n const sellingPrice = Number(item.sellingPrice.amount);\n\n if (listPrice <= sellingPrice) {\n return null;\n }\n\n const percent = Math.round(((listPrice - sellingPrice) / listPrice) * 100);\n const text = template.replaceAll('{percentage}', String(percent));\n\n return (\n <span {...props} className={className} data-fs-cart-item-discount-percentage=\"\" data-fs-text=\"\">\n {children ?? text}\n </span>\n );\n}\n\nexport function CartItemLineTotal({ className, children, ...props }: CartItemLineTotalProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n return (\n <span {...props} className={className} data-fs-cart-item-line-total=\"\">\n {children ?? formatPrice(item.sellingPrice.amount * item.quantity, locale, currency)}\n </span>\n );\n}\n\nexport function CartItemOwnTotal({ className, children, ...props }: CartItemOwnTotalProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n return (\n <span {...props} className={className} data-fs-cart-item-own-total=\"\" data-fs-text=\"\">\n {children ?? formatPrice(Number(item.ownTotal.amount), locale, currency)}\n </span>\n );\n}\n\nexport function CartItemAggregateTotal({ className, children, ...props }: CartItemAggregateTotalProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n return (\n <span {...props} className={className} data-fs-cart-item-aggregate-total=\"\" data-fs-text=\"\">\n {children ?? formatPrice(Number(item.total.amount), locale, currency)}\n </span>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Quantity stepper — the unit multiplier converts between the purchasable count the order form and\n// the cart mutations use, and the measure the merchant sells in. `1` is the identity, so a SKU sold\n// by the unit behaves exactly as it did before the multiplier was read.\n// ---------------------------------------------------------------------------\n\n/** Why a committed amount had to be corrected; drives both the notice and its styling hook. */\ntype QuantityAdjustment = { reason: 'min' | 'max' | 'multiple'; value: number };\n\nconst MAX_DISPLAY_DECIMALS = 6;\n\n/**\n * A multiplier the platform cannot sell in — zero, negative, or non-finite — is treated as `1`, which\n * is also the fallback when the optional slice is absent (the stepper composed with no own provider).\n */\nfunction effectiveMultiplier(slice: CartItemQuantitySlice | null | undefined): number {\n const multiplier = slice?.variant?.unitMultiplier ?? 1;\n return Number.isFinite(multiplier) && multiplier > 0 ? multiplier : 1;\n}\n\n/**\n * Precision of the multiplier, so a displayed amount never carries binary-representation noise.\n * Derived through `toFixed` rather than `String`: `String` switches to exponential notation below\n * 1e-6, where it carries no decimal point and would report zero places, yielding a step of 0 that no\n * control can move. The normal-path precisions (0.5, 0.1, 0.333) are covered by tests; the\n * exponential branch is not, since it needs a multiplier no SKU can have.\n */\nfunction decimalPlacesOf(multiplier: number): number {\n const [, decimals = ''] = multiplier.toFixed(MAX_DISPLAY_DECIMALS).split('.');\n // Trailing zeros carry no precision; strip them with a linear scan rather than a `/0+$/` regex,\n // which SonarQube flags as super-linear (harmless here — the input is a bounded toFixed string).\n let end = decimals.length;\n while (end > 0 && decimals[end - 1] === '0') end -= 1;\n return end;\n}\n\nfunction roundTo(value: number, decimals: number): number {\n return Number(value.toFixed(decimals));\n}\n\nfunction resolveAdjustment(\n entered: number,\n snappedDisplay: number,\n multiplier: number,\n minDisplay: number,\n maxDisplay: number | undefined,\n): QuantityAdjustment | null {\n if (maxDisplay != null && entered > maxDisplay) return { reason: 'max', value: snappedDisplay };\n if (entered < minDisplay) return { reason: 'min', value: snappedDisplay };\n // Tolerance is relative to the multiplier, so recovering the count through a division never reads\n // as an off-grid entry.\n if (Math.abs(entered - snappedDisplay) > multiplier * 1e-6) return { reason: 'multiple', value: snappedDisplay };\n return null;\n}\n\nexport function CartItemQuantity({\n className,\n children,\n min = 1,\n max,\n step,\n removeOnDecrement,\n validationMessage = 'Quantity adjusted to {value}.',\n $data: $dataProp,\n $actions: $actionsProp,\n ...props\n}: CartItemQuantityProps) {\n const item = useCartItem();\n const slice = useDataOverride<CartItemQuantitySlice | null | undefined>($dataProp);\n const $actions = useActionsOverride($actionsProp);\n const [adjustment, setAdjustment] = useState<QuantityAdjustment | null>(null);\n\n // A gift line has no interactive quantity control, the same way CartItemRemove has no remove\n // control: a promotion-controlled quantity the shopper cannot validly change, and — with\n // removeOnDecrement — a path that would otherwise dispatch removeItem on a gift, discarding a\n // benefit T6 already sealed off from the remove button. Suppressing the stepper closes that path\n // under any composition. A store showing a gift's fixed quantity composes CartItemQuantityValue.\n if (item.gift) return null;\n\n const cart = $actions?.cart;\n\n const multiplier = effectiveMultiplier(slice);\n const decimals = decimalPlacesOf(multiplier);\n // min, max, and step are counts of purchasable units, and a cart line holds a whole number of\n // them, so a fractional bound has no valid value to land on. Left unrounded, a fractional `min` or\n // `max` becomes the clamp result and leaks into the delta — `decreaseQuantity({ by: 2.5 })` — which\n // the cart mutations reject. Rounding all three keeps every reachable value, and every dispatched\n // delta, an integer.\n const stepUnits = Math.max(1, Math.round(step ?? 1));\n const minUnits = Math.round(min);\n const maxUnits = max == null ? undefined : Math.round(max);\n const displayStep = roundTo(stepUnits * multiplier, decimals);\n const minDisplay = roundTo(minUnits * multiplier, decimals);\n const maxDisplay = maxUnits == null ? undefined : roundTo(maxUnits * multiplier, decimals);\n\n // Commit rather than change: `onValueChange` fires per parseable keystroke, so enforcing there\n // writes to the cart for every digit typed and snaps a value the shopper has not finished entering.\n function handleValueCommitted(value: number | null) {\n if (value == null) return;\n\n if (removeOnDecrement && value < minDisplay) {\n setAdjustment(null);\n void cart?.removeItem?.({ id: item.id });\n return;\n }\n\n const snapped = Math.min(Math.max(Math.round(value / multiplier), minUnits), maxUnits ?? Number.POSITIVE_INFINITY);\n const snappedDisplay = roundTo(snapped * multiplier, decimals);\n setAdjustment(resolveAdjustment(value, snappedDisplay, multiplier, minDisplay, maxDisplay));\n\n const delta = snapped - item.quantity;\n if (delta > 0) {\n void cart?.increaseQuantity?.({ id: item.id, by: delta });\n } else if (delta < 0) {\n void cart?.decreaseQuantity?.({ id: item.id, by: -delta });\n }\n }\n\n const effectiveMin = removeOnDecrement ? 0 : minDisplay;\n const computedClassName = removeOnDecrement ? (className ? `group ${className}` : 'group') : className;\n\n return (\n <BaseNumberField.Root\n {...props}\n value={roundTo(item.quantity * multiplier, decimals)}\n onValueCommitted={handleValueCommitted}\n // A shopper editing the field again makes the previous correction stale, so it is dropped as\n // soon as they type. Step and scrub reasons are excluded: those land on the grid by\n // construction and never raise a notice, so clearing on them would be dead code.\n onValueChange={(_value, details) => {\n if (details.reason === 'input-change' || details.reason === 'input-paste' || details.reason === 'input-clear') {\n setAdjustment(null);\n }\n }}\n min={effectiveMin}\n max={maxDisplay}\n step={displayStep}\n smallStep={displayStep}\n largeStep={roundTo(displayStep * 10, decimals)}\n snapOnStep\n // Base UI clamps text entry to min/max on its own, which would correct an out-of-bounds amount\n // before this component ever saw it — and a correction nobody can observe cannot be explained.\n allowOutOfRange\n className={computedClassName}\n data-fs-cart-item-quantity=\"\"\n {...(multiplier === 1 ? {} : { 'data-fs-cart-item-quantity-multiplier': String(multiplier) })}\n {...(adjustment\n ? {\n 'data-fs-cart-item-quantity-adjusted': '',\n 'data-fs-cart-item-quantity-adjusted-reason': adjustment.reason,\n }\n : {})}\n {...(removeOnDecrement && item.quantity <= minUnits ? { 'data-fs-cart-item-at-min': '' } : {})}\n >\n {children}\n {adjustment ? (\n <div role=\"alert\" data-fs-cart-item-quantity-validation=\"\">\n {validationMessage.replaceAll('{value}', String(adjustment.value))}\n </div>\n ) : null}\n </BaseNumberField.Root>\n );\n}\n\nexport function CartItemQuantityValue({ className, children, ...props }: CartItemQuantityValueProps) {\n const item = useCartItem();\n\n return (\n <span {...props} className={className} data-fs-cart-item-quantity-value=\"\">\n {children ?? String(item.quantity)}\n </span>\n );\n}\n\nexport function CartItemRemove({\n className,\n children,\n onClick,\n $actions: $actionsProp,\n ...props\n}: CartItemRemoveProps) {\n const $actions = useActionsOverride($actionsProp);\n const item = useCartItem();\n\n // A gift line has no remove control: the promotion granted the item, and this phase does not let a\n // shopper discard it. Suppressed here rather than by composition, because CartList renders one\n // template for every line — an author cannot leave the leaf out of gift rows alone. Covers the\n // remove-on-alert composition too; an unavailable gift is recovered by the cart-level\n // CartRemoveUnavailable batch and by server-side cart validation.\n if (item.gift) return null;\n\n function handleClick(e: React.MouseEvent<HTMLButtonElement>) {\n void $actions?.cart?.removeItem?.({ id: item.id });\n onClick?.(e);\n }\n\n return (\n <button\n {...props}\n type=\"button\"\n className={className}\n onClick={handleClick}\n data-fs-cart-item-remove=\"\"\n data-fs-button=\"\"\n >\n {children}\n </button>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Availability gates — read `variant.availability` from the core slice (F4).\n// UNKNOWN and any future non-available state fall back to the generic \"Unavailable\".\n// ---------------------------------------------------------------------------\n\nconst UNAVAILABLE_LABELS: Record<string, string> = {\n OUT_OF_STOCK: 'Out of stock',\n UNAVAILABLE: 'Cannot be delivered',\n};\n\nfunction unavailableLabel(availability: string): string {\n return UNAVAILABLE_LABELS[availability] ?? 'Unavailable';\n}\n\nexport function CartItemAvailable({ className, children, ...props }: CartItemAvailableProps) {\n const item = useCartItem();\n if (item.variant.availability !== 'AVAILABLE') return null;\n\n return (\n <div {...props} className={className} data-fs-cart-item-available=\"\">\n {children}\n </div>\n );\n}\n\nexport function CartItemUnavailable({ className, children, ...props }: CartItemUnavailableProps) {\n const item = useCartItem();\n const { availability } = item.variant;\n if (availability === 'AVAILABLE') return null;\n\n return (\n <div\n {...props}\n className={className}\n data-fs-cart-item-unavailable=\"\"\n data-fs-cart-item-unavailable-state={availability.toLowerCase()}\n >\n {children ?? unavailableLabel(availability)}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// F9 — gift marker. Inline, so `children` override the label rather than nesting a subtree; a gift\n// row's other needs are met by the row's own `data-fs-cart-item-gift-line` hook. \"Free\" is\n// operational copy, so it ships as a built-in default like the unavailability labels above, and moves\n// to the localization catalog rather than to a content model.\n// ---------------------------------------------------------------------------\n\nexport function CartItemGift({ className, children, ...props }: CartItemGiftProps) {\n const item = useCartItem();\n if (!item.gift) return null;\n\n return (\n <span {...props} className={className} data-fs-cart-item-gift=\"\" data-fs-text=\"\">\n {children ?? 'Free'}\n </span>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cart-item.js","names":["BaseNumberField"],"sources":["../../../src/matter/cart-item/cart-item.tsx"],"sourcesContent":["'use client';\n\nimport { NumberField as BaseNumberField } from '@base-ui/react/number-field';\nimport { defineComponent } from '@vtex/fs-core';\nimport {\n type ComponentPropsWithRef,\n createContext,\n type ReactNode,\n type SyntheticEvent,\n useCallback,\n useContext,\n useState,\n} from 'react';\nimport { NavigationLink, type NavigationLinkProps } from '../../particles/navigation-link/navigation-link.js';\nimport { useOptionalToast } from '../../particles/toast/toast.js';\nimport { useActionsOverride } from '../../sdk/actions/actions-override-context.js';\nimport type { ComponentActions } from '../../sdk/actions/types.js';\nimport type { CartItemEntity } from '../../sdk/cart/operations.js';\nimport { useStorefrontContext } from '../../sdk/context/storefront-context.js';\nimport { useDataOverride } from '../../sdk/data/data-override-context.js';\nimport { graphql } from '../../sdk/generated/gql.js';\nimport { loggableErrorName } from '../../utils/loggable-error-name.js';\nimport { resolveSafeHref } from '../../utils/safe-href.js';\nimport { formatPrice, isFiniteAmount } from '../cart/cart.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type CartItemRootProps = ComponentPropsWithRef<'div'> & {\n /**\n * Per-line slice delivered by the `reactive-list-item` runtime (`ReactiveListItemDataProvider`) —\n * the resolved `CartItem` entity read from the normalized cache via `useFragment(CartItem:<id>)`.\n * Injected through the iteration context in a compiled page; the `$data` prop is retained for test\n * injection.\n */\n $data?: CartItemEntity | null;\n};\n\nexport type CartItemImageProps = ComponentPropsWithRef<'img'> & {\n objectFit?: 'contain' | 'cover' | 'fill' | 'none';\n};\n\n// The destination is the line's own product page, so `href` is not authorable. `$content` goes with it\n// because the leaf declares no content model: a product URL is commerce data, not copy. `target` goes\n// too, so the same-tab rule is enforced by the type rather than merely absent from the schema — an\n// image-only link cannot carry the \"(opens in new tab)\" cue a `_blank` destination needs.\nexport type CartItemProductLinkProps = Omit<NavigationLinkProps, 'href' | '$content' | 'target'>;\n\nexport type CartItemNameProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemVariantProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemSkuProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemPriceProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemListPriceProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemDiscountPercentageProps = ComponentPropsWithRef<'span'> & {\n template?: string;\n};\n\nexport type CartItemLineTotalProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemOwnTotalProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemAggregateTotalProps = ComponentPropsWithRef<'span'>;\n\n/**\n * Per-line slice delivered by the `reactive-list-item` runtime: the value the wrapper publishes\n * through `DataOverrideContext`, read via `useDataOverride`. Only the field the stepper converts with\n * is typed; the resolved cache slice carries the full fragment selection.\n */\nexport type CartItemQuantitySlice = {\n variant: { unitMultiplier: number } | null;\n};\n\nexport type CartItemQuantityProps = Omit<ComponentPropsWithRef<'div'>, 'defaultValue' | 'value'> & {\n /** Minimum, in purchasable units — not in the unit shown to the shopper. */\n min?: number;\n /** Maximum, in purchasable units. Unbounded when unset. */\n max?: number;\n /** Step, in purchasable units: one step moves the display by one unit multiplier. */\n step?: number;\n removeOnDecrement?: boolean;\n /** Snap-notice template; `{value}` is replaced with the corrected displayed amount. */\n validationMessage?: string;\n children?: ReactNode;\n /** Per-line slice from the reactive-list-item runtime; injected/overridden by the wrapper. */\n $data?: CartItemQuantitySlice | null;\n /** Injected by the compiler from `actions: { cart: [...] }`; the cart write surface. */\n $actions?: ComponentActions;\n};\n\nexport type CartItemQuantityValueProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemRemoveProps = ComponentPropsWithRef<'button'> & {\n /** Title of the toast raised after a removal that returns no errors. */\n removedMessage?: string;\n /** Injected by the compiler from `actions: { cart: ['removeItem'] }`; the cart write surface. */\n $actions?: ComponentActions;\n};\n\nexport type CartItemAvailableProps = ComponentPropsWithRef<'div'>;\n\nexport type CartItemUnavailableProps = ComponentPropsWithRef<'div'>;\n\nexport type CartItemGiftProps = ComponentPropsWithRef<'span'>;\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment — typed via @graphql-codegen client-preset (T7)\n//\n// CartItemRoot owns the core-scalar slice of each cart line (ADR-0012 Option B). Fields are inlined\n// (no `...` spread) so the compiler's list-field derivation finds the iterated `items` list by its\n// direct child field and re-roots this fragment to `CartItemRootData_CartItem on CartItem` for the\n// per-line `useFragment` subscription. `variant.availability` stays here — it is a cross-cutting core\n// scalar (CartCheckoutGuard, CartItemAvailable/Unavailable read it), not an optional display leaf.\n// `gift` stays here for the same reason: CartItemGift, CartItemRemove's suppression, and the row's\n// own state hook all read it, so one selection serves three consumers.\n//\n// Every field selected here MUST also be selected by the mutation fragment (`CartItemFragment` in\n// sdk/cart/operations.ts). ReactiveListItemDataProvider resolves `complete ? data : fallbackData`,\n// so a field present here but missing there makes every line incomplete after a cart write and\n// degrades the whole list to the cart-level slice, which carries no product or prices.\n// ---------------------------------------------------------------------------\n\nconst fragment = graphql(`\n fragment CartItemRootData on Query {\n cart {\n items {\n __typename\n id\n lineId\n quantity\n price {\n amount\n currencyCode\n }\n listPrice {\n amount\n currencyCode\n }\n sellingPrice {\n amount\n currencyCode\n }\n gift\n ownTotal {\n ...MoneyFragment\n }\n total {\n ...MoneyFragment\n }\n variant {\n __typename\n variantId\n sku\n name\n image {\n url\n altText\n }\n availability\n }\n product {\n __typename\n productId\n title\n detailUrl\n categories {\n categoryId\n name\n }\n brand {\n brandId\n name\n }\n }\n }\n }\n }\n`);\n\n// ADR-0011: the unit multiplier is optional per-line data owned by the stepper that converts with\n// it, not by the core root — a cart line showing a read-only quantity never fetches it. Fields are\n// inlined (no `...` spread) so the derived per-item fragment is self-contained for `useFragment`, and\n// it drills the identical cart { items { ... } } path as the other per-item matters so it merges\n// un-aliased (ADR-0012).\nconst cartItemQuantityFragment = graphql(`\n fragment CartItemQuantityData on Query {\n cart {\n items {\n __typename\n id\n variant {\n __typename\n unitMultiplier\n }\n }\n }\n }\n`);\n\n// ---------------------------------------------------------------------------\n// Cart item context — provided by CartItemRoot with its resolved per-line slice\n// ---------------------------------------------------------------------------\n\nconst CartItemContext = createContext<CartItemEntity | null>(null);\n\nfunction useCartItem(): CartItemEntity {\n const item = useContext(CartItemContext);\n if (!item) {\n throw new Error('Cart item components must be used inside a <CartItemRoot> subtree.');\n }\n return item;\n}\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const CartItemRootDefinition = defineComponent<CartItemRootProps>({\n name: 'CartItemRoot',\n importPath: '@vtex/faststore/components',\n component: CartItemRoot,\n fragment,\n fetchStrategy: 'reactive-list-item',\n // Placement: requires CartList's `cart-item` iteration context and provides `cart-item-data` to the\n // core particles below. The two tokens differ, so it does not self-satisfy — the context-constraints\n // walk checks `requiresContext` before pushing this node's own `providesContext`.\n requiresContext: 'cart-item',\n providesContext: 'cart-item-data',\n editor: { label: 'Cart Item Root', category: 'commerce' },\n schema: {},\n guideline:\n 'Per-line data provider for the cart line-item template: place it directly inside CartList, wrapping the CartItem* particles (CartItemImage, CartItemName, CartItemPrice, the CartItemQuantity stepper, CartItemRemove, …). It owns the core per-item fields and delivers each line its own slice, so the core particles must sit inside it. Must be inside CartList. See cart-item.md.',\n});\n\nexport const CartItemImageDefinition = defineComponent<CartItemImageProps>({\n name: 'CartItemImage',\n importPath: '@vtex/faststore/components',\n component: CartItemImage,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Image', category: 'commerce' },\n schema: {\n objectFit: { type: ['contain', 'cover', 'fill', 'none'] as const, default: 'contain', label: 'Object Fit' },\n },\n guideline:\n 'Renders the product image from the cart item. When the line has no image, or the image fails to load, it renders a decorative placeholder span in the same slot (same className and style, hidden from assistive technology) carrying data-fs-cart-item-image-broken for a swatch to style. The placeholder keeps the inline-level display of the image (inline-block) so width and height apply, and adds no color, border or background. Must be inside CartItemRoot.',\n});\n\nexport const CartItemProductLinkDefinition = defineComponent<CartItemProductLinkProps>({\n name: 'CartItemProductLink',\n importPath: '@vtex/faststore/components',\n component: CartItemProductLink,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Product Link', category: 'commerce' },\n schema: {},\n guideline:\n 'Links the cart line image to its product page, using the product URL the line already carries: place it inside CartItemRoot wrapping CartItemImage. Whether a line links is decided by placing this element, so there is no prop to toggle it — a checkout line simply omits it. Renders its children unwrapped when the line has no image or no usable product URL, so it never leaves an empty or unsafe link. The link names itself with an aria-label taken from the image alt text (or the variant name), so it stays named when the image shows the placeholder; an aria-label passed to the link wins. Navigation is same-tab, and the leaf adds no fields to the page query. Must be inside CartItemRoot. See cart-item.md.',\n});\n\nexport const CartItemNameDefinition = defineComponent<CartItemNameProps>({\n name: 'CartItemName',\n importPath: '@vtex/faststore/components',\n component: CartItemName,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Name', category: 'commerce' },\n schema: {},\n guideline: 'Renders the product name from the cart item. Must be inside CartItemRoot.',\n});\n\nexport const CartItemVariantDefinition = defineComponent<CartItemVariantProps>({\n name: 'CartItemVariant',\n importPath: '@vtex/faststore/components',\n component: CartItemVariant,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Variant', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the selected variant name of the cart item (e.g. \"Midnight Black\"); renders null when the variant has no name. Use as a subtitle under CartItemName, typically alongside CartItemSku. Must be inside CartItemRoot.',\n});\n\nexport const CartItemSkuDefinition = defineComponent<CartItemSkuProps>({\n name: 'CartItemSku',\n importPath: '@vtex/faststore/components',\n component: CartItemSku,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item SKU', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the SKU code of the cart item variant (e.g. \"WNC-400X\"); renders null when the variant has no SKU. Use as a subtitle under CartItemName, typically alongside CartItemVariant. Must be inside CartItemRoot.',\n});\n\nexport const CartItemPriceDefinition = defineComponent<CartItemPriceProps>({\n name: 'CartItemPrice',\n importPath: '@vtex/faststore/components',\n component: CartItemPrice,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Price', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the selling price of the cart item, formatted from the active storefront context. Must be inside CartItemRoot.',\n});\n\nexport const CartItemListPriceDefinition = defineComponent<CartItemListPriceProps>({\n name: 'CartItemListPrice',\n importPath: '@vtex/faststore/components',\n component: CartItemListPrice,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item List Price', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the original list price of the cart item (strikethrough); renders null when no discount is active. Use alongside CartItemPrice to show promotional pricing. Must be inside CartItemRoot.',\n});\n\nexport const CartItemDiscountPercentageDefinition = defineComponent<CartItemDiscountPercentageProps>({\n name: 'CartItemDiscountPercentage',\n importPath: '@vtex/faststore/components',\n component: CartItemDiscountPercentage,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Discount %', category: 'commerce' },\n schema: {\n template: { type: 'string', default: '-{percentage}%', label: 'Template' },\n },\n guideline:\n 'Per-line discount badge showing the integer listPrice-vs-sellingPrice percentage (e.g. \"-20%\"); use the template prop to customize the format ({percentage} is the integer percent). Renders null when the line has no discount (sellingPrice >= listPrice), so it only appears on promotional lines. Place alongside CartItemPrice/CartItemListPrice. Must be inside CartItemRoot.',\n});\n\nexport const CartItemLineTotalDefinition = defineComponent<CartItemLineTotalProps>({\n name: 'CartItemLineTotal',\n importPath: '@vtex/faststore/components',\n component: CartItemLineTotal,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Line Total', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the per-line total (selling price × quantity) formatted from the active storefront context. Must be inside CartItemRoot.',\n});\n\nexport const CartItemOwnTotalDefinition = defineComponent<CartItemOwnTotalProps>({\n name: 'CartItemOwnTotal',\n importPath: '@vtex/faststore/components',\n component: CartItemOwnTotal,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Own Total', category: 'commerce' },\n schema: {},\n guideline:\n \"Renders the line's own total (excludes bundle/composition children), formatted from the active storefront context. Equals CartItemAggregateTotal on lines with no compositions. Must be inside CartItemRoot. See cart-item.md.\",\n});\n\nexport const CartItemAggregateTotalDefinition = defineComponent<CartItemAggregateTotalProps>({\n name: 'CartItemAggregateTotal',\n importPath: '@vtex/faststore/components',\n component: CartItemAggregateTotal,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Aggregate Total', category: 'commerce' },\n schema: {},\n guideline:\n \"Renders the line's aggregate total including all nested composition children, formatted from the active storefront context — the price the shopper actually pays for a bundled line. Equals CartItemOwnTotal on lines with no compositions. Must be inside CartItemRoot. See cart-item.md.\",\n});\n\nexport const CartItemQuantityDefinition = defineComponent<CartItemQuantityProps>({\n name: 'CartItemQuantity',\n importPath: '@vtex/faststore/components',\n component: CartItemQuantity,\n fragment: cartItemQuantityFragment,\n fetchStrategy: 'reactive-list-item',\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Quantity', category: 'commerce' },\n schema: {\n min: { type: 'number', default: 1, label: 'Min' },\n max: { type: 'number', label: 'Max' },\n step: { type: 'number', default: 1, label: 'Step' },\n removeOnDecrement: { type: 'boolean', default: false, label: 'Remove On Decrement' },\n validationMessage: { type: 'string', default: 'Quantity adjusted to {value}.', label: 'Validation Message' },\n },\n actions: { cart: ['increaseQuantity', 'decreaseQuantity', 'removeItem'] },\n guideline:\n \"Quantity stepper for a cart item, shown in the unit the merchant sells in: the displayed amount is the line's quantity times its unit multiplier, so a SKU sold in 500 g portions reads 1.5 rather than 3. min, max, and step are configured in purchasable units and converted for display, so one template serves SKUs with different multipliers. A typed amount that is out of bounds or off the multiplier grid snaps to the nearest valid value on commit and renders validationMessage. Set removeOnDecrement=true to remove the item when quantity reaches the minimum. Renders nothing on a gift line — a gift's quantity is promotion-controlled and cannot be stepped or removed, so use CartItemQuantityValue to show a gift's fixed quantity. Must be inside CartItemRoot. See cart-item.md.\",\n});\n\nexport const CartItemQuantityValueDefinition = defineComponent<CartItemQuantityValueProps>({\n name: 'CartItemQuantityValue',\n importPath: '@vtex/faststore/components',\n component: CartItemQuantityValue,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Quantity Value', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the read-only quantity count of the cart item as text (e.g. a \"Qty\" badge). Distinct from the editable CartItemQuantity stepper. Must be inside CartItemRoot.',\n});\n\nexport const CartItemRemoveDefinition = defineComponent<CartItemRemoveProps>({\n name: 'CartItemRemove',\n importPath: '@vtex/faststore/components',\n component: CartItemRemove,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Remove', category: 'commerce' },\n schema: {\n removedMessage: { type: 'string', default: 'Item removed', label: 'Removed message' },\n },\n actions: { cart: ['removeItem'] },\n guideline:\n 'Button that removes the current line item from the cart. Disabled while the removal is in flight; when the removal returns no errors it raises a toast titled removedMessage (needs a ToastProvider with a ToastList in the layout; with none, no toast shows and removal still works). Must be inside CartItemRoot. Also composes inside CartItemUnavailable as a remove-on-alert control. See cart-item.md.',\n});\n\nexport const CartItemAvailableDefinition = defineComponent<CartItemAvailableProps>({\n name: 'CartItemAvailable',\n importPath: '@vtex/faststore/components',\n component: CartItemAvailable,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Available', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders its children only when the current cart item is in stock (availability AVAILABLE). Wrap price, quantity stepper, and line total so they hide for unavailable items. Must be inside CartItemRoot.',\n});\n\nexport const CartItemUnavailableDefinition = defineComponent<CartItemUnavailableProps>({\n name: 'CartItemUnavailable',\n importPath: '@vtex/faststore/components',\n component: CartItemUnavailable,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Unavailable', category: 'commerce' },\n schema: {},\n guideline:\n 'Status badge shown only when the current cart item is NOT available. Renders a built-in English label per state (OUT_OF_STOCK → \"Out of stock\", UNAVAILABLE → \"Cannot be delivered\", otherwise \"Unavailable\"); pass children to override the text — children REPLACE the label, so a remove-on-alert composition nests an explicit label plus a CartItemRemove. Emits data-fs-cart-item-unavailable-state for per-state styling. Must be inside CartItemRoot. See cart-item.md.',\n});\n\nexport const CartItemGiftDefinition = defineComponent<CartItemGiftProps>({\n name: 'CartItemGift',\n importPath: '@vtex/faststore/components',\n component: CartItemGift,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Gift', category: 'commerce' },\n schema: {},\n guideline:\n 'Free marker for a promotional gift line: renders only when the current cart item is a gift, showing a built-in \"Free\" label; pass children to override the text — children REPLACE the label. Place it beside CartItemPrice/CartItemListPrice, whose struck-through list price it complements. It is an inline marker, not a wrapper for a gift-only subtree. On a gift line CartItemRemove suppresses itself and CartItemRoot marks the row with data-fs-cart-item-gift-line, so neither needs composing differently. Must be inside CartItemRoot. See cart-item.md.',\n});\n\n// ---------------------------------------------------------------------------\n// Implementations\n// ---------------------------------------------------------------------------\n\nexport function CartItemRoot({ $data: $dataProp, className, children, ...props }: CartItemRootProps) {\n // Read the resolved per-line slice through the DataOverrideContext seam the reactive-list-item\n // wrapper publishes (or via $data in tests), then re-provide it to the core particles — the same\n // \"read my slice, then provide React context to descendants\" shape CartRoot uses for the cart.\n const slice = useDataOverride<CartItemEntity | null | undefined>($dataProp) ?? null;\n\n return (\n <CartItemContext value={slice}>\n <div\n {...props}\n className={className}\n data-fs-cart-item-root=\"\"\n {...(slice?.gift ? { 'data-fs-cart-item-gift-line': '' } : {})}\n >\n {children}\n </div>\n </CartItemContext>\n );\n}\n\nexport function CartItemImage({ className, objectFit = 'contain', ref, onError, ...props }: CartItemImageProps) {\n const item = useCartItem();\n const image = item.variant.image;\n const url = image?.url ?? '';\n const [failedUrl, setFailedUrl] = useState<string | null>(null);\n\n // A failure is remembered only for the URL that failed, so a new URL gets a fresh attempt.\n if (failedUrl !== null && failedUrl !== url) setFailedUrl(null);\n\n // An image whose request failed before hydration never delivers `error` to React, so a settled\n // image with no pixels is treated as failed at mount.\n const setImageRef = useCallback(\n (node: HTMLImageElement | null) => {\n if (node?.complete && node.naturalWidth === 0) setFailedUrl(url);\n if (typeof ref === 'function') return ref(node);\n if (ref) {\n ref.current = node;\n return () => {\n ref.current = null;\n };\n }\n },\n [ref, url],\n );\n\n if (!image || !url.trim() || failedUrl === url) {\n return (\n <span\n className={className}\n style={{ display: 'inline-block', ...props.style }}\n aria-hidden=\"true\"\n data-fs-cart-item-image=\"\"\n data-fs-cart-item-image-broken=\"\"\n />\n );\n }\n\n function handleError(event: SyntheticEvent<HTMLImageElement, Event>) {\n setFailedUrl(url);\n onError?.(event);\n }\n\n return (\n // biome-ignore lint/performance/noImgElement: Cart item image URL is dynamic; matter stays Next-agnostic.\n <img // react-doctor-disable-line nextjs-no-img-element -- matter stays Next-agnostic; the host decides how to optimize the image\n {...props}\n ref={setImageRef}\n src={image.url}\n alt={image.altText ?? item.variant.name}\n className={className}\n style={{ objectFit, ...props.style }}\n onError={handleError}\n data-fs-cart-item-image=\"\"\n data-fs-image=\"\"\n />\n );\n}\n\nexport function CartItemProductLink({ className, children, ...props }: Readonly<CartItemProductLinkProps>) {\n const item = useCartItem();\n // The particle gates only CMS-authored destinations, so a platform URL arriving on the `href` prop\n // is this leaf's own to validate. `detailUrl` is `String!`, which excludes null but not an empty or\n // hostile value. Either way the leaf degrades to a passthrough rather than self-hiding the way the\n // data-bearing leaves do: returning null would take the image with it.\n const destination = resolveSafeHref(item.product.detailUrl);\n\n if (!destination.safe) {\n // A blank URL is ordinary, so it stays quiet. Anything else was actively refused, and the reason\n // is logged the way the particle logs a refused authored destination — never the value itself.\n if (destination.reason !== 'blank') {\n console.warn('[CartItemProductLink] product URL rejected', { reason: destination.reason });\n }\n return children;\n }\n\n // The anchor names itself with the image's alt text (`altText ?? variant.name`) because the image\n // can turn into a hidden placeholder. With no image there is nothing to render or name; with a blank\n // name the anchor would be a focus stop with no accessible name. Either way, degrade to the\n // unwrapped children rather than emit an unnamed link.\n const image = item.variant.image;\n const name = (image?.altText ?? item.variant.name ?? '').trim();\n if (!image || !name) return children;\n\n return (\n <NavigationLink\n aria-label={name}\n {...props}\n href={destination.href}\n className={className}\n data-fs-cart-item-product-link=\"\"\n >\n {children}\n </NavigationLink>\n );\n}\n\nexport function CartItemName({ className, children, ...props }: CartItemNameProps) {\n const item = useCartItem();\n\n return (\n <span {...props} className={className} data-fs-cart-item-name=\"\" data-fs-text=\"\">\n {children ?? item.product.title}\n </span>\n );\n}\n\nexport function CartItemVariant({ className, children, ...props }: CartItemVariantProps) {\n const item = useCartItem();\n const { name } = item.variant;\n if (!name) return null;\n\n return (\n <span {...props} className={className} data-fs-cart-item-variant=\"\">\n {children ?? name}\n </span>\n );\n}\n\nexport function CartItemSku({ className, children, ...props }: CartItemSkuProps) {\n const item = useCartItem();\n const { sku } = item.variant;\n if (!sku) return null;\n\n return (\n <span {...props} className={className} data-fs-cart-item-sku=\"\">\n {children ?? sku}\n </span>\n );\n}\n\nexport function CartItemPrice({ className, children, ...props }: CartItemPriceProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n return (\n <span {...props} className={className} data-fs-cart-item-price=\"\" data-fs-text=\"\">\n {children ?? formatPrice(Number(item.sellingPrice.amount), locale, currency)}\n </span>\n );\n}\n\nexport function CartItemListPrice({ className, children, ...props }: CartItemListPriceProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n if (!isFiniteAmount(item.listPrice.amount) || !isFiniteAmount(item.sellingPrice.amount)) {\n return null;\n }\n\n // `Decimal` arrives as a string (\"100.00\"), whatever the generated type says, and strings compare\n // character by character: \"100.00\" <= \"90.00\" holds. Compare the numbers, as the discount leaf does.\n const listPrice = Number(item.listPrice.amount);\n const sellingPrice = Number(item.sellingPrice.amount);\n\n if (listPrice <= sellingPrice) {\n return null;\n }\n\n return (\n <span {...props} className={className} data-fs-cart-item-list-price=\"\" data-fs-text=\"\">\n {children ?? formatPrice(listPrice, locale, currency)}\n </span>\n );\n}\n\nexport function CartItemDiscountPercentage({\n template = '-{percentage}%',\n className,\n children,\n ...props\n}: CartItemDiscountPercentageProps) {\n const item = useCartItem();\n\n const listPrice = Number(item.listPrice.amount);\n const sellingPrice = Number(item.sellingPrice.amount);\n\n if (listPrice <= sellingPrice) {\n return null;\n }\n\n const percent = Math.round(((listPrice - sellingPrice) / listPrice) * 100);\n const text = template.replaceAll('{percentage}', String(percent));\n\n return (\n <span {...props} className={className} data-fs-cart-item-discount-percentage=\"\" data-fs-text=\"\">\n {children ?? text}\n </span>\n );\n}\n\nexport function CartItemLineTotal({ className, children, ...props }: CartItemLineTotalProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n return (\n <span {...props} className={className} data-fs-cart-item-line-total=\"\">\n {children ?? formatPrice(Number(item.sellingPrice.amount) * item.quantity, locale, currency)}\n </span>\n );\n}\n\nexport function CartItemOwnTotal({ className, children, ...props }: CartItemOwnTotalProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n return (\n <span {...props} className={className} data-fs-cart-item-own-total=\"\" data-fs-text=\"\">\n {children ?? formatPrice(Number(item.ownTotal.amount), locale, currency)}\n </span>\n );\n}\n\nexport function CartItemAggregateTotal({ className, children, ...props }: CartItemAggregateTotalProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n return (\n <span {...props} className={className} data-fs-cart-item-aggregate-total=\"\" data-fs-text=\"\">\n {children ?? formatPrice(Number(item.total.amount), locale, currency)}\n </span>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Quantity stepper — the unit multiplier converts between the purchasable count the order form and\n// the cart mutations use, and the measure the merchant sells in. `1` is the identity, so a SKU sold\n// by the unit behaves exactly as it did before the multiplier was read.\n// ---------------------------------------------------------------------------\n\n/** Why a committed amount had to be corrected; drives both the notice and its styling hook. */\ntype QuantityAdjustment = { reason: 'min' | 'max' | 'multiple'; value: number };\n\nconst MAX_DISPLAY_DECIMALS = 6;\n\n/**\n * A multiplier the platform cannot sell in — zero, negative, or non-finite — is treated as `1`, which\n * is also the fallback when the optional slice is absent (the stepper composed with no own provider).\n */\nfunction effectiveMultiplier(slice: CartItemQuantitySlice | null | undefined): number {\n const multiplier = slice?.variant?.unitMultiplier ?? 1;\n return Number.isFinite(multiplier) && multiplier > 0 ? multiplier : 1;\n}\n\n/**\n * Precision of the multiplier, so a displayed amount never carries binary-representation noise.\n * Derived through `toFixed` rather than `String`: `String` switches to exponential notation below\n * 1e-6, where it carries no decimal point and would report zero places, yielding a step of 0 that no\n * control can move. The normal-path precisions (0.5, 0.1, 0.333) are covered by tests; the\n * exponential branch is not, since it needs a multiplier no SKU can have.\n */\nfunction decimalPlacesOf(multiplier: number): number {\n const [, decimals = ''] = multiplier.toFixed(MAX_DISPLAY_DECIMALS).split('.');\n // Trailing zeros carry no precision; strip them with a linear scan rather than a `/0+$/` regex,\n // which SonarQube flags as super-linear (harmless here — the input is a bounded toFixed string).\n let end = decimals.length;\n while (end > 0 && decimals[end - 1] === '0') end -= 1;\n return end;\n}\n\nfunction roundTo(value: number, decimals: number): number {\n return Number(value.toFixed(decimals));\n}\n\nfunction resolveAdjustment(\n entered: number,\n snappedDisplay: number,\n multiplier: number,\n minDisplay: number,\n maxDisplay: number | undefined,\n): QuantityAdjustment | null {\n if (maxDisplay != null && entered > maxDisplay) return { reason: 'max', value: snappedDisplay };\n if (entered < minDisplay) return { reason: 'min', value: snappedDisplay };\n // Tolerance is relative to the multiplier, so recovering the count through a division never reads\n // as an off-grid entry.\n if (Math.abs(entered - snappedDisplay) > multiplier * 1e-6) return { reason: 'multiple', value: snappedDisplay };\n return null;\n}\n\nexport function CartItemQuantity({\n className,\n children,\n min = 1,\n max,\n step,\n removeOnDecrement,\n validationMessage = 'Quantity adjusted to {value}.',\n $data: $dataProp,\n $actions: $actionsProp,\n ...props\n}: CartItemQuantityProps) {\n const item = useCartItem();\n const slice = useDataOverride<CartItemQuantitySlice | null | undefined>($dataProp);\n const $actions = useActionsOverride($actionsProp);\n const [adjustment, setAdjustment] = useState<QuantityAdjustment | null>(null);\n\n // A gift line has no interactive quantity control, the same way CartItemRemove has no remove\n // control: a promotion-controlled quantity the shopper cannot validly change, and — with\n // removeOnDecrement — a path that would otherwise dispatch removeItem on a gift, discarding a\n // benefit T6 already sealed off from the remove button. Suppressing the stepper closes that path\n // under any composition. A store showing a gift's fixed quantity composes CartItemQuantityValue.\n if (item.gift) return null;\n\n const cart = $actions?.cart;\n\n const multiplier = effectiveMultiplier(slice);\n const decimals = decimalPlacesOf(multiplier);\n // min, max, and step are counts of purchasable units, and a cart line holds a whole number of\n // them, so a fractional bound has no valid value to land on. Left unrounded, a fractional `min` or\n // `max` becomes the clamp result and leaks into the delta — `decreaseQuantity({ by: 2.5 })` — which\n // the cart mutations reject. Rounding all three keeps every reachable value, and every dispatched\n // delta, an integer.\n const stepUnits = Math.max(1, Math.round(step ?? 1));\n const minUnits = Math.round(min);\n const maxUnits = max == null ? undefined : Math.round(max);\n const displayStep = roundTo(stepUnits * multiplier, decimals);\n const minDisplay = roundTo(minUnits * multiplier, decimals);\n const maxDisplay = maxUnits == null ? undefined : roundTo(maxUnits * multiplier, decimals);\n\n // Commit rather than change: `onValueChange` fires per parseable keystroke, so enforcing there\n // writes to the cart for every digit typed and snaps a value the shopper has not finished entering.\n function handleValueCommitted(value: number | null) {\n if (value == null) return;\n\n if (removeOnDecrement && value < minDisplay) {\n setAdjustment(null);\n void cart?.removeItem?.({ id: item.id });\n return;\n }\n\n const snapped = Math.min(Math.max(Math.round(value / multiplier), minUnits), maxUnits ?? Number.POSITIVE_INFINITY);\n const snappedDisplay = roundTo(snapped * multiplier, decimals);\n setAdjustment(resolveAdjustment(value, snappedDisplay, multiplier, minDisplay, maxDisplay));\n\n const delta = snapped - item.quantity;\n if (delta > 0) {\n void cart?.increaseQuantity?.({ id: item.id, by: delta });\n } else if (delta < 0) {\n void cart?.decreaseQuantity?.({ id: item.id, by: -delta });\n }\n }\n\n const effectiveMin = removeOnDecrement ? 0 : minDisplay;\n const computedClassName = removeOnDecrement ? (className ? `group ${className}` : 'group') : className;\n\n return (\n <BaseNumberField.Root\n {...props}\n value={roundTo(item.quantity * multiplier, decimals)}\n onValueCommitted={handleValueCommitted}\n // A shopper editing the field again makes the previous correction stale, so it is dropped as\n // soon as they type. Step and scrub reasons are excluded: those land on the grid by\n // construction and never raise a notice, so clearing on them would be dead code.\n onValueChange={(_value, details) => {\n if (details.reason === 'input-change' || details.reason === 'input-paste' || details.reason === 'input-clear') {\n setAdjustment(null);\n }\n }}\n min={effectiveMin}\n max={maxDisplay}\n step={displayStep}\n smallStep={displayStep}\n largeStep={roundTo(displayStep * 10, decimals)}\n snapOnStep\n // Base UI clamps text entry to min/max on its own, which would correct an out-of-bounds amount\n // before this component ever saw it — and a correction nobody can observe cannot be explained.\n allowOutOfRange\n className={computedClassName}\n data-fs-cart-item-quantity=\"\"\n {...(multiplier === 1 ? {} : { 'data-fs-cart-item-quantity-multiplier': String(multiplier) })}\n {...(adjustment\n ? {\n 'data-fs-cart-item-quantity-adjusted': '',\n 'data-fs-cart-item-quantity-adjusted-reason': adjustment.reason,\n }\n : {})}\n {...(removeOnDecrement && item.quantity <= minUnits ? { 'data-fs-cart-item-at-min': '' } : {})}\n >\n {children}\n {adjustment ? (\n <div role=\"alert\" data-fs-cart-item-quantity-validation=\"\">\n {validationMessage.replaceAll('{value}', String(adjustment.value))}\n </div>\n ) : null}\n </BaseNumberField.Root>\n );\n}\n\nexport function CartItemQuantityValue({ className, children, ...props }: CartItemQuantityValueProps) {\n const item = useCartItem();\n\n return (\n <span {...props} className={className} data-fs-cart-item-quantity-value=\"\">\n {children ?? String(item.quantity)}\n </span>\n );\n}\n\nexport function CartItemRemove({\n className,\n children,\n onClick,\n disabled,\n removedMessage = 'Item removed',\n $actions: $actionsProp,\n ...props\n}: CartItemRemoveProps) {\n const $actions = useActionsOverride($actionsProp);\n const item = useCartItem();\n const toast = useOptionalToast();\n const [pending, setPending] = useState(false);\n\n // A gift line has no remove control: the promotion granted the item, and this phase does not let a\n // shopper discard it. Suppressed here rather than by composition, because CartList renders one\n // template for every line — an author cannot leave the leaf out of gift rows alone. Covers the\n // remove-on-alert composition too; an unavailable gift is recovered by the cart-level\n // CartRemoveUnavailable batch and by server-side cart validation.\n if (item.gift) return null;\n\n async function handleClick(e: React.MouseEvent<HTMLButtonElement>) {\n if (pending) return;\n const removeItem = $actions?.cart?.removeItem;\n if (typeof removeItem !== 'function') {\n onClick?.(e);\n return;\n }\n\n setPending(true);\n onClick?.(e);\n try {\n const userErrors = await removeItem({ id: item.id });\n // `removeItem` resolves with its errors instead of rejecting, so only an empty list is a removal.\n if (Array.isArray(userErrors) && userErrors.length === 0) {\n toast?.add({ title: removedMessage, type: 'success' });\n }\n } catch (error) {\n console.error('[CartItemRemove] removeItem failed', loggableErrorName(error));\n } finally {\n setPending(false);\n }\n }\n\n return (\n <button\n {...props}\n type=\"button\"\n className={className}\n disabled={pending || disabled}\n onClick={handleClick}\n data-fs-cart-item-remove=\"\"\n data-fs-button=\"\"\n >\n {children}\n </button>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Availability gates — read `variant.availability` from the core slice (F4).\n// UNKNOWN and any future non-available state fall back to the generic \"Unavailable\".\n// ---------------------------------------------------------------------------\n\nconst UNAVAILABLE_LABELS: Record<string, string> = {\n OUT_OF_STOCK: 'Out of stock',\n UNAVAILABLE: 'Cannot be delivered',\n};\n\nfunction unavailableLabel(availability: string): string {\n return UNAVAILABLE_LABELS[availability] ?? 'Unavailable';\n}\n\nexport function CartItemAvailable({ className, children, ...props }: CartItemAvailableProps) {\n const item = useCartItem();\n if (item.variant.availability !== 'AVAILABLE') return null;\n\n return (\n <div {...props} className={className} data-fs-cart-item-available=\"\">\n {children}\n </div>\n );\n}\n\nexport function CartItemUnavailable({ className, children, ...props }: CartItemUnavailableProps) {\n const item = useCartItem();\n const { availability } = item.variant;\n if (availability === 'AVAILABLE') return null;\n\n return (\n <div\n {...props}\n className={className}\n data-fs-cart-item-unavailable=\"\"\n data-fs-cart-item-unavailable-state={availability.toLowerCase()}\n >\n {children ?? unavailableLabel(availability)}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// F9 — gift marker. Inline, so `children` override the label rather than nesting a subtree; a gift\n// row's other needs are met by the row's own `data-fs-cart-item-gift-line` hook. \"Free\" is\n// operational copy, so it ships as a built-in default like the unavailability labels above, and moves\n// to the localization catalog rather than to a content model.\n// ---------------------------------------------------------------------------\n\nexport function CartItemGift({ className, children, ...props }: CartItemGiftProps) {\n const item = useCartItem();\n if (!item.gift) return null;\n\n return (\n <span {...props} className={className} data-fs-cart-item-gift=\"\" data-fs-text=\"\">\n {children ?? 'Free'}\n </span>\n );\n}\n"],"mappings":"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"}
@@ -4,7 +4,7 @@ import "../../core/dist/index.js";
4
4
  import { useStorefrontContext } from "../../sdk/context/storefront-context.js";
5
5
  import { graphql } from "../../sdk/generated/gql.js";
6
6
  import { useDataOverride } from "../../sdk/data/data-override-context.js";
7
- import { CartTotalValueContext, formatPrice } from "../cart/cart.js";
7
+ import { CartTotalValueContext, formatPrice, isFiniteAmount } from "../cart/cart.js";
8
8
  import { createContext, useContext, useMemo } from "react";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
10
  //#region src/matter/cart-totalizer/cart-totalizer.tsx
@@ -97,7 +97,8 @@ function CartTotalizerLine({ totalizerId, label, variant = "default", zeroLabel,
97
97
  const { locale, currency } = useStorefrontContext();
98
98
  const totalizer = totalizers.find((t) => t.id === totalizerId);
99
99
  if (!totalizer) return null;
100
- const amount = totalizer.value.amount;
100
+ if (!isFiniteAmount(totalizer.value.amount)) return null;
101
+ const amount = Number(totalizer.value.amount);
101
102
  if (variant === "discount" && amount === 0) return null;
102
103
  const valueContent = amount === 0 && zeroLabel != null ? zeroLabel : formatPrice(amount, locale, currency);
103
104
  return /* @__PURE__ */ jsxs("div", {