@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
@@ -1 +1 @@
1
- {"version":3,"file":"footer-social.js","names":[],"sources":["../../../src/particles/footer-social/footer-social.tsx"],"sourcesContent":["import { type ContentValue, defineComponent, defineContentType } from '@vtex/fs-core';\nimport type { ComponentPropsWithRef } from 'react';\nimport { resolveSafeHref } from '../../utils/safe-href.js';\nimport { Icon } from '../icon/icon.js';\nimport { Image } from '../image/image.js';\nimport { NavigationLink } from '../navigation-link/navigation-link.js';\n\n// ---------------------------------------------------------------------------\n// Content model\n// ---------------------------------------------------------------------------\n\nexport const FooterSocialContent = defineContentType({\n id: 'footer-social',\n title: 'Footer Social',\n description: \"Links to the store's official social profiles, shown as a row of platform icons.\",\n schema: {\n type: 'object',\n additionalProperties: false,\n // Neither property is required. A missing element key already omits the component before any\n // required check runs, so requiring them would buy nothing and cost the prop fallback: an entry\n // authoring one field alone would be shape-invalid and take the whole element down with it.\n properties: {\n heading: { type: 'string', title: 'Heading' },\n entries: {\n type: 'array',\n title: 'Social profiles',\n // Author-time guidance only: no read-time gate enforces item bounds, so its one consumer is\n // the Content Platform Admin's form. An empty list still reaches the component, which\n // self-hides on it. No ceiling — a brand is on as many platforms as it is on.\n minItems: 1,\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['label', 'url'],\n properties: {\n // Inline rather than a named constant: the catalog scanner reads this file statically\n // and rejects an identifier it cannot evaluate. `SOCIAL_PLATFORM_NAMES` derives from here.\n // Every name resolves: facebook, instagram and pinterest through the base dialect's\n // embedded floor, linkedin and youtube only through a provisioned lucide. No twitter —\n // both vocabularies still draw the old bird, and an enum member cannot be narrowed once\n // published, so Twitter/X rides iconUrl alongside TikTok and WhatsApp.\n platform: {\n type: 'string',\n enum: ['facebook', 'instagram', 'linkedin', 'pinterest', 'youtube'],\n title: 'Platform',\n },\n url: {\n type: 'string',\n minLength: 1,\n title: 'Profile URL',\n format: 'uri-reference',\n // Author-time guidance only, and deliberately identical to FooterLinkGroup's: the Admin\n // form must not reject a destination `resolveSafeHref` would have accepted. Lookahead-free\n // because the Admin's regex engine may be an RE2 family that rejects lookahead outright.\n pattern: '^(?:https?://.*|mailto:.+|tel:.+|/(?:[^/].*)?)$',\n },\n label: { type: 'string', minLength: 1, title: 'Label' },\n iconUrl: {\n type: 'string',\n format: 'uri',\n title: 'Custom icon',\n widget: { 'ui:widget': 'media-gallery' },\n },\n },\n },\n },\n },\n },\n});\n\n/**\n * The platform glyphs `platform` admits, every one verified to resolve. Exported because a store's\n * `global.json` has to list them under `icons.include` for a CMS-chosen name to resolve at runtime —\n * one import there instead of five retyped strings. Reading it off the model rather than declaring it\n * beside one makes the two impossible to drift apart.\n */\nexport const SOCIAL_PLATFORM_NAMES = FooterSocialContent.schema.properties.entries.items.properties.platform.enum;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype FooterSocialContentValue = ContentValue<typeof FooterSocialContent>;\n\n/** One profile in the row. Derived from the model so the prop and content channels cannot drift. */\nexport type FooterSocialEntry = NonNullable<FooterSocialContentValue['entries']>[number];\n\ntype SocialPlatformName = NonNullable<FooterSocialEntry['platform']>;\n\nexport type FooterSocialProps = ComponentPropsWithRef<'div'> & {\n /** Authored fallback for the section heading, used when no CMS entry supplies one. */\n heading?: string;\n /** Authored fallback for the profile list, used when no CMS entry supplies one. */\n entries?: readonly FooterSocialEntry[];\n /** Injected by the compiler, sliced from the page's or layout's composite content entry. */\n $content?: FooterSocialContentValue;\n};\n\n// ---------------------------------------------------------------------------\n// Definition\n// ---------------------------------------------------------------------------\n\nexport const FooterSocialDefinition = defineComponent({\n name: 'FooterSocial',\n importPath: '@vtex/faststore/components',\n component: FooterSocial,\n content: FooterSocialContent,\n editor: { label: 'Footer Social', category: 'navigation' },\n schema: {\n heading: { type: 'string', label: 'Heading' },\n entries: { type: 'array', label: 'Social profiles' },\n },\n guideline:\n \"The store's official social profiles as a row of icon links, both the heading and the list CMS-editable. Every link opens in a new tab. Every predefined platform name must be listed in the store's icons.include. See footer-social.md.\",\n});\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * An entry that can render: a non-blank label, one glyph source, and a destination the allowlist\n * either cleared or refused. The render branches on `href` first — a null one renders the label as\n * text and no glyph at all — so on that path the glyph source survives only to key the discriminator.\n */\ntype ResolvedEntry =\n | { label: string; href: string | null; platform: SocialPlatformName; src?: undefined }\n | { label: string; href: string | null; platform?: SocialPlatformName; src: string };\n\n/**\n * Drops every entry that cannot render both a glyph and an accessible name, then vets what is left.\n * Neither read-time gate enforces `minLength`, so a blank label reaches the component, and `platform`\n * is optional so TikTok can arrive through `iconUrl` — which leaves an entry carrying neither, also\n * legal and also unrenderable. An icon with no name is the one thing F3 must not ship.\n *\n * Renderability is settled before the destination is looked at, so a dropped entry never logs: the\n * filter answers whether this is a social entry at all, and the destination only decides which form\n * it takes.\n */\nfunction resolveEntries(entries: readonly FooterSocialEntry[]): ResolvedEntry[] {\n const resolved: ResolvedEntry[] = [];\n\n entries.forEach((entry, index) => {\n if (!entry.label.trim() || !(entry.iconUrl?.trim() || entry.platform)) {\n return;\n }\n\n const destination = resolveSafeHref(entry.url);\n if (!destination.safe) {\n // A bounded reason and no authored value, the convention the content runtime's own log follows.\n console.warn('[FooterSocial] link destination rejected', { index, reason: destination.reason });\n }\n const href = destination.safe ? destination.href : null;\n\n if (entry.iconUrl?.trim()) {\n resolved.push({ label: entry.label, href, platform: entry.platform, src: entry.iconUrl });\n } else if (entry.platform) {\n resolved.push({ label: entry.label, href, platform: entry.platform });\n }\n });\n\n return resolved;\n}\n\nexport function FooterSocial({ heading, entries, $content, className, ...props }: FooterSocialProps) {\n const sectionHeading = $content?.heading ?? heading;\n // An entry supplying `entries: []` beats an authored prop: a merchant who cleared the row cleared it.\n const resolved = resolveEntries($content?.entries ?? entries ?? []);\n\n if (resolved.length === 0) return null;\n\n // A blank heading is unconfigured, not an empty value: no element and no group name, rather than\n // an empty one. Trim to decide, never to render.\n const named = Boolean(sectionHeading?.trim());\n\n return (\n <div {...props} className={className} data-fs-footer-social=\"\">\n {named ? (\n <h3 data-fs-text=\"\" data-fs-footer-social-heading=\"\">\n {sectionHeading}\n </h3>\n ) : null}\n {/* biome-ignore lint/a11y/noRedundantRoles: WebKit drops the implicit list role from a ul whose\n computed `list-style` is `none` — exactly what this component's swatch sets — so the explicit\n role is what holds the semantics the named list was chosen for. */}\n <ul role=\"list\" data-fs-footer-social-list=\"\" aria-label={named ? sectionHeading : undefined}>\n {resolved.map((entry, index) => (\n <li key={index} data-fs-footer-social-item=\"\" data-fs-footer-social-platform={entry.platform}>\n {entry.href === null ? (\n <span data-fs-text=\"\" data-fs-footer-social-text=\"\">\n {entry.label}\n </span>\n ) : (\n <NavigationLink href={entry.href} target=\"_blank\" data-fs-footer-social-link=\"\">\n {entry.src === undefined ? (\n <>\n <Icon name={entry.platform} data-fs-footer-social-icon=\"\" />\n <span data-fs-sr-only=\"\" className=\"sr-only\">\n {entry.label}\n </span>\n </>\n ) : (\n <Image src={entry.src} alt={entry.label} data-fs-footer-social-icon=\"\" />\n )}\n </NavigationLink>\n )}\n </li>\n ))}\n </ul>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"footer-social.js","names":[],"sources":["../../../src/particles/footer-social/footer-social.tsx"],"sourcesContent":["import { type ContentValue, defineComponent, defineContentType } from '@vtex/fs-core';\nimport type { ComponentPropsWithRef } from 'react';\nimport { resolveSafeHref } from '../../utils/safe-href.js';\nimport { Icon } from '../icon/icon.js';\nimport { Image } from '../image/image.js';\nimport { NavigationLink } from '../navigation-link/navigation-link.js';\n\n// ---------------------------------------------------------------------------\n// Content model\n// ---------------------------------------------------------------------------\n\nexport const FooterSocialContent = defineContentType({\n id: 'footer-social',\n title: 'Footer Social',\n description: \"Links to the store's official social profiles, shown as a row of platform icons.\",\n schema: {\n type: 'object',\n additionalProperties: false,\n // Neither property is required. A missing element key already omits the component before any\n // required check runs, so requiring them would buy nothing and cost the prop fallback: an entry\n // authoring one field alone would be shape-invalid and take the whole element down with it.\n properties: {\n heading: { type: 'string', title: 'Heading' },\n entries: {\n type: 'array',\n title: 'Social profiles',\n // Author-time guidance only: no read-time gate enforces item bounds, so its one consumer is\n // the Content Platform Admin's form. An empty list still reaches the component, which\n // self-hides on it. No ceiling — a brand is on as many platforms as it is on.\n minItems: 1,\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['label', 'url'],\n properties: {\n // Inline rather than a named constant: the catalog scanner reads this file statically\n // and rejects an identifier it cannot evaluate. `SOCIAL_PLATFORM_NAMES` derives from here.\n // Every name resolves: facebook, instagram and pinterest through the base dialect's\n // embedded floor, linkedin and youtube only through a provisioned lucide. No twitter —\n // both vocabularies still draw the old bird, and an enum member cannot be narrowed once\n // published, so Twitter/X rides iconUrl alongside TikTok and WhatsApp.\n platform: {\n type: 'string',\n enum: ['facebook', 'instagram', 'linkedin', 'pinterest', 'youtube'],\n title: 'Platform',\n },\n url: {\n type: 'string',\n minLength: 1,\n title: 'Profile URL',\n format: 'uri-reference',\n // Author-time guidance only, and deliberately identical to FooterLinkGroup's: the Admin\n // form must not reject a destination `resolveSafeHref` would have accepted. Lookahead-free\n // because the Admin's regex engine may be an RE2 family that rejects lookahead outright.\n pattern: '^(?:https?://.*|mailto:.+|tel:.+|/(?:[^/].*)?)$',\n },\n label: { type: 'string', minLength: 1, title: 'Label' },\n iconUrl: {\n type: 'string',\n format: 'uri',\n title: 'Custom icon',\n widget: { 'ui:widget': 'media-gallery' },\n },\n },\n },\n },\n },\n },\n});\n\n/**\n * The platform glyphs `platform` admits, every one verified to resolve. Exported because a store's\n * foundation has to list them under `icons.include` for a CMS-chosen name to resolve at runtime —\n * one import there instead of five retyped strings. Reading it off the model rather than declaring it\n * beside one makes the two impossible to drift apart.\n */\nexport const SOCIAL_PLATFORM_NAMES = FooterSocialContent.schema.properties.entries.items.properties.platform.enum;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype FooterSocialContentValue = ContentValue<typeof FooterSocialContent>;\n\n/** One profile in the row. Derived from the model so the prop and content channels cannot drift. */\nexport type FooterSocialEntry = NonNullable<FooterSocialContentValue['entries']>[number];\n\ntype SocialPlatformName = NonNullable<FooterSocialEntry['platform']>;\n\nexport type FooterSocialProps = ComponentPropsWithRef<'div'> & {\n /** Authored fallback for the section heading, used when no CMS entry supplies one. */\n heading?: string;\n /** Authored fallback for the profile list, used when no CMS entry supplies one. */\n entries?: readonly FooterSocialEntry[];\n /** Injected by the compiler, sliced from the page's or layout's composite content entry. */\n $content?: FooterSocialContentValue;\n};\n\n// ---------------------------------------------------------------------------\n// Definition\n// ---------------------------------------------------------------------------\n\nexport const FooterSocialDefinition = defineComponent({\n name: 'FooterSocial',\n importPath: '@vtex/faststore/components',\n component: FooterSocial,\n content: FooterSocialContent,\n editor: { label: 'Footer Social', category: 'navigation' },\n schema: {\n heading: { type: 'string', label: 'Heading' },\n entries: { type: 'array', label: 'Social profiles' },\n },\n guideline:\n \"The store's official social profiles as a row of icon links, both the heading and the list CMS-editable. Every link opens in a new tab. Every predefined platform name must be listed in the store's icons.include. See footer-social.md.\",\n});\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * An entry that can render: a non-blank label, one glyph source, and a destination the allowlist\n * either cleared or refused. The render branches on `href` first — a null one renders the label as\n * text and no glyph at all — so on that path the glyph source survives only to key the discriminator.\n */\ntype ResolvedEntry =\n | { label: string; href: string | null; platform: SocialPlatformName; src?: undefined }\n | { label: string; href: string | null; platform?: SocialPlatformName; src: string };\n\n/**\n * Drops every entry that cannot render both a glyph and an accessible name, then vets what is left.\n * Neither read-time gate enforces `minLength`, so a blank label reaches the component, and `platform`\n * is optional so TikTok can arrive through `iconUrl` — which leaves an entry carrying neither, also\n * legal and also unrenderable. An icon with no name is the one thing F3 must not ship.\n *\n * Renderability is settled before the destination is looked at, so a dropped entry never logs: the\n * filter answers whether this is a social entry at all, and the destination only decides which form\n * it takes.\n */\nfunction resolveEntries(entries: readonly FooterSocialEntry[]): ResolvedEntry[] {\n const resolved: ResolvedEntry[] = [];\n\n entries.forEach((entry, index) => {\n if (!entry.label.trim() || !(entry.iconUrl?.trim() || entry.platform)) {\n return;\n }\n\n const destination = resolveSafeHref(entry.url);\n if (!destination.safe) {\n // A bounded reason and no authored value, the convention the content runtime's own log follows.\n console.warn('[FooterSocial] link destination rejected', { index, reason: destination.reason });\n }\n const href = destination.safe ? destination.href : null;\n\n if (entry.iconUrl?.trim()) {\n resolved.push({ label: entry.label, href, platform: entry.platform, src: entry.iconUrl });\n } else if (entry.platform) {\n resolved.push({ label: entry.label, href, platform: entry.platform });\n }\n });\n\n return resolved;\n}\n\nexport function FooterSocial({ heading, entries, $content, className, ...props }: FooterSocialProps) {\n const sectionHeading = $content?.heading ?? heading;\n // An entry supplying `entries: []` beats an authored prop: a merchant who cleared the row cleared it.\n const resolved = resolveEntries($content?.entries ?? entries ?? []);\n\n if (resolved.length === 0) return null;\n\n // A blank heading is unconfigured, not an empty value: no element and no group name, rather than\n // an empty one. Trim to decide, never to render.\n const named = Boolean(sectionHeading?.trim());\n\n return (\n <div {...props} className={className} data-fs-footer-social=\"\">\n {named ? (\n <h3 data-fs-text=\"\" data-fs-footer-social-heading=\"\">\n {sectionHeading}\n </h3>\n ) : null}\n {/* biome-ignore lint/a11y/noRedundantRoles: WebKit drops the implicit list role from a ul whose\n computed `list-style` is `none` — exactly what this component's swatch sets — so the explicit\n role is what holds the semantics the named list was chosen for. */}\n <ul role=\"list\" data-fs-footer-social-list=\"\" aria-label={named ? sectionHeading : undefined}>\n {resolved.map((entry, index) => (\n <li key={index} data-fs-footer-social-item=\"\" data-fs-footer-social-platform={entry.platform}>\n {entry.href === null ? (\n <span data-fs-text=\"\" data-fs-footer-social-text=\"\">\n {entry.label}\n </span>\n ) : (\n <NavigationLink href={entry.href} target=\"_blank\" data-fs-footer-social-link=\"\">\n {entry.src === undefined ? (\n <>\n <Icon name={entry.platform} data-fs-footer-social-icon=\"\" />\n <span data-fs-sr-only=\"\" className=\"sr-only\">\n {entry.label}\n </span>\n </>\n ) : (\n <Image src={entry.src} alt={entry.label} data-fs-footer-social-icon=\"\" />\n )}\n </NavigationLink>\n )}\n </li>\n ))}\n </ul>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -65,7 +65,7 @@ export declare const IncentivesContent: {
65
65
  };
66
66
  };
67
67
  /**
68
- * The benefit glyphs the `icon` enum admits. Exported because a store's `global.json` has to list
68
+ * The benefit glyphs the `icon` enum admits. Exported because a store's foundation has to list
69
69
  * them under `icons.include` for a CMS-chosen name to resolve at runtime — one import there instead
70
70
  * of five retyped strings, which is what keeps the layout task from silently missing one. Reading it
71
71
  * off the model rather than declaring it alongside makes the two impossible to drift apart.
@@ -70,7 +70,7 @@ const IncentivesContent = defineContentType({
70
70
  }
71
71
  });
72
72
  /**
73
- * The benefit glyphs the `icon` enum admits. Exported because a store's `global.json` has to list
73
+ * The benefit glyphs the `icon` enum admits. Exported because a store's foundation has to list
74
74
  * them under `icons.include` for a CMS-chosen name to resolve at runtime — one import there instead
75
75
  * of five retyped strings, which is what keeps the layout task from silently missing one. Reading it
76
76
  * off the model rather than declaring it alongside makes the two impossible to drift apart.
@@ -1 +1 @@
1
- {"version":3,"file":"incentives.js","names":[],"sources":["../../../src/particles/incentives/incentives.tsx"],"sourcesContent":["import { type ContentValue, defineComponent, defineContentType } from '@vtex/fs-core';\nimport type { ComponentPropsWithRef } from 'react';\nimport type { PageRef } from '../../sdk/navigation/types.js';\nimport { resolveSafeHref } from '../../utils/safe-href.js';\nimport { Icon } from '../icon/icon.js';\nimport { Image } from '../image/image.js';\nimport { NavigationLink } from '../navigation-link/navigation-link.js';\n\n// ---------------------------------------------------------------------------\n// Content model\n// ---------------------------------------------------------------------------\n\nexport const IncentivesContent = defineContentType({\n id: 'incentives',\n title: 'Incentives',\n description: 'Store benefit highlights rendered as a strip in the footer.',\n schema: {\n type: 'object',\n additionalProperties: false,\n // `items` is deliberately optional. A missing element key already omits the component before any\n // required check runs, so requiring the list would buy nothing and cost the prop fallback: an\n // entry without it would be shape-invalid and the authored `items` never reached.\n properties: {\n items: {\n type: 'array',\n title: 'Incentive items',\n // Author-time guidance only: no read-time gate enforces item bounds, so its one consumer is\n // the Content Platform Admin's form. An empty list still reaches the component, which\n // self-hides on it. No ceiling — the swatch has to lay out at any count regardless.\n minItems: 1,\n items: {\n type: 'object',\n additionalProperties: false,\n // `title` alone. A nested required property is validated at read time and a violation omits\n // the whole strip, so every entry here widens the blast radius of one under-filled item.\n required: ['title'],\n properties: {\n // `minLength` on the title alone: it is the required property, so a blank one is the only\n // blank that decides anything. Both lines render if present and drop if blank, which is\n // guidance the Admin form has no reason to state differently for one of them.\n title: { type: 'string', minLength: 1, title: 'Title' },\n firstLineText: { type: 'string', title: 'First line' },\n secondLineText: { type: 'string', title: 'Second line' },\n // Optional, because a benefit strip links nowhere and a shortcut row links everywhere,\n // and the two are the same shape. Author-time guidance only, deliberately identical to\n // FooterLinkGroup's so the Admin form cannot reject a destination `resolveSafeHref`\n // would have accepted; lookahead-free because that regex engine may be an RE2 family.\n href: {\n type: 'string',\n minLength: 1,\n title: 'Link',\n format: 'uri-reference',\n pattern: '^(?:https?://.*|mailto:.+|tel:.+|/(?:[^/].*)?)$',\n },\n // Inline rather than a named constant: the catalog scanner reads this file statically and\n // rejects an identifier it cannot evaluate.\n icon: {\n type: 'string',\n enum: ['truck', 'calendar', 'gift', 'store', 'shield-check'],\n title: 'Icon',\n },\n iconUrl: {\n type: 'string',\n format: 'uri',\n title: 'Custom icon',\n widget: { 'ui:widget': 'media-gallery' },\n },\n alt: { type: 'string', title: 'Custom icon alt text' },\n },\n },\n },\n },\n },\n});\n\n/**\n * The benefit glyphs the `icon` enum admits. Exported because a store's `global.json` has to list\n * them under `icons.include` for a CMS-chosen name to resolve at runtime — one import there instead\n * of five retyped strings, which is what keeps the layout task from silently missing one. Reading it\n * off the model rather than declaring it alongside makes the two impossible to drift apart.\n */\nexport const INCENTIVE_ICON_NAMES = IncentivesContent.schema.properties.items.items.properties.icon.enum;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype IncentivesContentValue = ContentValue<typeof IncentivesContent>;\n\ntype IncentiveContentItem = NonNullable<IncentivesContentValue['items']>[number];\n\n/** One incentive item. The prop accepts a page reference; a CMS href stays a string. */\nexport type IncentiveItem = Omit<IncentiveContentItem, 'href'> & {\n href?: PageRef | string;\n};\n\ntype IncentiveIconName = NonNullable<IncentiveItem['icon']>;\n\nexport type IncentivesProps = ComponentPropsWithRef<'ul'> & {\n /** Authored fallback list, rendered when no CMS entry supplies one. */\n items?: readonly IncentiveItem[];\n /** Injected by the compiler, sliced from the layout's composite content entry. */\n $content?: IncentivesContentValue;\n};\n\n// ---------------------------------------------------------------------------\n// Definition\n// ---------------------------------------------------------------------------\n\nexport const IncentivesDefinition = defineComponent({\n name: 'Incentives',\n importPath: '@vtex/faststore/components',\n component: Incentives,\n content: IncentivesContent,\n editor: { label: 'Incentives', category: 'content' },\n schema: {\n items: { type: 'array', label: 'Incentive items' },\n },\n guideline:\n \"The store's value propositions as a strip of icon-and-copy items, the whole list CMS-editable: each item carries a title, up to two description lines, either a predefined icon or an uploaded one, and an optional href that turns the whole block into a link. Every predefined icon name must be listed in the store's icons.include. Renders nothing when unconfigured. See incentives.md.\",\n});\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * The three glyph states an item can reach, kept as a union so \"at most one source\" is a property of\n * the type rather than of how the resolver happens to build it. `alt` belongs to the uploaded member\n * alone, since a sprite glyph has nothing to name.\n */\ntype ResolvedGlyph =\n | { icon: IncentiveIconName; src?: undefined; alt?: undefined }\n | { icon?: undefined; src: string; alt?: string }\n | { icon?: undefined; src?: undefined; alt?: undefined };\n\n/**\n * An item that can render: a non-blank title, its surviving description lines, a glyph state, and a\n * destination the allowlist cleared. The title is the item's identity, so it is the only part whose\n * absence drops the item.\n */\ntype ResolvedIncentive = { title: string; lines: string[]; href: PageRef | string | null } & ResolvedGlyph;\n\n/**\n * The item's destination, or null for both \"none authored\" and \"the allowlist refused it\". Unlike the\n * link group and the social row, where a link is the whole point, a refusal here costs the anchor\n * alone — an unlinked item is this component's ordinary shape, so the glyph and copy still render.\n */\nfunction readItems(\n content: readonly IncentiveContentItem[] | undefined,\n authored: readonly IncentiveItem[] | undefined,\n): readonly IncentiveItem[] {\n if (content !== undefined) return content;\n return authored ?? [];\n}\n\nfunction resolveHref(raw: PageRef | string | undefined, index: number): PageRef | string | null {\n // A page reference skips the string allowlist. Absent is \"no link\", not a rejection.\n if (typeof raw !== 'string') return raw ?? null;\n // Blank is \"no link\", not a rejection, so it never reaches the allowlist or the log.\n if (!raw.trim()) return null;\n\n const destination = resolveSafeHref(raw);\n if (!destination.safe) {\n // A bounded reason and no authored value, the convention the content runtime's own log follows.\n console.warn('[Incentives] link destination rejected', { index, reason: destination.reason });\n return null;\n }\n\n return destination.href;\n}\n\n/**\n * Drops every item with no title to show, and every description line that would render empty.\n * Neither read-time gate enforces `minLength`, so both blanks reach the component. The asymmetry is\n * deliberate: an icon with no label is not an incentive, while a missing line is supplementary.\n */\nfunction resolveIncentives(items: readonly IncentiveItem[]): ResolvedIncentive[] {\n const resolved: ResolvedIncentive[] = [];\n\n items.forEach((item, index) => {\n if (!item.title.trim()) return;\n\n const lines = [item.firstLineText, item.secondLineText].filter((line): line is string => Boolean(line?.trim()));\n const href = resolveHref(item.href, index);\n\n if (item.iconUrl?.trim()) {\n resolved.push({ title: item.title, lines, href, src: item.iconUrl, alt: item.alt });\n } else if (item.icon) {\n resolved.push({ title: item.title, lines, href, icon: item.icon });\n } else {\n resolved.push({ title: item.title, lines, href });\n }\n });\n\n return resolved;\n}\n\n/**\n * The item's glyph, or nothing. Presence decides the channel — an upload first, then a predefined\n * sprite name — and an item carrying neither renders as copy alone rather than an empty slot.\n */\nfunction IncentiveGlyph({ item }: { item: ResolvedIncentive }) {\n if (item.src !== undefined) {\n return <Image src={item.src} alt={item.alt} data-fs-incentive-icon=\"\" />;\n }\n\n if (item.icon !== undefined) {\n return <Icon name={item.icon} data-fs-incentive-icon=\"\" />;\n }\n\n return null;\n}\n\n/**\n * The glyph and copy an item renders in both forms. Kept whole rather than split across the linked\n * and unlinked branches so the anchor wraps the entire block: on a shortcut row the pictogram is\n * half the click target, and a link around the label alone leaves it dead.\n */\nfunction IncentiveBody({ item }: { item: ResolvedIncentive }) {\n return (\n <>\n <IncentiveGlyph item={item} />\n <p data-fs-text=\"\" data-fs-incentive-title=\"\">\n {item.title}\n </p>\n {item.lines.map((line, lineIndex) => (\n <p key={lineIndex} data-fs-text=\"\" data-fs-incentive-description=\"\">\n {line}\n </p>\n ))}\n </>\n );\n}\n\nexport function Incentives({ items, $content, className, ...props }: IncentivesProps) {\n const resolved = resolveIncentives(readItems($content?.items, items));\n\n if (resolved.length === 0) return null;\n\n // `role` precedes the spread so a consumer can still override it.\n return (\n // biome-ignore lint/a11y/noRedundantRoles: WebKit drops the implicit list role from a ul whose computed `list-style` is `none` — exactly what this component's swatch sets — so the explicit role is what holds the semantics the list was chosen for.\n <ul role=\"list\" {...props} className={className} data-fs-incentives=\"\">\n {resolved.map((item, index) => (\n <li key={index} data-fs-incentive=\"\">\n {item.href === null ? (\n <IncentiveBody item={item} />\n ) : (\n // No `target`: these are the store's own shortcuts, not the outbound profiles the social\n // row forces a new tab for.\n <NavigationLink href={item.href} data-fs-incentive-link=\"\">\n <IncentiveBody item={item} />\n </NavigationLink>\n )}\n </li>\n ))}\n </ul>\n );\n}\n"],"mappings":";;;;;;;;;AAYA,MAAa,oBAAoB,kBAAkB;CACjD,IAAI;CACJ,OAAO;CACP,aAAa;CACb,QAAQ;EACN,MAAM;EACN,sBAAsB;EAItB,YAAY,EACV,OAAO;GACL,MAAM;GACN,OAAO;GAIP,UAAU;GACV,OAAO;IACL,MAAM;IACN,sBAAsB;IAGtB,UAAU,CAAC,OAAO;IAClB,YAAY;KAIV,OAAO;MAAE,MAAM;MAAU,WAAW;MAAG,OAAO;KAAQ;KACtD,eAAe;MAAE,MAAM;MAAU,OAAO;KAAa;KACrD,gBAAgB;MAAE,MAAM;MAAU,OAAO;KAAc;KAKvD,MAAM;MACJ,MAAM;MACN,WAAW;MACX,OAAO;MACP,QAAQ;MACR,SAAS;KACX;KAGA,MAAM;MACJ,MAAM;MACN,MAAM;OAAC;OAAS;OAAY;OAAQ;OAAS;MAAc;MAC3D,OAAO;KACT;KACA,SAAS;MACP,MAAM;MACN,QAAQ;MACR,OAAO;MACP,QAAQ,EAAE,aAAa,gBAAgB;KACzC;KACA,KAAK;MAAE,MAAM;MAAU,OAAO;KAAuB;IACvD;GACF;EACF,EACF;CACF;AACF,CAAC;;;;;;;AAQD,MAAa,uBAAuB,kBAAkB,OAAO,WAAW,MAAM,MAAM,WAAW,KAAK;AA4BpG,MAAa,uBAAuB,gBAAgB;CAClD,MAAM;CACN,YAAY;CACZ,WAAW;CACX,SAAS;CACT,QAAQ;EAAE,OAAO;EAAc,UAAU;CAAU;CACnD,QAAQ,EACN,OAAO;EAAE,MAAM;EAAS,OAAO;CAAkB,EACnD;CACA,WACE;AACJ,CAAC;;;;;;AA4BD,SAAS,UACP,SACA,UAC0B;CAC1B,IAAI,YAAY,KAAA,GAAW,OAAO;CAClC,OAAO,YAAY,CAAC;AACtB;AAEA,SAAS,YAAY,KAAmC,OAAwC;CAE9F,IAAI,OAAO,QAAQ,UAAU,OAAO,OAAO;CAE3C,IAAI,CAAC,IAAI,KAAK,GAAG,OAAO;CAExB,MAAM,cAAc,gBAAgB,GAAG;CACvC,IAAI,CAAC,YAAY,MAAM;EAErB,QAAQ,KAAK,0CAA0C;GAAE;GAAO,QAAQ,YAAY;EAAO,CAAC;EAC5F,OAAO;CACT;CAEA,OAAO,YAAY;AACrB;;;;;;AAOA,SAAS,kBAAkB,OAAsD;CAC/E,MAAM,WAAgC,CAAC;CAEvC,MAAM,SAAS,MAAM,UAAU;EAC7B,IAAI,CAAC,KAAK,MAAM,KAAK,GAAG;EAExB,MAAM,QAAQ,CAAC,KAAK,eAAe,KAAK,cAAc,CAAC,CAAC,QAAQ,SAAyB,QAAQ,MAAM,KAAK,CAAC,CAAC;EAC9G,MAAM,OAAO,YAAY,KAAK,MAAM,KAAK;EAEzC,IAAI,KAAK,SAAS,KAAK,GACrB,SAAS,KAAK;GAAE,OAAO,KAAK;GAAO;GAAO;GAAM,KAAK,KAAK;GAAS,KAAK,KAAK;EAAI,CAAC;OAC7E,IAAI,KAAK,MACd,SAAS,KAAK;GAAE,OAAO,KAAK;GAAO;GAAO;GAAM,MAAM,KAAK;EAAK,CAAC;OAEjE,SAAS,KAAK;GAAE,OAAO,KAAK;GAAO;GAAO;EAAK,CAAC;CAEpD,CAAC;CAED,OAAO;AACT;;;;;AAMA,SAAS,eAAe,EAAE,QAAqC;CAC7D,IAAI,KAAK,QAAQ,KAAA,GACf,OAAO,oBAAC,OAAD;EAAO,KAAK,KAAK;EAAK,KAAK,KAAK;EAAK,0BAAuB;CAAI,CAAA;CAGzE,IAAI,KAAK,SAAS,KAAA,GAChB,OAAO,oBAAC,MAAD;EAAM,MAAM,KAAK;EAAM,0BAAuB;CAAI,CAAA;CAG3D,OAAO;AACT;;;;;;AAOA,SAAS,cAAc,EAAE,QAAqC;CAC5D,OACE,qBAAA,UAAA,EAAA,UAAA;EACE,oBAAC,gBAAD,EAAsB,KAAO,CAAA;EAC7B,oBAAC,KAAD;GAAG,gBAAa;GAAG,2BAAwB;GACxC,UAAA,KAAK;EACL,CAAA;EACF,KAAK,MAAM,KAAK,MAAM,cACrB,oBAAC,KAAD;GAAmB,gBAAa;GAAG,iCAA8B;GAC9D,UAAA;EACA,GAFK,SAEL,CACJ;CACD,EAAA,CAAA;AAEN;AAEA,SAAgB,WAAW,EAAE,OAAO,UAAU,WAAW,GAAG,SAA0B;CACpF,MAAM,WAAW,kBAAkB,UAAU,UAAU,OAAO,KAAK,CAAC;CAEpE,IAAI,SAAS,WAAW,GAAG,OAAO;CAGlC,OAEE,oBAAC,MAAD;EAAI,MAAK;EAAO,GAAI;EAAkB;EAAW,sBAAmB;EACjE,UAAA,SAAS,KAAK,MAAM,UACnB,oBAAC,MAAD;GAAgB,qBAAkB;GAC/B,UAAA,KAAK,SAAS,OACb,oBAAC,eAAD,EAAqB,KAAO,CAAA,IAI5B,oBAAC,gBAAD;IAAgB,MAAM,KAAK;IAAM,0BAAuB;IACtD,UAAA,oBAAC,eAAD,EAAqB,KAAO,CAAA;GACd,CAAA;EAEhB,GAVK,KAUL,CACL;CACC,CAAA;AAER"}
1
+ {"version":3,"file":"incentives.js","names":[],"sources":["../../../src/particles/incentives/incentives.tsx"],"sourcesContent":["import { type ContentValue, defineComponent, defineContentType } from '@vtex/fs-core';\nimport type { ComponentPropsWithRef } from 'react';\nimport type { PageRef } from '../../sdk/navigation/types.js';\nimport { resolveSafeHref } from '../../utils/safe-href.js';\nimport { Icon } from '../icon/icon.js';\nimport { Image } from '../image/image.js';\nimport { NavigationLink } from '../navigation-link/navigation-link.js';\n\n// ---------------------------------------------------------------------------\n// Content model\n// ---------------------------------------------------------------------------\n\nexport const IncentivesContent = defineContentType({\n id: 'incentives',\n title: 'Incentives',\n description: 'Store benefit highlights rendered as a strip in the footer.',\n schema: {\n type: 'object',\n additionalProperties: false,\n // `items` is deliberately optional. A missing element key already omits the component before any\n // required check runs, so requiring the list would buy nothing and cost the prop fallback: an\n // entry without it would be shape-invalid and the authored `items` never reached.\n properties: {\n items: {\n type: 'array',\n title: 'Incentive items',\n // Author-time guidance only: no read-time gate enforces item bounds, so its one consumer is\n // the Content Platform Admin's form. An empty list still reaches the component, which\n // self-hides on it. No ceiling — the swatch has to lay out at any count regardless.\n minItems: 1,\n items: {\n type: 'object',\n additionalProperties: false,\n // `title` alone. A nested required property is validated at read time and a violation omits\n // the whole strip, so every entry here widens the blast radius of one under-filled item.\n required: ['title'],\n properties: {\n // `minLength` on the title alone: it is the required property, so a blank one is the only\n // blank that decides anything. Both lines render if present and drop if blank, which is\n // guidance the Admin form has no reason to state differently for one of them.\n title: { type: 'string', minLength: 1, title: 'Title' },\n firstLineText: { type: 'string', title: 'First line' },\n secondLineText: { type: 'string', title: 'Second line' },\n // Optional, because a benefit strip links nowhere and a shortcut row links everywhere,\n // and the two are the same shape. Author-time guidance only, deliberately identical to\n // FooterLinkGroup's so the Admin form cannot reject a destination `resolveSafeHref`\n // would have accepted; lookahead-free because that regex engine may be an RE2 family.\n href: {\n type: 'string',\n minLength: 1,\n title: 'Link',\n format: 'uri-reference',\n pattern: '^(?:https?://.*|mailto:.+|tel:.+|/(?:[^/].*)?)$',\n },\n // Inline rather than a named constant: the catalog scanner reads this file statically and\n // rejects an identifier it cannot evaluate.\n icon: {\n type: 'string',\n enum: ['truck', 'calendar', 'gift', 'store', 'shield-check'],\n title: 'Icon',\n },\n iconUrl: {\n type: 'string',\n format: 'uri',\n title: 'Custom icon',\n widget: { 'ui:widget': 'media-gallery' },\n },\n alt: { type: 'string', title: 'Custom icon alt text' },\n },\n },\n },\n },\n },\n});\n\n/**\n * The benefit glyphs the `icon` enum admits. Exported because a store's foundation has to list\n * them under `icons.include` for a CMS-chosen name to resolve at runtime — one import there instead\n * of five retyped strings, which is what keeps the layout task from silently missing one. Reading it\n * off the model rather than declaring it alongside makes the two impossible to drift apart.\n */\nexport const INCENTIVE_ICON_NAMES = IncentivesContent.schema.properties.items.items.properties.icon.enum;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype IncentivesContentValue = ContentValue<typeof IncentivesContent>;\n\ntype IncentiveContentItem = NonNullable<IncentivesContentValue['items']>[number];\n\n/** One incentive item. The prop accepts a page reference; a CMS href stays a string. */\nexport type IncentiveItem = Omit<IncentiveContentItem, 'href'> & {\n href?: PageRef | string;\n};\n\ntype IncentiveIconName = NonNullable<IncentiveItem['icon']>;\n\nexport type IncentivesProps = ComponentPropsWithRef<'ul'> & {\n /** Authored fallback list, rendered when no CMS entry supplies one. */\n items?: readonly IncentiveItem[];\n /** Injected by the compiler, sliced from the layout's composite content entry. */\n $content?: IncentivesContentValue;\n};\n\n// ---------------------------------------------------------------------------\n// Definition\n// ---------------------------------------------------------------------------\n\nexport const IncentivesDefinition = defineComponent({\n name: 'Incentives',\n importPath: '@vtex/faststore/components',\n component: Incentives,\n content: IncentivesContent,\n editor: { label: 'Incentives', category: 'content' },\n schema: {\n items: { type: 'array', label: 'Incentive items' },\n },\n guideline:\n \"The store's value propositions as a strip of icon-and-copy items, the whole list CMS-editable: each item carries a title, up to two description lines, either a predefined icon or an uploaded one, and an optional href that turns the whole block into a link. Every predefined icon name must be listed in the store's icons.include. Renders nothing when unconfigured. See incentives.md.\",\n});\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * The three glyph states an item can reach, kept as a union so \"at most one source\" is a property of\n * the type rather than of how the resolver happens to build it. `alt` belongs to the uploaded member\n * alone, since a sprite glyph has nothing to name.\n */\ntype ResolvedGlyph =\n | { icon: IncentiveIconName; src?: undefined; alt?: undefined }\n | { icon?: undefined; src: string; alt?: string }\n | { icon?: undefined; src?: undefined; alt?: undefined };\n\n/**\n * An item that can render: a non-blank title, its surviving description lines, a glyph state, and a\n * destination the allowlist cleared. The title is the item's identity, so it is the only part whose\n * absence drops the item.\n */\ntype ResolvedIncentive = { title: string; lines: string[]; href: PageRef | string | null } & ResolvedGlyph;\n\n/**\n * The item's destination, or null for both \"none authored\" and \"the allowlist refused it\". Unlike the\n * link group and the social row, where a link is the whole point, a refusal here costs the anchor\n * alone — an unlinked item is this component's ordinary shape, so the glyph and copy still render.\n */\nfunction readItems(\n content: readonly IncentiveContentItem[] | undefined,\n authored: readonly IncentiveItem[] | undefined,\n): readonly IncentiveItem[] {\n if (content !== undefined) return content;\n return authored ?? [];\n}\n\nfunction resolveHref(raw: PageRef | string | undefined, index: number): PageRef | string | null {\n // A page reference skips the string allowlist. Absent is \"no link\", not a rejection.\n if (typeof raw !== 'string') return raw ?? null;\n // Blank is \"no link\", not a rejection, so it never reaches the allowlist or the log.\n if (!raw.trim()) return null;\n\n const destination = resolveSafeHref(raw);\n if (!destination.safe) {\n // A bounded reason and no authored value, the convention the content runtime's own log follows.\n console.warn('[Incentives] link destination rejected', { index, reason: destination.reason });\n return null;\n }\n\n return destination.href;\n}\n\n/**\n * Drops every item with no title to show, and every description line that would render empty.\n * Neither read-time gate enforces `minLength`, so both blanks reach the component. The asymmetry is\n * deliberate: an icon with no label is not an incentive, while a missing line is supplementary.\n */\nfunction resolveIncentives(items: readonly IncentiveItem[]): ResolvedIncentive[] {\n const resolved: ResolvedIncentive[] = [];\n\n items.forEach((item, index) => {\n if (!item.title.trim()) return;\n\n const lines = [item.firstLineText, item.secondLineText].filter((line): line is string => Boolean(line?.trim()));\n const href = resolveHref(item.href, index);\n\n if (item.iconUrl?.trim()) {\n resolved.push({ title: item.title, lines, href, src: item.iconUrl, alt: item.alt });\n } else if (item.icon) {\n resolved.push({ title: item.title, lines, href, icon: item.icon });\n } else {\n resolved.push({ title: item.title, lines, href });\n }\n });\n\n return resolved;\n}\n\n/**\n * The item's glyph, or nothing. Presence decides the channel — an upload first, then a predefined\n * sprite name — and an item carrying neither renders as copy alone rather than an empty slot.\n */\nfunction IncentiveGlyph({ item }: { item: ResolvedIncentive }) {\n if (item.src !== undefined) {\n return <Image src={item.src} alt={item.alt} data-fs-incentive-icon=\"\" />;\n }\n\n if (item.icon !== undefined) {\n return <Icon name={item.icon} data-fs-incentive-icon=\"\" />;\n }\n\n return null;\n}\n\n/**\n * The glyph and copy an item renders in both forms. Kept whole rather than split across the linked\n * and unlinked branches so the anchor wraps the entire block: on a shortcut row the pictogram is\n * half the click target, and a link around the label alone leaves it dead.\n */\nfunction IncentiveBody({ item }: { item: ResolvedIncentive }) {\n return (\n <>\n <IncentiveGlyph item={item} />\n <p data-fs-text=\"\" data-fs-incentive-title=\"\">\n {item.title}\n </p>\n {item.lines.map((line, lineIndex) => (\n <p key={lineIndex} data-fs-text=\"\" data-fs-incentive-description=\"\">\n {line}\n </p>\n ))}\n </>\n );\n}\n\nexport function Incentives({ items, $content, className, ...props }: IncentivesProps) {\n const resolved = resolveIncentives(readItems($content?.items, items));\n\n if (resolved.length === 0) return null;\n\n // `role` precedes the spread so a consumer can still override it.\n return (\n // biome-ignore lint/a11y/noRedundantRoles: WebKit drops the implicit list role from a ul whose computed `list-style` is `none` — exactly what this component's swatch sets — so the explicit role is what holds the semantics the list was chosen for.\n <ul role=\"list\" {...props} className={className} data-fs-incentives=\"\">\n {resolved.map((item, index) => (\n <li key={index} data-fs-incentive=\"\">\n {item.href === null ? (\n <IncentiveBody item={item} />\n ) : (\n // No `target`: these are the store's own shortcuts, not the outbound profiles the social\n // row forces a new tab for.\n <NavigationLink href={item.href} data-fs-incentive-link=\"\">\n <IncentiveBody item={item} />\n </NavigationLink>\n )}\n </li>\n ))}\n </ul>\n );\n}\n"],"mappings":"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"}
@@ -50,7 +50,7 @@ export declare const PaymentMethodsContent: {
50
50
  };
51
51
  /**
52
52
  * The payment glyphs the base icon dialect carries, and so the only names `method` admits. Exported
53
- * because a store's `global.json` has to list them under `icons.include` for a CMS-chosen name to
53
+ * because a store's foundation has to list them under `icons.include` for a CMS-chosen name to
54
54
  * resolve at runtime — one import there instead of ten retyped strings. Reading it off the model
55
55
  * rather than declaring it beside one makes the two impossible to drift apart.
56
56
  */
@@ -61,7 +61,7 @@ const PaymentMethodsContent = defineContentType({
61
61
  });
62
62
  /**
63
63
  * The payment glyphs the base icon dialect carries, and so the only names `method` admits. Exported
64
- * because a store's `global.json` has to list them under `icons.include` for a CMS-chosen name to
64
+ * because a store's foundation has to list them under `icons.include` for a CMS-chosen name to
65
65
  * resolve at runtime — one import there instead of ten retyped strings. Reading it off the model
66
66
  * rather than declaring it beside one makes the two impossible to drift apart.
67
67
  */
@@ -1 +1 @@
1
- {"version":3,"file":"payment-methods.js","names":[],"sources":["../../../src/particles/payment-methods/payment-methods.tsx"],"sourcesContent":["import { type ContentValue, defineComponent, defineContentType } from '@vtex/fs-core';\nimport type { ComponentPropsWithRef } from 'react';\nimport { Icon } from '../icon/icon.js';\nimport { Image } from '../image/image.js';\n\n// ---------------------------------------------------------------------------\n// Content model\n// ---------------------------------------------------------------------------\n\nexport const PaymentMethodsContent = defineContentType({\n id: 'payment-methods',\n title: 'Payment Methods',\n description: 'Accepted payment methods shown as a strip of icons.',\n schema: {\n type: 'object',\n additionalProperties: false,\n // Neither property is required. A missing element key already omits the component before any\n // required check runs, so requiring them would buy nothing and cost the prop fallback: an entry\n // authoring one field alone would be shape-invalid and take the whole element down with it.\n properties: {\n heading: { type: 'string', title: 'Heading' },\n methods: {\n type: 'array',\n title: 'Payment methods',\n // Author-time guidance only: no read-time gate enforces item bounds, so its one consumer is\n // the Content Platform Admin's form. An empty list still reaches the component, which\n // self-hides on it. No ceiling — a merchant lists every method they accept.\n minItems: 1,\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['label'],\n properties: {\n // Inline rather than a named constant: the catalog scanner reads this file statically\n // and rejects an identifier it cannot evaluate. `PAYMENT_METHOD_NAMES` derives from here.\n method: {\n type: 'string',\n enum: [\n 'visa',\n 'amex',\n 'apple-pay',\n 'elo-card',\n 'diners',\n 'google-pay',\n 'mastercard',\n 'paypal',\n 'pix',\n 'stripe',\n ],\n title: 'Method',\n },\n label: { type: 'string', minLength: 1, title: 'Label' },\n iconUrl: {\n type: 'string',\n format: 'uri',\n title: 'Custom icon',\n widget: { 'ui:widget': 'media-gallery' },\n },\n },\n },\n },\n },\n },\n});\n\n/**\n * The payment glyphs the base icon dialect carries, and so the only names `method` admits. Exported\n * because a store's `global.json` has to list them under `icons.include` for a CMS-chosen name to\n * resolve at runtime — one import there instead of ten retyped strings. Reading it off the model\n * rather than declaring it beside one makes the two impossible to drift apart.\n */\nexport const PAYMENT_METHOD_NAMES = PaymentMethodsContent.schema.properties.methods.items.properties.method.enum;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype PaymentMethodsContentValue = ContentValue<typeof PaymentMethodsContent>;\n\n/** One row of the strip. Derived from the model so the prop and content channels cannot drift. */\nexport type PaymentMethodItem = NonNullable<PaymentMethodsContentValue['methods']>[number];\n\ntype PaymentMethodName = NonNullable<PaymentMethodItem['method']>;\n\nexport type PaymentMethodsProps = ComponentPropsWithRef<'div'> & {\n /** Authored fallback for the section heading, used when no CMS entry supplies one. */\n heading?: string;\n /** Authored fallback for the accepted set, used when no CMS entry supplies one. */\n methods?: readonly PaymentMethodItem[];\n /** Injected by the compiler, sliced from the page's or layout's composite content entry. */\n $content?: PaymentMethodsContentValue;\n};\n\n// ---------------------------------------------------------------------------\n// Definition\n// ---------------------------------------------------------------------------\n\nexport const PaymentMethodsDefinition = defineComponent({\n name: 'PaymentMethods',\n importPath: '@vtex/faststore/components',\n component: PaymentMethods,\n content: PaymentMethodsContent,\n editor: { label: 'Payment Methods', category: 'content' },\n schema: {\n heading: { type: 'string', label: 'Heading' },\n methods: { type: 'array', label: 'Payment methods' },\n },\n guideline:\n \"The store's accepted payment methods as a strip of flags, both the heading and the list CMS-editable. Not PricingPaymentMethodsList, which renders a product's installment plans from commerce data. Every predefined method name must be listed in the store's icons.include. See payment-methods.md.\",\n});\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * A method that can render: a non-blank label plus exactly one glyph source. The two members are\n * what the union encodes — an upload names itself through `alt`, while a sprite glyph is decorative\n * and needs the label alongside it.\n */\ntype ResolvedMethod =\n | { label: string; method: PaymentMethodName; src?: undefined }\n | { label: string; method?: PaymentMethodName; src: string };\n\n/**\n * Drops every item that cannot render both a glyph and an accessible name. Neither read-time gate\n * enforces `minLength`, so a blank label reaches the component, and `method` is optional so that\n * Boleto can arrive through `iconUrl` — which leaves an item carrying neither, also legal and also\n * unrenderable. An icon with no name is the one thing F5 must not ship.\n */\nfunction resolveMethods(methods: readonly PaymentMethodItem[]): ResolvedMethod[] {\n const resolved: ResolvedMethod[] = [];\n\n for (const item of methods) {\n if (!item.label.trim()) continue;\n\n if (item.iconUrl?.trim()) {\n resolved.push({ label: item.label, method: item.method, src: item.iconUrl });\n } else if (item.method) {\n resolved.push({ label: item.label, method: item.method });\n }\n }\n\n return resolved;\n}\n\nexport function PaymentMethods({ heading, methods, $content, className, ...props }: PaymentMethodsProps) {\n const sectionHeading = $content?.heading ?? heading;\n const resolved = resolveMethods($content?.methods ?? methods ?? []);\n\n if (resolved.length === 0) return null;\n\n // A blank heading is unconfigured, not an empty value: no element and no group name, rather than\n // an empty one. Trim to decide, never to render.\n const named = Boolean(sectionHeading?.trim());\n\n return (\n <div {...props} className={className} data-fs-payment-methods=\"\">\n {named ? (\n <h3 data-fs-text=\"\" data-fs-payment-methods-heading=\"\">\n {sectionHeading}\n </h3>\n ) : null}\n {/* biome-ignore lint/a11y/noRedundantRoles: WebKit drops the implicit list role from a ul whose\n computed `list-style` is `none` — exactly what this component's swatch sets — so the explicit\n role is what holds the semantics the named list was chosen for. */}\n <ul role=\"list\" data-fs-payment-methods-list=\"\" aria-label={named ? sectionHeading : undefined}>\n {resolved.map((item, index) => (\n <li key={index} data-fs-payment-method=\"\" data-fs-payment-method-name={item.method}>\n {item.src === undefined ? (\n <>\n <Icon name={item.method} data-fs-payment-method-icon=\"\" />\n <span data-fs-sr-only=\"\" className=\"sr-only\">\n {item.label}\n </span>\n </>\n ) : (\n <Image src={item.src} alt={item.label} data-fs-payment-method-icon=\"\" />\n )}\n </li>\n ))}\n </ul>\n </div>\n );\n}\n"],"mappings":";;;;;;;AASA,MAAa,wBAAwB,kBAAkB;CACrD,IAAI;CACJ,OAAO;CACP,aAAa;CACb,QAAQ;EACN,MAAM;EACN,sBAAsB;EAItB,YAAY;GACV,SAAS;IAAE,MAAM;IAAU,OAAO;GAAU;GAC5C,SAAS;IACP,MAAM;IACN,OAAO;IAIP,UAAU;IACV,OAAO;KACL,MAAM;KACN,sBAAsB;KACtB,UAAU,CAAC,OAAO;KAClB,YAAY;MAGV,QAAQ;OACN,MAAM;OACN,MAAM;QACJ;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;OACF;OACA,OAAO;MACT;MACA,OAAO;OAAE,MAAM;OAAU,WAAW;OAAG,OAAO;MAAQ;MACtD,SAAS;OACP,MAAM;OACN,QAAQ;OACR,OAAO;OACP,QAAQ,EAAE,aAAa,gBAAgB;MACzC;KACF;IACF;GACF;EACF;CACF;AACF,CAAC;;;;;;;AAQD,MAAa,uBAAuB,sBAAsB,OAAO,WAAW,QAAQ,MAAM,WAAW,OAAO;AA0B5G,MAAa,2BAA2B,gBAAgB;CACtD,MAAM;CACN,YAAY;CACZ,WAAW;CACX,SAAS;CACT,QAAQ;EAAE,OAAO;EAAmB,UAAU;CAAU;CACxD,QAAQ;EACN,SAAS;GAAE,MAAM;GAAU,OAAO;EAAU;EAC5C,SAAS;GAAE,MAAM;GAAS,OAAO;EAAkB;CACrD;CACA,WACE;AACJ,CAAC;;;;;;;AAqBD,SAAS,eAAe,SAAyD;CAC/E,MAAM,WAA6B,CAAC;CAEpC,KAAK,MAAM,QAAQ,SAAS;EAC1B,IAAI,CAAC,KAAK,MAAM,KAAK,GAAG;EAExB,IAAI,KAAK,SAAS,KAAK,GACrB,SAAS,KAAK;GAAE,OAAO,KAAK;GAAO,QAAQ,KAAK;GAAQ,KAAK,KAAK;EAAQ,CAAC;OACtE,IAAI,KAAK,QACd,SAAS,KAAK;GAAE,OAAO,KAAK;GAAO,QAAQ,KAAK;EAAO,CAAC;CAE5D;CAEA,OAAO;AACT;AAEA,SAAgB,eAAe,EAAE,SAAS,SAAS,UAAU,WAAW,GAAG,SAA8B;CACvG,MAAM,iBAAiB,UAAU,WAAW;CAC5C,MAAM,WAAW,eAAe,UAAU,WAAW,WAAW,CAAC,CAAC;CAElE,IAAI,SAAS,WAAW,GAAG,OAAO;CAIlC,MAAM,QAAQ,QAAQ,gBAAgB,KAAK,CAAC;CAE5C,OACE,qBAAC,OAAD;EAAK,GAAI;EAAkB;EAAW,2BAAwB;EAA9D,UAAA,CACG,QACC,oBAAC,MAAD;GAAI,gBAAa;GAAG,mCAAgC;GACjD,UAAA;EACC,CAAA,IACF,MAIJ,oBAAC,MAAD;GAAI,MAAK;GAAO,gCAA6B;GAAG,cAAY,QAAQ,iBAAiB,KAAA;GAClF,UAAA,SAAS,KAAK,MAAM,UACnB,oBAAC,MAAD;IAAgB,0BAAuB;IAAG,+BAA6B,KAAK;IACzE,UAAA,KAAK,QAAQ,KAAA,IACZ,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,MAAD;KAAM,MAAM,KAAK;KAAQ,+BAA4B;IAAI,CAAA,GACzD,oBAAC,QAAD;KAAM,mBAAgB;KAAG,WAAU;KAChC,UAAA,KAAK;IACF,CAAA,CACN,EAAA,CAAA,IAEF,oBAAC,OAAD;KAAO,KAAK,KAAK;KAAK,KAAK,KAAK;KAAO,+BAA4B;IAAI,CAAA;GAEvE,GAXK,KAWL,CACL;EACC,CAAA,CACD;;AAET"}
1
+ {"version":3,"file":"payment-methods.js","names":[],"sources":["../../../src/particles/payment-methods/payment-methods.tsx"],"sourcesContent":["import { type ContentValue, defineComponent, defineContentType } from '@vtex/fs-core';\nimport type { ComponentPropsWithRef } from 'react';\nimport { Icon } from '../icon/icon.js';\nimport { Image } from '../image/image.js';\n\n// ---------------------------------------------------------------------------\n// Content model\n// ---------------------------------------------------------------------------\n\nexport const PaymentMethodsContent = defineContentType({\n id: 'payment-methods',\n title: 'Payment Methods',\n description: 'Accepted payment methods shown as a strip of icons.',\n schema: {\n type: 'object',\n additionalProperties: false,\n // Neither property is required. A missing element key already omits the component before any\n // required check runs, so requiring them would buy nothing and cost the prop fallback: an entry\n // authoring one field alone would be shape-invalid and take the whole element down with it.\n properties: {\n heading: { type: 'string', title: 'Heading' },\n methods: {\n type: 'array',\n title: 'Payment methods',\n // Author-time guidance only: no read-time gate enforces item bounds, so its one consumer is\n // the Content Platform Admin's form. An empty list still reaches the component, which\n // self-hides on it. No ceiling — a merchant lists every method they accept.\n minItems: 1,\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['label'],\n properties: {\n // Inline rather than a named constant: the catalog scanner reads this file statically\n // and rejects an identifier it cannot evaluate. `PAYMENT_METHOD_NAMES` derives from here.\n method: {\n type: 'string',\n enum: [\n 'visa',\n 'amex',\n 'apple-pay',\n 'elo-card',\n 'diners',\n 'google-pay',\n 'mastercard',\n 'paypal',\n 'pix',\n 'stripe',\n ],\n title: 'Method',\n },\n label: { type: 'string', minLength: 1, title: 'Label' },\n iconUrl: {\n type: 'string',\n format: 'uri',\n title: 'Custom icon',\n widget: { 'ui:widget': 'media-gallery' },\n },\n },\n },\n },\n },\n },\n});\n\n/**\n * The payment glyphs the base icon dialect carries, and so the only names `method` admits. Exported\n * because a store's foundation has to list them under `icons.include` for a CMS-chosen name to\n * resolve at runtime — one import there instead of ten retyped strings. Reading it off the model\n * rather than declaring it beside one makes the two impossible to drift apart.\n */\nexport const PAYMENT_METHOD_NAMES = PaymentMethodsContent.schema.properties.methods.items.properties.method.enum;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype PaymentMethodsContentValue = ContentValue<typeof PaymentMethodsContent>;\n\n/** One row of the strip. Derived from the model so the prop and content channels cannot drift. */\nexport type PaymentMethodItem = NonNullable<PaymentMethodsContentValue['methods']>[number];\n\ntype PaymentMethodName = NonNullable<PaymentMethodItem['method']>;\n\nexport type PaymentMethodsProps = ComponentPropsWithRef<'div'> & {\n /** Authored fallback for the section heading, used when no CMS entry supplies one. */\n heading?: string;\n /** Authored fallback for the accepted set, used when no CMS entry supplies one. */\n methods?: readonly PaymentMethodItem[];\n /** Injected by the compiler, sliced from the page's or layout's composite content entry. */\n $content?: PaymentMethodsContentValue;\n};\n\n// ---------------------------------------------------------------------------\n// Definition\n// ---------------------------------------------------------------------------\n\nexport const PaymentMethodsDefinition = defineComponent({\n name: 'PaymentMethods',\n importPath: '@vtex/faststore/components',\n component: PaymentMethods,\n content: PaymentMethodsContent,\n editor: { label: 'Payment Methods', category: 'content' },\n schema: {\n heading: { type: 'string', label: 'Heading' },\n methods: { type: 'array', label: 'Payment methods' },\n },\n guideline:\n \"The store's accepted payment methods as a strip of flags, both the heading and the list CMS-editable. Not PricingPaymentMethodsList, which renders a product's installment plans from commerce data. Every predefined method name must be listed in the store's icons.include. See payment-methods.md.\",\n});\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * A method that can render: a non-blank label plus exactly one glyph source. The two members are\n * what the union encodes — an upload names itself through `alt`, while a sprite glyph is decorative\n * and needs the label alongside it.\n */\ntype ResolvedMethod =\n | { label: string; method: PaymentMethodName; src?: undefined }\n | { label: string; method?: PaymentMethodName; src: string };\n\n/**\n * Drops every item that cannot render both a glyph and an accessible name. Neither read-time gate\n * enforces `minLength`, so a blank label reaches the component, and `method` is optional so that\n * Boleto can arrive through `iconUrl` — which leaves an item carrying neither, also legal and also\n * unrenderable. An icon with no name is the one thing F5 must not ship.\n */\nfunction resolveMethods(methods: readonly PaymentMethodItem[]): ResolvedMethod[] {\n const resolved: ResolvedMethod[] = [];\n\n for (const item of methods) {\n if (!item.label.trim()) continue;\n\n if (item.iconUrl?.trim()) {\n resolved.push({ label: item.label, method: item.method, src: item.iconUrl });\n } else if (item.method) {\n resolved.push({ label: item.label, method: item.method });\n }\n }\n\n return resolved;\n}\n\nexport function PaymentMethods({ heading, methods, $content, className, ...props }: PaymentMethodsProps) {\n const sectionHeading = $content?.heading ?? heading;\n const resolved = resolveMethods($content?.methods ?? methods ?? []);\n\n if (resolved.length === 0) return null;\n\n // A blank heading is unconfigured, not an empty value: no element and no group name, rather than\n // an empty one. Trim to decide, never to render.\n const named = Boolean(sectionHeading?.trim());\n\n return (\n <div {...props} className={className} data-fs-payment-methods=\"\">\n {named ? (\n <h3 data-fs-text=\"\" data-fs-payment-methods-heading=\"\">\n {sectionHeading}\n </h3>\n ) : null}\n {/* biome-ignore lint/a11y/noRedundantRoles: WebKit drops the implicit list role from a ul whose\n computed `list-style` is `none` — exactly what this component's swatch sets — so the explicit\n role is what holds the semantics the named list was chosen for. */}\n <ul role=\"list\" data-fs-payment-methods-list=\"\" aria-label={named ? sectionHeading : undefined}>\n {resolved.map((item, index) => (\n <li key={index} data-fs-payment-method=\"\" data-fs-payment-method-name={item.method}>\n {item.src === undefined ? (\n <>\n <Icon name={item.method} data-fs-payment-method-icon=\"\" />\n <span data-fs-sr-only=\"\" className=\"sr-only\">\n {item.label}\n </span>\n </>\n ) : (\n <Image src={item.src} alt={item.label} data-fs-payment-method-icon=\"\" />\n )}\n </li>\n ))}\n </ul>\n </div>\n );\n}\n"],"mappings":";;;;;;;AASA,MAAa,wBAAwB,kBAAkB;CACrD,IAAI;CACJ,OAAO;CACP,aAAa;CACb,QAAQ;EACN,MAAM;EACN,sBAAsB;EAItB,YAAY;GACV,SAAS;IAAE,MAAM;IAAU,OAAO;GAAU;GAC5C,SAAS;IACP,MAAM;IACN,OAAO;IAIP,UAAU;IACV,OAAO;KACL,MAAM;KACN,sBAAsB;KACtB,UAAU,CAAC,OAAO;KAClB,YAAY;MAGV,QAAQ;OACN,MAAM;OACN,MAAM;QACJ;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;OACF;OACA,OAAO;MACT;MACA,OAAO;OAAE,MAAM;OAAU,WAAW;OAAG,OAAO;MAAQ;MACtD,SAAS;OACP,MAAM;OACN,QAAQ;OACR,OAAO;OACP,QAAQ,EAAE,aAAa,gBAAgB;MACzC;KACF;IACF;GACF;EACF;CACF;AACF,CAAC;;;;;;;AAQD,MAAa,uBAAuB,sBAAsB,OAAO,WAAW,QAAQ,MAAM,WAAW,OAAO;AA0B5G,MAAa,2BAA2B,gBAAgB;CACtD,MAAM;CACN,YAAY;CACZ,WAAW;CACX,SAAS;CACT,QAAQ;EAAE,OAAO;EAAmB,UAAU;CAAU;CACxD,QAAQ;EACN,SAAS;GAAE,MAAM;GAAU,OAAO;EAAU;EAC5C,SAAS;GAAE,MAAM;GAAS,OAAO;EAAkB;CACrD;CACA,WACE;AACJ,CAAC;;;;;;;AAqBD,SAAS,eAAe,SAAyD;CAC/E,MAAM,WAA6B,CAAC;CAEpC,KAAK,MAAM,QAAQ,SAAS;EAC1B,IAAI,CAAC,KAAK,MAAM,KAAK,GAAG;EAExB,IAAI,KAAK,SAAS,KAAK,GACrB,SAAS,KAAK;GAAE,OAAO,KAAK;GAAO,QAAQ,KAAK;GAAQ,KAAK,KAAK;EAAQ,CAAC;OACtE,IAAI,KAAK,QACd,SAAS,KAAK;GAAE,OAAO,KAAK;GAAO,QAAQ,KAAK;EAAO,CAAC;CAE5D;CAEA,OAAO;AACT;AAEA,SAAgB,eAAe,EAAE,SAAS,SAAS,UAAU,WAAW,GAAG,SAA8B;CACvG,MAAM,iBAAiB,UAAU,WAAW;CAC5C,MAAM,WAAW,eAAe,UAAU,WAAW,WAAW,CAAC,CAAC;CAElE,IAAI,SAAS,WAAW,GAAG,OAAO;CAIlC,MAAM,QAAQ,QAAQ,gBAAgB,KAAK,CAAC;CAE5C,OACE,qBAAC,OAAD;EAAK,GAAI;EAAkB;EAAW,2BAAwB;EAA9D,UAAA,CACG,QACC,oBAAC,MAAD;GAAI,gBAAa;GAAG,mCAAgC;GACjD,UAAA;EACC,CAAA,IACF,MAIJ,oBAAC,MAAD;GAAI,MAAK;GAAO,gCAA6B;GAAG,cAAY,QAAQ,iBAAiB,KAAA;GAClF,UAAA,SAAS,KAAK,MAAM,UACnB,oBAAC,MAAD;IAAgB,0BAAuB;IAAG,+BAA6B,KAAK;IACzE,UAAA,KAAK,QAAQ,KAAA,IACZ,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,MAAD;KAAM,MAAM,KAAK;KAAQ,+BAA4B;IAAI,CAAA,GACzD,oBAAC,QAAD;KAAM,mBAAgB;KAAG,WAAU;KAChC,UAAA,KAAK;IACF,CAAA,CACN,EAAA,CAAA,IAEF,oBAAC,OAAD;KAAO,KAAK,KAAK;KAAK,KAAK,KAAK;KAAO,+BAA4B;IAAI,CAAA;GAEvE,GAXK,KAWL,CACL;EACC,CAAA,CACD;;AAET"}
@@ -1,10 +1,12 @@
1
1
  import { ComponentDefinition } from "../../core/dist/define-component.js";
2
2
  import "../../core/dist/index.js";
3
3
  import { ComponentPropsWithRef, ReactNode } from "react";
4
- import { Toast, ToastManager, ToastManager as ToastManager$1, ToastRootToastObject } from "@base-ui/react/toast";
4
+ import { Toast, ToastManager, ToastManager as ToastManager$1, ToastRootToastObject, UseToastManagerReturnValue } from "@base-ui/react/toast";
5
5
  //#region src/particles/toast/toast.d.ts
6
6
  /** The queued toast object `useToast().toasts` returns and `ToastRoot` renders. */
7
7
  export type ToastObject = ToastRootToastObject;
8
+ /** What `useOptionalToast` returns under a provider; same shape as `useToast()`. */
9
+ export type ToastManagerValue = UseToastManagerReturnValue;
8
10
  type SwipeDirection = 'up' | 'down' | 'left' | 'right';
9
11
  export type ToastProviderProps = Readonly<{
10
12
  /** Max concurrent toasts before older ones are marked limited. */
@@ -21,13 +23,17 @@ export type ToastPortalProps = Readonly<{
21
23
  }>;
22
24
  export type ToastViewportProps = ComponentPropsWithRef<'div'>;
23
25
  export type ToastRootProps = Readonly<{
24
- /** The queued toast to render, from `useToast().toasts`. */
25
- toast: ToastObject;
26
+ /** The queued toast to render. Defaults to the toast `ToastList` publishes. */
27
+ toast?: ToastObject;
26
28
  /** Directions the toast can be swiped to dismiss. @default ['down', 'right'] */
27
29
  swipeDirection?: SwipeDirection | SwipeDirection[];
28
30
  className?: string;
29
31
  children?: ReactNode;
30
32
  }>;
33
+ export type ToastListProps = Readonly<{
34
+ /** The per-toast template, rendered once per queued toast. */
35
+ children?: ReactNode;
36
+ }>;
31
37
  export type ToastTitleProps = ComponentPropsWithRef<'h2'>;
32
38
  export type ToastDescriptionProps = ComponentPropsWithRef<'p'>;
33
39
  export type ToastActionProps = ComponentPropsWithRef<'button'>;
@@ -47,21 +53,28 @@ export declare const ToastPortalDefinition: ComponentDefinition<Readonly<{
47
53
  }>>;
48
54
  export declare const ToastViewportDefinition: ComponentDefinition<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>>;
49
55
  export declare const ToastRootDefinition: ComponentDefinition<Readonly<{
50
- /** The queued toast to render, from `useToast().toasts`. */
51
- toast: ToastObject;
56
+ /** The queued toast to render. Defaults to the toast `ToastList` publishes. */
57
+ toast?: ToastObject;
52
58
  /** Directions the toast can be swiped to dismiss. @default ['down', 'right'] */
53
59
  swipeDirection?: SwipeDirection | SwipeDirection[];
54
60
  className?: string;
55
61
  children?: ReactNode;
56
62
  }>>;
63
+ export declare const ToastListDefinition: ComponentDefinition<Readonly<{
64
+ /** The per-toast template, rendered once per queued toast. */
65
+ children?: ReactNode;
66
+ }>>;
57
67
  export declare const ToastTitleDefinition: ComponentDefinition<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>>;
58
68
  export declare const ToastDescriptionDefinition: ComponentDefinition<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>>;
59
69
  export declare const ToastActionDefinition: ComponentDefinition<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>>;
60
70
  export declare const ToastCloseDefinition: ComponentDefinition<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>>;
61
71
  export declare function ToastProvider({ limit, timeout, toastManager, children }: ToastProviderProps): import("react/jsx-runtime").JSX.Element;
72
+ /** The toast manager under a `ToastProvider`, or `null` without one. Never throws. */
73
+ export declare function useOptionalToast(): ToastManagerValue | null;
74
+ export declare function ToastList({ children }: ToastListProps): import("react/jsx-runtime").JSX.Element | null;
62
75
  export declare function ToastPortal({ className, children }: ToastPortalProps): import("react/jsx-runtime").JSX.Element;
63
76
  export declare function ToastViewport({ className, children, 'aria-label': ariaLabel, ...props }: ToastViewportProps): import("react/jsx-runtime").JSX.Element;
64
- export declare function ToastRoot({ toast, swipeDirection, className, children }: ToastRootProps): import("react/jsx-runtime").JSX.Element;
77
+ export declare function ToastRoot({ toast: toastProp, swipeDirection, className, children }: ToastRootProps): import("react/jsx-runtime").JSX.Element | null;
65
78
  export declare function ToastTitle({ className, children, ...props }: ToastTitleProps): import("react/jsx-runtime").JSX.Element;
66
79
  export declare function ToastDescription({ className, children, ...props }: ToastDescriptionProps): import("react/jsx-runtime").JSX.Element;
67
80
  export declare function ToastAction({ className, children, ...props }: ToastActionProps): import("react/jsx-runtime").JSX.Element;
@@ -72,6 +85,7 @@ declare const Toast$1: {
72
85
  Provider: typeof ToastProvider;
73
86
  Portal: typeof ToastPortal;
74
87
  Viewport: typeof ToastViewport;
88
+ List: typeof ToastList;
75
89
  Root: typeof ToastRoot;
76
90
  Title: typeof ToastTitle;
77
91
  Description: typeof ToastDescription;
@@ -2,10 +2,14 @@
2
2
  import { defineComponent } from "../../core/dist/define-component.js";
3
3
  import "../../core/dist/index.js";
4
4
  import { useIntl } from "../../sdk/i18n/client.js";
5
- import { Children } from "react";
6
- import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { Children, createContext, useContext } from "react";
6
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
7
  import { Toast } from "@base-ui/react/toast";
8
8
  //#region src/particles/toast/toast.tsx
9
+ /** Set by `ToastProvider`; `null` outside one. */
10
+ const ToastManagerContext = createContext(null);
11
+ /** Set by `ToastList` for each queued toast; read by `ToastRoot` when its `toast` prop is absent. */
12
+ const ToastItemContext = createContext(null);
9
13
  const ToastProviderDefinition = defineComponent({
10
14
  name: "ToastProvider",
11
15
  importPath: "@vtex/faststore/components",
@@ -26,7 +30,8 @@ const ToastProviderDefinition = defineComponent({
26
30
  label: "Default Auto-Dismiss (ms)"
27
31
  }
28
32
  },
29
- guideline: "Mount once at the layout root to enable toasts anywhere in the tree. Compose as: ToastProvider > ToastPortal > ToastViewport (a childless viewport renders the active toasts itself; add ToastRoot children only to override). Components trigger toasts via the useToast() hook, not through page-schema composition."
33
+ providesContext: "toast",
34
+ guideline: "Mount once at the layout root, above the Slot. Compose as: ToastProvider > ToastPortal > ToastViewport. A childless ToastViewport renders the active toasts itself; to control the markup, put a ToastList with a ToastRoot template inside it. Code raises toasts with useOptionalToast() (null without a provider) or useToast() (throws without one)."
30
35
  });
31
36
  const ToastPortalDefinition = defineComponent({
32
37
  name: "ToastPortal",
@@ -48,7 +53,7 @@ const ToastViewportDefinition = defineComponent({
48
53
  category: "layout"
49
54
  },
50
55
  schema: {},
51
- guideline: "The fixed-position stack container for all active toasts. With no children it renders every active toast itself (title, description, action and a localized close button), so a page schema needs only ToastProvider > ToastPortal > ToastViewport. Pass children (a ToastRoot per useToast().toasts entry) to take control of the markup. Position it with a swatch on data-fs-toast-viewport (e.g. bottom-right vs. top-center). Direct child of ToastPortal."
56
+ guideline: "The fixed-position stack container for all active toasts. With no children it renders every active toast itself (title, description, action and a localized close button), so a page schema needs only ToastProvider > ToastPortal > ToastViewport. Pass children to take control of the markup: a ToastList with a ToastRoot template is the schema-composable way (it renders the template once per queued toast), or map useToast().toasts to ToastRoot in code. Position it with a swatch on data-fs-toast-viewport (e.g. bottom-right vs. top-center). Direct child of ToastPortal."
52
57
  });
53
58
  const ToastRootDefinition = defineComponent({
54
59
  name: "ToastRoot",
@@ -59,7 +64,21 @@ const ToastRootDefinition = defineComponent({
59
64
  category: "layout"
60
65
  },
61
66
  schema: {},
62
- guideline: "One instance per active toast. A childless ToastViewport renders these itself; compose ToastRoot only to override that, by mapping over useToast().toasts. Compose as: ToastRoot > (Icon + ToastTitle + ToastDescription + ToastAction + ToastClose), all optional except at least one of Title/Description."
67
+ requiresContext: "toast-item",
68
+ guideline: "One instance per active toast. Inside a ToastList it is the per-toast template and reads its toast from the list; a childless ToastViewport renders its own ToastRoot per toast, so compose one only to override that. Compose as: ToastRoot > (Icon + ToastTitle + ToastDescription + ToastAction + ToastClose), all optional except at least one of Title/Description."
69
+ });
70
+ const ToastListDefinition = defineComponent({
71
+ name: "ToastList",
72
+ importPath: "@vtex/faststore/components",
73
+ component: ToastList,
74
+ editor: {
75
+ label: "Toast List",
76
+ category: "layout"
77
+ },
78
+ schema: {},
79
+ requiresContext: "toast",
80
+ providesContext: "toast-item",
81
+ guideline: "Renders its children once per queued toast; place a ToastRoot (with ToastTitle and ToastDescription, both auto-filled from the toast) inside it as the template. With no children it renders that template by default. Direct child of ToastViewport; renders nothing without a ToastProvider ancestor."
63
82
  });
64
83
  const ToastTitleDefinition = defineComponent({
65
84
  name: "ToastTitle",
@@ -110,9 +129,28 @@ function ToastProvider({ limit = 3, timeout = 5e3, toastManager, children }) {
110
129
  limit,
111
130
  timeout,
112
131
  toastManager,
132
+ children: /* @__PURE__ */ jsx(ToastManagerBridge, { children })
133
+ });
134
+ }
135
+ function ToastManagerBridge({ children }) {
136
+ const manager = Toast.useToastManager();
137
+ return /* @__PURE__ */ jsx(ToastManagerContext.Provider, {
138
+ value: manager,
113
139
  children
114
140
  });
115
141
  }
142
+ /** The toast manager under a `ToastProvider`, or `null` without one. Never throws. */
143
+ function useOptionalToast() {
144
+ return useContext(ToastManagerContext);
145
+ }
146
+ function ToastList({ children }) {
147
+ const manager = useOptionalToast();
148
+ if (!manager) return null;
149
+ return /* @__PURE__ */ jsx(Fragment$1, { children: manager.toasts.map((toast) => /* @__PURE__ */ jsx(ToastItemContext.Provider, {
150
+ value: toast,
151
+ children: children ?? /* @__PURE__ */ jsxs(ToastRoot, { children: [/* @__PURE__ */ jsx(ToastTitle, {}), /* @__PURE__ */ jsx(ToastDescription, {})] })
152
+ }, toast.id)) });
153
+ }
116
154
  function ToastPortal({ className, children }) {
117
155
  return /* @__PURE__ */ jsx(Toast.Portal, {
118
156
  className,
@@ -155,7 +193,10 @@ function ToastViewport({ className, children, "aria-label": ariaLabel, ...props
155
193
  })
156
194
  });
157
195
  }
158
- function ToastRoot({ toast, swipeDirection, className, children }) {
196
+ function ToastRoot({ toast: toastProp, swipeDirection, className, children }) {
197
+ const itemToast = useContext(ToastItemContext);
198
+ const toast = toastProp ?? itemToast;
199
+ if (!toast) return null;
159
200
  return /* @__PURE__ */ jsx(Toast.Root, {
160
201
  toast,
161
202
  swipeDirection,
@@ -208,6 +249,7 @@ const Toast$1 = {
208
249
  Provider: ToastProvider,
209
250
  Portal: ToastPortal,
210
251
  Viewport: ToastViewport,
252
+ List: ToastList,
211
253
  Root: ToastRoot,
212
254
  Title: ToastTitle,
213
255
  Description: ToastDescription,
@@ -215,6 +257,6 @@ const Toast$1 = {
215
257
  Close: ToastClose
216
258
  };
217
259
  //#endregion
218
- export { Toast$1 as Toast, ToastAction, ToastActionDefinition, ToastClose, ToastCloseDefinition, ToastDescription, ToastDescriptionDefinition, ToastPortal, ToastPortalDefinition, ToastProvider, ToastProviderDefinition, ToastRoot, ToastRootDefinition, ToastTitle, ToastTitleDefinition, ToastViewport, ToastViewportDefinition, useToast };
260
+ export { Toast$1 as Toast, ToastAction, ToastActionDefinition, ToastClose, ToastCloseDefinition, ToastDescription, ToastDescriptionDefinition, ToastList, ToastListDefinition, ToastPortal, ToastPortalDefinition, ToastProvider, ToastProviderDefinition, ToastRoot, ToastRootDefinition, ToastTitle, ToastTitleDefinition, ToastViewport, ToastViewportDefinition, useOptionalToast, useToast };
219
261
 
220
262
  //# sourceMappingURL=toast.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"toast.js","names":["BaseToast","Toast"],"sources":["../../../src/particles/toast/toast.tsx"],"sourcesContent":["'use client';\nimport type { ToastManager, ToastRootToastObject } from '@base-ui/react/toast';\nimport { Toast as BaseToast } from '@base-ui/react/toast';\nimport { defineComponent } from '@vtex/fs-core';\nimport type { ComponentPropsWithRef, ReactNode } from 'react';\nimport { Children } from 'react';\nimport { useIntl } from '../../sdk/i18n/client.js';\n\n/** The queued toast object `useToast().toasts` returns and `ToastRoot` renders. */\nexport type ToastObject = ToastRootToastObject;\nexport type { ToastManager } from '@base-ui/react/toast';\n\ntype SwipeDirection = 'up' | 'down' | 'left' | 'right';\n\nexport type ToastProviderProps = Readonly<{\n /** Max concurrent toasts before older ones are marked limited. */\n limit?: number;\n /** Default auto-dismiss delay (ms); `0` disables. */\n timeout?: number;\n /** External manager for triggering toasts outside React (e.g. a non-component module). */\n toastManager?: ToastManager;\n children?: ReactNode;\n}>;\n\nexport type ToastPortalProps = Readonly<{\n className?: string;\n children?: ReactNode;\n}>;\n\nexport type ToastViewportProps = ComponentPropsWithRef<'div'>;\n\nexport type ToastRootProps = Readonly<{\n /** The queued toast to render, from `useToast().toasts`. */\n toast: ToastObject;\n /** Directions the toast can be swiped to dismiss. @default ['down', 'right'] */\n swipeDirection?: SwipeDirection | SwipeDirection[];\n className?: string;\n children?: ReactNode;\n}>;\n\nexport type ToastTitleProps = ComponentPropsWithRef<'h2'>;\n\nexport type ToastDescriptionProps = ComponentPropsWithRef<'p'>;\n\nexport type ToastActionProps = ComponentPropsWithRef<'button'>;\n\nexport type ToastCloseProps = ComponentPropsWithRef<'button'>;\n\nexport const ToastProviderDefinition = defineComponent<ToastProviderProps>({\n name: 'ToastProvider',\n importPath: '@vtex/faststore/components',\n component: ToastProvider,\n editor: { label: 'Toast Provider', category: 'layout' },\n schema: {\n limit: { type: 'number', default: 3, label: 'Max Concurrent Toasts' },\n timeout: { type: 'number', default: 5000, label: 'Default Auto-Dismiss (ms)' },\n },\n guideline:\n 'Mount once at the layout root to enable toasts anywhere in the tree. Compose as: ToastProvider > ToastPortal > ToastViewport (a childless viewport renders the active toasts itself; add ToastRoot children only to override). Components trigger toasts via the useToast() hook, not through page-schema composition.',\n});\n\nexport const ToastPortalDefinition = defineComponent<ToastPortalProps>({\n name: 'ToastPortal',\n importPath: '@vtex/faststore/components',\n component: ToastPortal,\n editor: { label: 'Toast Portal', category: 'layout' },\n schema: {},\n guideline:\n 'Renders its children (ToastViewport) outside the DOM tree, into document.body. Direct child of ToastProvider.',\n});\n\nexport const ToastViewportDefinition = defineComponent<ToastViewportProps>({\n name: 'ToastViewport',\n importPath: '@vtex/faststore/components',\n component: ToastViewport,\n editor: { label: 'Toast Viewport', category: 'layout' },\n schema: {},\n guideline:\n 'The fixed-position stack container for all active toasts. With no children it renders every active toast itself (title, description, action and a localized close button), so a page schema needs only ToastProvider > ToastPortal > ToastViewport. Pass children (a ToastRoot per useToast().toasts entry) to take control of the markup. Position it with a swatch on data-fs-toast-viewport (e.g. bottom-right vs. top-center). Direct child of ToastPortal.',\n});\n\nexport const ToastRootDefinition = defineComponent<ToastRootProps>({\n name: 'ToastRoot',\n importPath: '@vtex/faststore/components',\n component: ToastRoot,\n editor: { label: 'Toast Root', category: 'layout' },\n schema: {},\n guideline:\n 'One instance per active toast. A childless ToastViewport renders these itself; compose ToastRoot only to override that, by mapping over useToast().toasts. Compose as: ToastRoot > (Icon + ToastTitle + ToastDescription + ToastAction + ToastClose), all optional except at least one of Title/Description.',\n});\n\nexport const ToastTitleDefinition = defineComponent<ToastTitleProps>({\n name: 'ToastTitle',\n importPath: '@vtex/faststore/components',\n component: ToastTitle,\n editor: { label: 'Toast Title', category: 'layout' },\n schema: {},\n guideline: 'Renders the toast.title automatically when composed with no children. Direct child of ToastRoot.',\n});\n\nexport const ToastDescriptionDefinition = defineComponent<ToastDescriptionProps>({\n name: 'ToastDescription',\n importPath: '@vtex/faststore/components',\n component: ToastDescription,\n editor: { label: 'Toast Description', category: 'layout' },\n schema: {},\n guideline:\n 'Renders the toast.description automatically when composed with no children. Direct child of ToastRoot, after ToastTitle.',\n});\n\nexport const ToastActionDefinition = defineComponent<ToastActionProps>({\n name: 'ToastAction',\n importPath: '@vtex/faststore/components',\n component: ToastAction,\n editor: { label: 'Toast Action', category: 'action' },\n schema: {},\n guideline: \"Reversible-action button (e.g. Undo) forwarding the toast's actionProps. Omit for toasts with no action.\",\n});\n\nexport const ToastCloseDefinition = defineComponent<ToastCloseProps>({\n name: 'ToastClose',\n importPath: '@vtex/faststore/components',\n component: ToastClose,\n editor: { label: 'Toast Close', category: 'action' },\n schema: {},\n guideline: 'Dismisses the nearest ancestor ToastRoot when clicked. Can appear alongside or instead of ToastAction.',\n});\n\nexport function ToastProvider({ limit = 3, timeout = 5000, toastManager, children }: ToastProviderProps) {\n return (\n <BaseToast.Provider limit={limit} timeout={timeout} toastManager={toastManager}>\n {children}\n </BaseToast.Provider>\n );\n}\n\nexport function ToastPortal({ className, children }: ToastPortalProps) {\n return <BaseToast.Portal className={className}>{children}</BaseToast.Portal>;\n}\n\nfunction hasChildren(children: ReactNode): boolean {\n return Children.toArray(children).length > 0;\n}\n\nfunction DefaultToastList() {\n const intl = useIntl();\n const { toasts } = useToast();\n\n return toasts.map((toast) => (\n <ToastRoot key={toast.id} toast={toast}>\n <ToastTitle />\n <ToastDescription />\n <ToastAction />\n <ToastClose aria-label={intl.formatMessage({ id: 'fs/toast.dismiss' })}>\n <span aria-hidden=\"true\">×</span>\n </ToastClose>\n </ToastRoot>\n ));\n}\n\nexport function ToastViewport({ className, children, 'aria-label': ariaLabel, ...props }: ToastViewportProps) {\n const intl = useIntl();\n\n return (\n <BaseToast.Viewport\n {...props}\n aria-label={ariaLabel ?? intl.formatMessage({ id: 'fs/toast.region' })}\n className={className}\n render={(renderProps, state) => (\n <div\n {...renderProps}\n data-fs-toast-viewport=\"\"\n data-fs-toast-viewport-expanded={state.expanded ? '' : undefined}\n >\n {hasChildren(children) ? children : <DefaultToastList />}\n </div>\n )}\n />\n );\n}\n\n// Uses Base UI's `render` prop (rather than plain prop-forwarding, unlike the rest of this file) so\n// `data-fs-toast-root-type`/`-limited`/`-swiping` can mirror internal render state (`state.type`,\n// `state.limited`, `state.swiping`) that is never exposed back to us as a prop.\nexport function ToastRoot({ toast, swipeDirection, className, children }: ToastRootProps) {\n return (\n <BaseToast.Root\n toast={toast}\n swipeDirection={swipeDirection}\n className={className}\n render={(renderProps, state) => (\n <div\n {...renderProps}\n data-fs-toast-root=\"\"\n data-fs-toast-root-type={state.type}\n data-fs-toast-root-limited={state.limited ? '' : undefined}\n data-fs-toast-root-swiping={state.swiping ? '' : undefined}\n >\n {children}\n </div>\n )}\n />\n );\n}\n\nexport function ToastTitle({ className, children, ...props }: ToastTitleProps) {\n return (\n <BaseToast.Title {...props} className={className} data-fs-toast-title=\"\">\n {children}\n </BaseToast.Title>\n );\n}\n\nexport function ToastDescription({ className, children, ...props }: ToastDescriptionProps) {\n return (\n <BaseToast.Description {...props} className={className} data-fs-toast-description=\"\">\n {children}\n </BaseToast.Description>\n );\n}\n\nexport function ToastAction({ className, children, ...props }: ToastActionProps) {\n return (\n <BaseToast.Action {...props} className={className} data-fs-toast-action=\"\">\n {children}\n </BaseToast.Action>\n );\n}\n\nexport function ToastClose({ className, children, ...props }: ToastCloseProps) {\n return (\n <BaseToast.Close {...props} className={className} data-fs-toast-close=\"\">\n {children}\n </BaseToast.Close>\n );\n}\n\n/** Thin re-export of Base UI's `useToastManager()` — see Key Decision 1 in the spec. */\nexport const useToast = BaseToast.useToastManager;\n\nexport const Toast = {\n Provider: ToastProvider,\n Portal: ToastPortal,\n Viewport: ToastViewport,\n Root: ToastRoot,\n Title: ToastTitle,\n Description: ToastDescription,\n Action: ToastAction,\n Close: ToastClose,\n};\n"],"mappings":";;;;;;;;AAgDA,MAAa,0BAA0B,gBAAoC;CACzE,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAkB,UAAU;CAAS;CACtD,QAAQ;EACN,OAAO;GAAE,MAAM;GAAU,SAAS;GAAG,OAAO;EAAwB;EACpE,SAAS;GAAE,MAAM;GAAU,SAAS;GAAM,OAAO;EAA4B;CAC/E;CACA,WACE;AACJ,CAAC;AAED,MAAa,wBAAwB,gBAAkC;CACrE,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAgB,UAAU;CAAS;CACpD,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,MAAa,0BAA0B,gBAAoC;CACzE,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAkB,UAAU;CAAS;CACtD,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,MAAa,sBAAsB,gBAAgC;CACjE,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAc,UAAU;CAAS;CAClD,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,MAAa,uBAAuB,gBAAiC;CACnE,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAe,UAAU;CAAS;CACnD,QAAQ,CAAC;CACT,WAAW;AACb,CAAC;AAED,MAAa,6BAA6B,gBAAuC;CAC/E,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAqB,UAAU;CAAS;CACzD,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,MAAa,wBAAwB,gBAAkC;CACrE,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAgB,UAAU;CAAS;CACpD,QAAQ,CAAC;CACT,WAAW;AACb,CAAC;AAED,MAAa,uBAAuB,gBAAiC;CACnE,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAe,UAAU;CAAS;CACnD,QAAQ,CAAC;CACT,WAAW;AACb,CAAC;AAED,SAAgB,cAAc,EAAE,QAAQ,GAAG,UAAU,KAAM,cAAc,YAAgC;CACvG,OACE,oBAACA,MAAU,UAAX;EAA2B;EAAgB;EAAuB;EAC/D;CACiB,CAAA;AAExB;AAEA,SAAgB,YAAY,EAAE,WAAW,YAA8B;CACrE,OAAO,oBAACA,MAAU,QAAX;EAA6B;EAAY;CAA2B,CAAA;AAC7E;AAEA,SAAS,YAAY,UAA8B;CACjD,OAAO,SAAS,QAAQ,QAAQ,CAAC,CAAC,SAAS;AAC7C;AAEA,SAAS,mBAAmB;CAC1B,MAAM,OAAO,QAAQ;CACrB,MAAM,EAAE,WAAW,SAAS;CAE5B,OAAO,OAAO,KAAK,UACjB,qBAAC,WAAD;EAAiC;EAAjC,UAAA;GACE,oBAAC,YAAD,CAAa,CAAA;GACb,oBAAC,kBAAD,CAAmB,CAAA;GACnB,oBAAC,aAAD,CAAc,CAAA;GACd,oBAAC,YAAD;IAAY,cAAY,KAAK,cAAc,EAAE,IAAI,mBAAmB,CAAC;IACnE,UAAA,oBAAC,QAAD;KAAM,eAAY;KAAO,UAAA;IAAO,CAAA;GACtB,CAAA;EACH;CAPK,GAAA,MAAM,EAOX,CACZ;AACH;AAEA,SAAgB,cAAc,EAAE,WAAW,UAAU,cAAc,WAAW,GAAG,SAA6B;CAC5G,MAAM,OAAO,QAAQ;CAErB,OACE,oBAACA,MAAU,UAAX;EACE,GAAI;EACJ,cAAY,aAAa,KAAK,cAAc,EAAE,IAAI,kBAAkB,CAAC;EAC1D;EACX,SAAS,aAAa,UACpB,oBAAC,OAAD;GACE,GAAI;GACJ,0BAAuB;GACvB,mCAAiC,MAAM,WAAW,KAAK,KAAA;GAEtD,UAAA,YAAY,QAAQ,IAAI,WAAW,oBAAC,kBAAD,CAAmB,CAAA;EACpD,CAAA;CAER,CAAA;AAEL;AAKA,SAAgB,UAAU,EAAE,OAAO,gBAAgB,WAAW,YAA4B;CACxF,OACE,oBAACA,MAAU,MAAX;EACS;EACS;EACL;EACX,SAAS,aAAa,UACpB,oBAAC,OAAD;GACE,GAAI;GACJ,sBAAmB;GACnB,2BAAyB,MAAM;GAC/B,8BAA4B,MAAM,UAAU,KAAK,KAAA;GACjD,8BAA4B,MAAM,UAAU,KAAK,KAAA;GAEhD;EACE,CAAA;CAER,CAAA;AAEL;AAEA,SAAgB,WAAW,EAAE,WAAW,UAAU,GAAG,SAA0B;CAC7E,OACE,oBAACA,MAAU,OAAX;EAAiB,GAAI;EAAkB;EAAW,uBAAoB;EACnE;CACc,CAAA;AAErB;AAEA,SAAgB,iBAAiB,EAAE,WAAW,UAAU,GAAG,SAAgC;CACzF,OACE,oBAACA,MAAU,aAAX;EAAuB,GAAI;EAAkB;EAAW,6BAA0B;EAC/E;CACoB,CAAA;AAE3B;AAEA,SAAgB,YAAY,EAAE,WAAW,UAAU,GAAG,SAA2B;CAC/E,OACE,oBAACA,MAAU,QAAX;EAAkB,GAAI;EAAkB;EAAW,wBAAqB;EACrE;CACe,CAAA;AAEtB;AAEA,SAAgB,WAAW,EAAE,WAAW,UAAU,GAAG,SAA0B;CAC7E,OACE,oBAACA,MAAU,OAAX;EAAiB,GAAI;EAAkB;EAAW,uBAAoB;EACnE;CACc,CAAA;AAErB;;AAGA,MAAa,WAAWA,MAAU;AAElC,MAAaC,UAAQ;CACnB,UAAU;CACV,QAAQ;CACR,UAAU;CACV,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,OAAO;AACT"}
1
+ {"version":3,"file":"toast.js","names":["BaseToast","Toast"],"sources":["../../../src/particles/toast/toast.tsx"],"sourcesContent":["'use client';\nimport type { ToastManager, ToastRootToastObject, UseToastManagerReturnValue } from '@base-ui/react/toast';\nimport { Toast as BaseToast } from '@base-ui/react/toast';\nimport { defineComponent } from '@vtex/fs-core';\nimport type { ComponentPropsWithRef, ReactNode } from 'react';\nimport { Children, createContext, useContext } from 'react';\nimport { useIntl } from '../../sdk/i18n/client.js';\n\n/** The queued toast object `useToast().toasts` returns and `ToastRoot` renders. */\nexport type ToastObject = ToastRootToastObject;\nexport type { ToastManager } from '@base-ui/react/toast';\n\n/** What `useOptionalToast` returns under a provider; same shape as `useToast()`. */\nexport type ToastManagerValue = UseToastManagerReturnValue;\n\n/** Set by `ToastProvider`; `null` outside one. */\nconst ToastManagerContext = createContext<ToastManagerValue | null>(null);\n\n/** Set by `ToastList` for each queued toast; read by `ToastRoot` when its `toast` prop is absent. */\nconst ToastItemContext = createContext<ToastObject | null>(null);\n\ntype SwipeDirection = 'up' | 'down' | 'left' | 'right';\n\nexport type ToastProviderProps = Readonly<{\n /** Max concurrent toasts before older ones are marked limited. */\n limit?: number;\n /** Default auto-dismiss delay (ms); `0` disables. */\n timeout?: number;\n /** External manager for triggering toasts outside React (e.g. a non-component module). */\n toastManager?: ToastManager;\n children?: ReactNode;\n}>;\n\nexport type ToastPortalProps = Readonly<{\n className?: string;\n children?: ReactNode;\n}>;\n\nexport type ToastViewportProps = ComponentPropsWithRef<'div'>;\n\nexport type ToastRootProps = Readonly<{\n /** The queued toast to render. Defaults to the toast `ToastList` publishes. */\n toast?: ToastObject;\n /** Directions the toast can be swiped to dismiss. @default ['down', 'right'] */\n swipeDirection?: SwipeDirection | SwipeDirection[];\n className?: string;\n children?: ReactNode;\n}>;\n\nexport type ToastListProps = Readonly<{\n /** The per-toast template, rendered once per queued toast. */\n children?: ReactNode;\n}>;\n\nexport type ToastTitleProps = ComponentPropsWithRef<'h2'>;\n\nexport type ToastDescriptionProps = ComponentPropsWithRef<'p'>;\n\nexport type ToastActionProps = ComponentPropsWithRef<'button'>;\n\nexport type ToastCloseProps = ComponentPropsWithRef<'button'>;\n\nexport const ToastProviderDefinition = defineComponent<ToastProviderProps>({\n name: 'ToastProvider',\n importPath: '@vtex/faststore/components',\n component: ToastProvider,\n editor: { label: 'Toast Provider', category: 'layout' },\n schema: {\n limit: { type: 'number', default: 3, label: 'Max Concurrent Toasts' },\n timeout: { type: 'number', default: 5000, label: 'Default Auto-Dismiss (ms)' },\n },\n providesContext: 'toast',\n guideline:\n 'Mount once at the layout root, above the Slot. Compose as: ToastProvider > ToastPortal > ToastViewport. A childless ToastViewport renders the active toasts itself; to control the markup, put a ToastList with a ToastRoot template inside it. Code raises toasts with useOptionalToast() (null without a provider) or useToast() (throws without one).',\n});\n\nexport const ToastPortalDefinition = defineComponent<ToastPortalProps>({\n name: 'ToastPortal',\n importPath: '@vtex/faststore/components',\n component: ToastPortal,\n editor: { label: 'Toast Portal', category: 'layout' },\n schema: {},\n guideline:\n 'Renders its children (ToastViewport) outside the DOM tree, into document.body. Direct child of ToastProvider.',\n});\n\nexport const ToastViewportDefinition = defineComponent<ToastViewportProps>({\n name: 'ToastViewport',\n importPath: '@vtex/faststore/components',\n component: ToastViewport,\n editor: { label: 'Toast Viewport', category: 'layout' },\n schema: {},\n guideline:\n 'The fixed-position stack container for all active toasts. With no children it renders every active toast itself (title, description, action and a localized close button), so a page schema needs only ToastProvider > ToastPortal > ToastViewport. Pass children to take control of the markup: a ToastList with a ToastRoot template is the schema-composable way (it renders the template once per queued toast), or map useToast().toasts to ToastRoot in code. Position it with a swatch on data-fs-toast-viewport (e.g. bottom-right vs. top-center). Direct child of ToastPortal.',\n});\n\nexport const ToastRootDefinition = defineComponent<ToastRootProps>({\n name: 'ToastRoot',\n importPath: '@vtex/faststore/components',\n component: ToastRoot,\n editor: { label: 'Toast Root', category: 'layout' },\n schema: {},\n requiresContext: 'toast-item',\n guideline:\n 'One instance per active toast. Inside a ToastList it is the per-toast template and reads its toast from the list; a childless ToastViewport renders its own ToastRoot per toast, so compose one only to override that. Compose as: ToastRoot > (Icon + ToastTitle + ToastDescription + ToastAction + ToastClose), all optional except at least one of Title/Description.',\n});\n\nexport const ToastListDefinition = defineComponent<ToastListProps>({\n name: 'ToastList',\n importPath: '@vtex/faststore/components',\n component: ToastList,\n editor: { label: 'Toast List', category: 'layout' },\n schema: {},\n requiresContext: 'toast',\n providesContext: 'toast-item',\n guideline:\n 'Renders its children once per queued toast; place a ToastRoot (with ToastTitle and ToastDescription, both auto-filled from the toast) inside it as the template. With no children it renders that template by default. Direct child of ToastViewport; renders nothing without a ToastProvider ancestor.',\n});\n\nexport const ToastTitleDefinition = defineComponent<ToastTitleProps>({\n name: 'ToastTitle',\n importPath: '@vtex/faststore/components',\n component: ToastTitle,\n editor: { label: 'Toast Title', category: 'layout' },\n schema: {},\n guideline: 'Renders the toast.title automatically when composed with no children. Direct child of ToastRoot.',\n});\n\nexport const ToastDescriptionDefinition = defineComponent<ToastDescriptionProps>({\n name: 'ToastDescription',\n importPath: '@vtex/faststore/components',\n component: ToastDescription,\n editor: { label: 'Toast Description', category: 'layout' },\n schema: {},\n guideline:\n 'Renders the toast.description automatically when composed with no children. Direct child of ToastRoot, after ToastTitle.',\n});\n\nexport const ToastActionDefinition = defineComponent<ToastActionProps>({\n name: 'ToastAction',\n importPath: '@vtex/faststore/components',\n component: ToastAction,\n editor: { label: 'Toast Action', category: 'action' },\n schema: {},\n guideline: \"Reversible-action button (e.g. Undo) forwarding the toast's actionProps. Omit for toasts with no action.\",\n});\n\nexport const ToastCloseDefinition = defineComponent<ToastCloseProps>({\n name: 'ToastClose',\n importPath: '@vtex/faststore/components',\n component: ToastClose,\n editor: { label: 'Toast Close', category: 'action' },\n schema: {},\n guideline: 'Dismisses the nearest ancestor ToastRoot when clicked. Can appear alongside or instead of ToastAction.',\n});\n\nexport function ToastProvider({ limit = 3, timeout = 5000, toastManager, children }: ToastProviderProps) {\n return (\n <BaseToast.Provider limit={limit} timeout={timeout} toastManager={toastManager}>\n <ToastManagerBridge>{children}</ToastManagerBridge>\n </BaseToast.Provider>\n );\n}\n\nfunction ToastManagerBridge({ children }: Readonly<{ children?: ReactNode }>) {\n const manager = BaseToast.useToastManager();\n return <ToastManagerContext.Provider value={manager}>{children}</ToastManagerContext.Provider>;\n}\n\n/** The toast manager under a `ToastProvider`, or `null` without one. Never throws. */\nexport function useOptionalToast(): ToastManagerValue | null {\n return useContext(ToastManagerContext);\n}\n\nexport function ToastList({ children }: ToastListProps) {\n const manager = useOptionalToast();\n if (!manager) return null;\n return (\n <>\n {manager.toasts.map((toast) => (\n <ToastItemContext.Provider key={toast.id} value={toast}>\n {children ?? (\n <ToastRoot>\n <ToastTitle />\n <ToastDescription />\n </ToastRoot>\n )}\n </ToastItemContext.Provider>\n ))}\n </>\n );\n}\n\nexport function ToastPortal({ className, children }: ToastPortalProps) {\n return <BaseToast.Portal className={className}>{children}</BaseToast.Portal>;\n}\n\nfunction hasChildren(children: ReactNode): boolean {\n return Children.toArray(children).length > 0;\n}\n\nfunction DefaultToastList() {\n const intl = useIntl();\n const { toasts } = useToast();\n\n return toasts.map((toast) => (\n <ToastRoot key={toast.id} toast={toast}>\n <ToastTitle />\n <ToastDescription />\n <ToastAction />\n <ToastClose aria-label={intl.formatMessage({ id: 'fs/toast.dismiss' })}>\n <span aria-hidden=\"true\">×</span>\n </ToastClose>\n </ToastRoot>\n ));\n}\n\nexport function ToastViewport({ className, children, 'aria-label': ariaLabel, ...props }: ToastViewportProps) {\n const intl = useIntl();\n\n return (\n <BaseToast.Viewport\n {...props}\n aria-label={ariaLabel ?? intl.formatMessage({ id: 'fs/toast.region' })}\n className={className}\n render={(renderProps, state) => (\n <div\n {...renderProps}\n data-fs-toast-viewport=\"\"\n data-fs-toast-viewport-expanded={state.expanded ? '' : undefined}\n >\n {hasChildren(children) ? children : <DefaultToastList />}\n </div>\n )}\n />\n );\n}\n\n// Uses Base UI's `render` prop (rather than plain prop-forwarding, unlike the rest of this file) so\n// `data-fs-toast-root-type`/`-limited`/`-swiping` can mirror internal render state (`state.type`,\n// `state.limited`, `state.swiping`) that is never exposed back to us as a prop.\nexport function ToastRoot({ toast: toastProp, swipeDirection, className, children }: ToastRootProps) {\n const itemToast = useContext(ToastItemContext);\n const toast = toastProp ?? itemToast;\n if (!toast) return null;\n return (\n <BaseToast.Root\n toast={toast}\n swipeDirection={swipeDirection}\n className={className}\n render={(renderProps, state) => (\n <div\n {...renderProps}\n data-fs-toast-root=\"\"\n data-fs-toast-root-type={state.type}\n data-fs-toast-root-limited={state.limited ? '' : undefined}\n data-fs-toast-root-swiping={state.swiping ? '' : undefined}\n >\n {children}\n </div>\n )}\n />\n );\n}\n\nexport function ToastTitle({ className, children, ...props }: ToastTitleProps) {\n return (\n <BaseToast.Title {...props} className={className} data-fs-toast-title=\"\">\n {children}\n </BaseToast.Title>\n );\n}\n\nexport function ToastDescription({ className, children, ...props }: ToastDescriptionProps) {\n return (\n <BaseToast.Description {...props} className={className} data-fs-toast-description=\"\">\n {children}\n </BaseToast.Description>\n );\n}\n\nexport function ToastAction({ className, children, ...props }: ToastActionProps) {\n return (\n <BaseToast.Action {...props} className={className} data-fs-toast-action=\"\">\n {children}\n </BaseToast.Action>\n );\n}\n\nexport function ToastClose({ className, children, ...props }: ToastCloseProps) {\n return (\n <BaseToast.Close {...props} className={className} data-fs-toast-close=\"\">\n {children}\n </BaseToast.Close>\n );\n}\n\n/** Thin re-export of Base UI's `useToastManager()` — see Key Decision 1 in the spec. */\nexport const useToast = BaseToast.useToastManager;\n\nexport const Toast = {\n Provider: ToastProvider,\n Portal: ToastPortal,\n Viewport: ToastViewport,\n List: ToastList,\n Root: ToastRoot,\n Title: ToastTitle,\n Description: ToastDescription,\n Action: ToastAction,\n Close: ToastClose,\n};\n"],"mappings":";;;;;;;;;AAgBA,MAAM,sBAAsB,cAAwC,IAAI;;AAGxE,MAAM,mBAAmB,cAAkC,IAAI;AA2C/D,MAAa,0BAA0B,gBAAoC;CACzE,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAkB,UAAU;CAAS;CACtD,QAAQ;EACN,OAAO;GAAE,MAAM;GAAU,SAAS;GAAG,OAAO;EAAwB;EACpE,SAAS;GAAE,MAAM;GAAU,SAAS;GAAM,OAAO;EAA4B;CAC/E;CACA,iBAAiB;CACjB,WACE;AACJ,CAAC;AAED,MAAa,wBAAwB,gBAAkC;CACrE,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAgB,UAAU;CAAS;CACpD,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,MAAa,0BAA0B,gBAAoC;CACzE,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAkB,UAAU;CAAS;CACtD,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,MAAa,sBAAsB,gBAAgC;CACjE,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAc,UAAU;CAAS;CAClD,QAAQ,CAAC;CACT,iBAAiB;CACjB,WACE;AACJ,CAAC;AAED,MAAa,sBAAsB,gBAAgC;CACjE,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAc,UAAU;CAAS;CAClD,QAAQ,CAAC;CACT,iBAAiB;CACjB,iBAAiB;CACjB,WACE;AACJ,CAAC;AAED,MAAa,uBAAuB,gBAAiC;CACnE,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAe,UAAU;CAAS;CACnD,QAAQ,CAAC;CACT,WAAW;AACb,CAAC;AAED,MAAa,6BAA6B,gBAAuC;CAC/E,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAqB,UAAU;CAAS;CACzD,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,MAAa,wBAAwB,gBAAkC;CACrE,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAgB,UAAU;CAAS;CACpD,QAAQ,CAAC;CACT,WAAW;AACb,CAAC;AAED,MAAa,uBAAuB,gBAAiC;CACnE,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAe,UAAU;CAAS;CACnD,QAAQ,CAAC;CACT,WAAW;AACb,CAAC;AAED,SAAgB,cAAc,EAAE,QAAQ,GAAG,UAAU,KAAM,cAAc,YAAgC;CACvG,OACE,oBAACA,MAAU,UAAX;EAA2B;EAAgB;EAAuB;EAChE,UAAA,oBAAC,oBAAD,EAAqB,SAA6B,CAAA;CAChC,CAAA;AAExB;AAEA,SAAS,mBAAmB,EAAE,YAAgD;CAC5E,MAAM,UAAUA,MAAU,gBAAgB;CAC1C,OAAO,oBAAC,oBAAoB,UAArB;EAA8B,OAAO;EAAU;CAAuC,CAAA;AAC/F;;AAGA,SAAgB,mBAA6C;CAC3D,OAAO,WAAW,mBAAmB;AACvC;AAEA,SAAgB,UAAU,EAAE,YAA4B;CACtD,MAAM,UAAU,iBAAiB;CACjC,IAAI,CAAC,SAAS,OAAO;CACrB,OACE,oBAAA,YAAA,EAAA,UACG,QAAQ,OAAO,KAAK,UACnB,oBAAC,iBAAiB,UAAlB;EAA0C,OAAO;EAC9C,UAAA,YACC,qBAAC,WAAD,EAAA,UAAA,CACE,oBAAC,YAAD,CAAa,CAAA,GACb,oBAAC,kBAAD,CAAmB,CAAA,CACV,EAAA,CAAA;CAEY,GAPK,MAAM,EAOX,CAC5B,EACD,CAAA;AAEN;AAEA,SAAgB,YAAY,EAAE,WAAW,YAA8B;CACrE,OAAO,oBAACA,MAAU,QAAX;EAA6B;EAAY;CAA2B,CAAA;AAC7E;AAEA,SAAS,YAAY,UAA8B;CACjD,OAAO,SAAS,QAAQ,QAAQ,CAAC,CAAC,SAAS;AAC7C;AAEA,SAAS,mBAAmB;CAC1B,MAAM,OAAO,QAAQ;CACrB,MAAM,EAAE,WAAW,SAAS;CAE5B,OAAO,OAAO,KAAK,UACjB,qBAAC,WAAD;EAAiC;EAAjC,UAAA;GACE,oBAAC,YAAD,CAAa,CAAA;GACb,oBAAC,kBAAD,CAAmB,CAAA;GACnB,oBAAC,aAAD,CAAc,CAAA;GACd,oBAAC,YAAD;IAAY,cAAY,KAAK,cAAc,EAAE,IAAI,mBAAmB,CAAC;IACnE,UAAA,oBAAC,QAAD;KAAM,eAAY;KAAO,UAAA;IAAO,CAAA;GACtB,CAAA;EACH;CAPK,GAAA,MAAM,EAOX,CACZ;AACH;AAEA,SAAgB,cAAc,EAAE,WAAW,UAAU,cAAc,WAAW,GAAG,SAA6B;CAC5G,MAAM,OAAO,QAAQ;CAErB,OACE,oBAACA,MAAU,UAAX;EACE,GAAI;EACJ,cAAY,aAAa,KAAK,cAAc,EAAE,IAAI,kBAAkB,CAAC;EAC1D;EACX,SAAS,aAAa,UACpB,oBAAC,OAAD;GACE,GAAI;GACJ,0BAAuB;GACvB,mCAAiC,MAAM,WAAW,KAAK,KAAA;GAEtD,UAAA,YAAY,QAAQ,IAAI,WAAW,oBAAC,kBAAD,CAAmB,CAAA;EACpD,CAAA;CAER,CAAA;AAEL;AAKA,SAAgB,UAAU,EAAE,OAAO,WAAW,gBAAgB,WAAW,YAA4B;CACnG,MAAM,YAAY,WAAW,gBAAgB;CAC7C,MAAM,QAAQ,aAAa;CAC3B,IAAI,CAAC,OAAO,OAAO;CACnB,OACE,oBAACA,MAAU,MAAX;EACS;EACS;EACL;EACX,SAAS,aAAa,UACpB,oBAAC,OAAD;GACE,GAAI;GACJ,sBAAmB;GACnB,2BAAyB,MAAM;GAC/B,8BAA4B,MAAM,UAAU,KAAK,KAAA;GACjD,8BAA4B,MAAM,UAAU,KAAK,KAAA;GAEhD;EACE,CAAA;CAER,CAAA;AAEL;AAEA,SAAgB,WAAW,EAAE,WAAW,UAAU,GAAG,SAA0B;CAC7E,OACE,oBAACA,MAAU,OAAX;EAAiB,GAAI;EAAkB;EAAW,uBAAoB;EACnE;CACc,CAAA;AAErB;AAEA,SAAgB,iBAAiB,EAAE,WAAW,UAAU,GAAG,SAAgC;CACzF,OACE,oBAACA,MAAU,aAAX;EAAuB,GAAI;EAAkB;EAAW,6BAA0B;EAC/E;CACoB,CAAA;AAE3B;AAEA,SAAgB,YAAY,EAAE,WAAW,UAAU,GAAG,SAA2B;CAC/E,OACE,oBAACA,MAAU,QAAX;EAAkB,GAAI;EAAkB;EAAW,wBAAqB;EACrE;CACe,CAAA;AAEtB;AAEA,SAAgB,WAAW,EAAE,WAAW,UAAU,GAAG,SAA0B;CAC7E,OACE,oBAACA,MAAU,OAAX;EAAiB,GAAI;EAAkB;EAAW,uBAAoB;EACnE;CACc,CAAA;AAErB;;AAGA,MAAa,WAAWA,MAAU;AAElC,MAAaC,UAAQ;CACnB,UAAU;CACV,QAAQ;CACR,UAAU;CACV,MAAM;CACN,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,OAAO;AACT"}