@vtex/faststore 0.0.7 → 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 (498) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/dist/actions.json +194 -1
  3. package/dist/catalog.json +3449 -1612
  4. package/dist/cli/index.d.ts +512 -63
  5. package/dist/cli/index.js +2098 -686
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/cli/server-runtime.js +44 -24
  8. package/dist/cli/server-runtime.js.map +1 -1
  9. package/dist/client.d.ts +5 -1
  10. package/dist/client.js +5 -1
  11. package/dist/components.d.ts +42 -10
  12. package/dist/components.js +47 -15
  13. package/dist/content-types.json +823 -0
  14. package/dist/core/dist/define-component.d.ts +12 -2
  15. package/dist/docs/account-access-gate.md +4 -4
  16. package/dist/docs/account-address-book.md +519 -0
  17. package/dist/docs/account-entry-point.md +5 -5
  18. package/dist/docs/account-menu.md +7 -7
  19. package/dist/docs/account-saved-cards.md +301 -0
  20. package/dist/docs/alert-dialog.md +2 -2
  21. package/dist/docs/billing-address.md +17 -9
  22. package/dist/docs/binding-switcher.md +154 -0
  23. package/dist/docs/card-entry.md +38 -7
  24. package/dist/docs/card-summary.md +175 -0
  25. package/dist/docs/cardholder-document.md +16 -14
  26. package/dist/docs/cart-destination.md +238 -0
  27. package/dist/docs/cart-item.md +22 -4
  28. package/dist/docs/cart-totalizer.md +1 -1
  29. package/dist/docs/cart.md +126 -19
  30. package/dist/docs/client-profile-fieldset.md +7 -5
  31. package/dist/docs/copy-text.md +84 -0
  32. package/dist/docs/document-field.md +8 -8
  33. package/dist/docs/footer-link-group.md +2 -2
  34. package/dist/docs/footer-social.md +1 -1
  35. package/dist/docs/free-shipping-progress.md +13 -9
  36. package/dist/docs/header-root.md +1 -1
  37. package/dist/docs/incentives.md +4 -4
  38. package/dist/docs/logo.md +6 -6
  39. package/dist/docs/my-account-profile.md +4 -4
  40. package/dist/docs/navigation-link-list.md +2 -2
  41. package/dist/docs/navigation-link.md +3 -3
  42. package/dist/docs/order-cancel.md +52 -0
  43. package/dist/docs/order-delivery-address.md +63 -0
  44. package/dist/docs/order-delivery-group.md +83 -0
  45. package/dist/docs/order-details-back.md +41 -0
  46. package/dist/docs/order-details-header.md +48 -0
  47. package/dist/docs/order-list-row.md +46 -0
  48. package/dist/docs/order-list-search.md +101 -0
  49. package/dist/docs/order-list-views.md +97 -0
  50. package/dist/docs/order-list.md +94 -0
  51. package/dist/docs/order-payment.md +94 -0
  52. package/dist/docs/order-placed-date.md +49 -0
  53. package/dist/docs/order-placed-payment-summary.md +13 -15
  54. package/dist/docs/order-placed.md +225 -0
  55. package/dist/docs/order-progress.md +88 -0
  56. package/dist/docs/order-status-badge.md +91 -0
  57. package/dist/docs/order-summary.md +53 -0
  58. package/dist/docs/payment-method-badges.md +16 -5
  59. package/dist/docs/payment-methods.md +1 -1
  60. package/dist/docs/place-order.md +13 -9
  61. package/dist/docs/product-information.md +2 -2
  62. package/dist/docs/product-shelf.md +7 -0
  63. package/dist/docs/saved-cards.md +2 -2
  64. package/dist/docs/select.md +22 -10
  65. package/dist/docs/session.md +1 -1
  66. package/dist/docs/shipping-address.md +1 -1
  67. package/dist/docs/sign-in-redirect.md +7 -7
  68. package/dist/docs/toast.md +35 -13
  69. package/dist/index.d.ts +7 -2
  70. package/dist/index.js +2 -1
  71. package/dist/internal/client.d.ts +9 -1
  72. package/dist/internal/client.js +10 -2
  73. package/dist/internal/server.d.ts +8 -2
  74. package/dist/internal/server.js +8 -2
  75. package/dist/matter/account-access-gate/account-access-gate.d.ts +2 -1
  76. package/dist/matter/account-access-gate/account-access-gate.js +19 -16
  77. package/dist/matter/account-access-gate/account-access-gate.js.map +1 -1
  78. package/dist/matter/account-address-book/account-address-book-delete.d.ts +60 -0
  79. package/dist/matter/account-address-book/account-address-book-delete.js +184 -0
  80. package/dist/matter/account-address-book/account-address-book-delete.js.map +1 -0
  81. package/dist/matter/account-address-book/account-address-book-deliverability.d.ts +19 -0
  82. package/dist/matter/account-address-book/account-address-book-deliverability.js +50 -0
  83. package/dist/matter/account-address-book/account-address-book-deliverability.js.map +1 -0
  84. package/dist/matter/account-address-book/account-address-book-form-parts.d.ts +42 -0
  85. package/dist/matter/account-address-book/account-address-book-form-parts.js +146 -0
  86. package/dist/matter/account-address-book/account-address-book-form-parts.js.map +1 -0
  87. package/dist/matter/account-address-book/account-address-book-profile-notice.d.ts +25 -0
  88. package/dist/matter/account-address-book/account-address-book-profile-notice.js +72 -0
  89. package/dist/matter/account-address-book/account-address-book-profile-notice.js.map +1 -0
  90. package/dist/matter/account-address-book/account-address-book.d.ts +120 -0
  91. package/dist/matter/account-address-book/account-address-book.js +469 -0
  92. package/dist/matter/account-address-book/account-address-book.js.map +1 -0
  93. package/dist/matter/account-address-book/account-address-country-switch.js +59 -0
  94. package/dist/matter/account-address-book/account-address-country-switch.js.map +1 -0
  95. package/dist/matter/account-address-book/account-address-country.d.ts +36 -0
  96. package/dist/matter/account-address-book/account-address-country.js +151 -0
  97. package/dist/matter/account-address-book/account-address-country.js.map +1 -0
  98. package/dist/matter/account-address-book/account-address-first-line.js +11 -0
  99. package/dist/matter/account-address-book/account-address-first-line.js.map +1 -0
  100. package/dist/matter/account-address-book/account-address-form-context.d.ts +55 -0
  101. package/dist/matter/account-address-book/account-address-form-context.js +14 -0
  102. package/dist/matter/account-address-book/account-address-form-context.js.map +1 -0
  103. package/dist/matter/account-address-book/account-address-form.d.ts +85 -0
  104. package/dist/matter/account-address-book/account-address-form.js +946 -0
  105. package/dist/matter/account-address-book/account-address-form.js.map +1 -0
  106. package/dist/matter/account-address-book/account-address-postal-autofill.js +189 -0
  107. package/dist/matter/account-address-book/account-address-postal-autofill.js.map +1 -0
  108. package/dist/matter/account-address-book/account-address-record.d.ts +26 -0
  109. package/dist/matter/account-address-book/account-address-record.js +138 -0
  110. package/dist/matter/account-address-book/account-address-record.js.map +1 -0
  111. package/dist/matter/account-entry-point/account-entry-point.js +11 -11
  112. package/dist/matter/account-entry-point/account-entry-point.js.map +1 -1
  113. package/dist/matter/account-menu/account-menu.d.ts +7 -3
  114. package/dist/matter/account-menu/account-menu.js +69 -28
  115. package/dist/matter/account-menu/account-menu.js.map +1 -1
  116. package/dist/matter/account-saved-cards/account-saved-cards.d.ts +383 -0
  117. package/dist/matter/account-saved-cards/account-saved-cards.js +956 -0
  118. package/dist/matter/account-saved-cards/account-saved-cards.js.map +1 -0
  119. package/dist/matter/billing-address/billing-address.d.ts +30 -17
  120. package/dist/matter/billing-address/billing-address.js +128 -33
  121. package/dist/matter/billing-address/billing-address.js.map +1 -1
  122. package/dist/matter/breadcrumb/breadcrumb.js +2 -2
  123. package/dist/matter/breadcrumb/breadcrumb.js.map +1 -1
  124. package/dist/matter/card-entry/card-capture.js +4 -3
  125. package/dist/matter/card-entry/card-capture.js.map +1 -1
  126. package/dist/matter/card-entry/card-entry.d.ts +5 -3
  127. package/dist/matter/card-entry/card-entry.js +77 -27
  128. package/dist/matter/card-entry/card-entry.js.map +1 -1
  129. package/dist/matter/cardholder-document/cardholder-document.d.ts +10 -9
  130. package/dist/matter/cardholder-document/cardholder-document.js +36 -16
  131. package/dist/matter/cardholder-document/cardholder-document.js.map +1 -1
  132. package/dist/matter/cart/cart-namespace.d.ts +5 -1
  133. package/dist/matter/cart/cart-namespace.js +5 -1
  134. package/dist/matter/cart/cart-namespace.js.map +1 -1
  135. package/dist/matter/cart/cart.d.ts +41 -6
  136. package/dist/matter/cart/cart.js +267 -54
  137. package/dist/matter/cart/cart.js.map +1 -1
  138. package/dist/matter/cart-destination/cart-destination.d.ts +222 -0
  139. package/dist/matter/cart-destination/cart-destination.js +613 -0
  140. package/dist/matter/cart-destination/cart-destination.js.map +1 -0
  141. package/dist/matter/cart-item/cart-item.d.ts +4 -2
  142. package/dist/matter/cart-item/cart-item.js +76 -17
  143. package/dist/matter/cart-item/cart-item.js.map +1 -1
  144. package/dist/matter/cart-totalizer/cart-totalizer.js +3 -2
  145. package/dist/matter/cart-totalizer/cart-totalizer.js.map +1 -1
  146. package/dist/matter/client-preferences/client-preferences-fieldset.js +1 -1
  147. package/dist/matter/client-profile-form/client-profile-fieldset.js +8 -5
  148. package/dist/matter/client-profile-form/client-profile-fieldset.js.map +1 -1
  149. package/dist/matter/free-shipping-progress/free-shipping-progress.d.ts +6 -3
  150. package/dist/matter/free-shipping-progress/free-shipping-progress.js +76 -33
  151. package/dist/matter/free-shipping-progress/free-shipping-progress.js.map +1 -1
  152. package/dist/matter/identity-edit/identity-edit.js +3 -3
  153. package/dist/matter/identity-edit/identity-edit.js.map +1 -1
  154. package/dist/matter/identity-summary/identity-summary.js +2 -2
  155. package/dist/matter/identity-summary/identity-summary.js.map +1 -1
  156. package/dist/matter/my-account-profile/my-account-profile.d.ts +3 -3
  157. package/dist/matter/my-account-profile/my-account-profile.js +4 -4
  158. package/dist/matter/my-account-profile/my-account-profile.js.map +1 -1
  159. package/dist/matter/order-cancel/order-cancel.d.ts +110 -0
  160. package/dist/matter/order-cancel/order-cancel.js +344 -0
  161. package/dist/matter/order-cancel/order-cancel.js.map +1 -0
  162. package/dist/matter/order-delivery-address/order-delivery-address.d.ts +73 -0
  163. package/dist/matter/order-delivery-address/order-delivery-address.js +172 -0
  164. package/dist/matter/order-delivery-address/order-delivery-address.js.map +1 -0
  165. package/dist/matter/order-delivery-group/order-delivery-group.d.ts +128 -0
  166. package/dist/matter/order-delivery-group/order-delivery-group.js +408 -0
  167. package/dist/matter/order-delivery-group/order-delivery-group.js.map +1 -0
  168. package/dist/matter/order-details-header/order-details-header.d.ts +104 -0
  169. package/dist/matter/order-details-header/order-details-header.js +190 -0
  170. package/dist/matter/order-details-header/order-details-header.js.map +1 -0
  171. package/dist/matter/order-list/customer-orders-filter.d.ts +12 -0
  172. package/dist/matter/order-list/customer-orders-filter.js +30 -0
  173. package/dist/matter/order-list/customer-orders-filter.js.map +1 -0
  174. package/dist/matter/order-list/order-list.d.ts +121 -0
  175. package/dist/matter/order-list/order-list.js +395 -0
  176. package/dist/matter/order-list/order-list.js.map +1 -0
  177. package/dist/matter/order-list-row/order-list-row.d.ts +44 -0
  178. package/dist/matter/order-list-row/order-list-row.js +127 -0
  179. package/dist/matter/order-list-row/order-list-row.js.map +1 -0
  180. package/dist/matter/order-list-search/order-list-search.d.ts +72 -0
  181. package/dist/matter/order-list-search/order-list-search.js +296 -0
  182. package/dist/matter/order-list-search/order-list-search.js.map +1 -0
  183. package/dist/matter/order-list-views/order-list-views.d.ts +65 -0
  184. package/dist/matter/order-list-views/order-list-views.js +154 -0
  185. package/dist/matter/order-list-views/order-list-views.js.map +1 -0
  186. package/dist/matter/order-payment/order-payment.d.ts +83 -0
  187. package/dist/matter/order-payment/order-payment.js +258 -0
  188. package/dist/matter/order-payment/order-payment.js.map +1 -0
  189. package/dist/matter/order-placed/order-placed-deliveries.d.ts +145 -0
  190. package/dist/matter/order-placed/order-placed-deliveries.js +380 -0
  191. package/dist/matter/order-placed/order-placed-deliveries.js.map +1 -0
  192. package/dist/matter/order-placed/order-placed.d.ts +122 -0
  193. package/dist/matter/order-placed/order-placed.js +452 -0
  194. package/dist/matter/order-placed/order-placed.js.map +1 -0
  195. package/dist/matter/order-placed-date/order-placed-date.d.ts +45 -0
  196. package/dist/matter/order-placed-date/order-placed-date.js +92 -0
  197. package/dist/matter/order-placed-date/order-placed-date.js.map +1 -0
  198. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.d.ts +5 -8
  199. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js +29 -81
  200. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js.map +1 -1
  201. package/dist/matter/order-progress/order-progress.d.ts +85 -0
  202. package/dist/matter/order-progress/order-progress.js +203 -0
  203. package/dist/matter/order-progress/order-progress.js.map +1 -0
  204. package/dist/matter/order-summary/order-summary.d.ts +81 -0
  205. package/dist/matter/order-summary/order-summary.js +192 -0
  206. package/dist/matter/order-summary/order-summary.js.map +1 -0
  207. package/dist/matter/payment/cardholder-document-rule.d.ts +35 -0
  208. package/dist/matter/payment/cardholder-document-rule.js +35 -0
  209. package/dist/matter/payment/cardholder-document-rule.js.map +1 -0
  210. package/dist/matter/payment/payment-card-capture.js +38 -23
  211. package/dist/matter/payment/payment-card-capture.js.map +1 -1
  212. package/dist/matter/payment/payment-controller.js +11 -0
  213. package/dist/matter/payment/payment-controller.js.map +1 -1
  214. package/dist/matter/payment/payment-method-badges.js +4 -2
  215. package/dist/matter/payment/payment-method-badges.js.map +1 -1
  216. package/dist/matter/payment/payment.js +11 -0
  217. package/dist/matter/payment/payment.js.map +1 -1
  218. package/dist/matter/payment/use-payment-method-selection.d.ts +39 -0
  219. package/dist/matter/payment/use-payment-method-selection.js +30 -2
  220. package/dist/matter/payment/use-payment-method-selection.js.map +1 -1
  221. package/dist/matter/payment/use-payment-submit-gate.d.ts +16 -2
  222. package/dist/matter/payment/use-payment-submit-gate.js +42 -14
  223. package/dist/matter/payment/use-payment-submit-gate.js.map +1 -1
  224. package/dist/matter/place-order/place-order.d.ts +12 -7
  225. package/dist/matter/place-order/place-order.js +126 -103
  226. package/dist/matter/place-order/place-order.js.map +1 -1
  227. package/dist/matter/pricing/pricing.js +5 -5
  228. package/dist/matter/pricing/pricing.js.map +1 -1
  229. package/dist/matter/product-shelf/product-shelf.d.ts +7 -0
  230. package/dist/matter/product-shelf/product-shelf.js +115 -1
  231. package/dist/matter/product-shelf/product-shelf.js.map +1 -1
  232. package/dist/matter/region/region.js +1 -1
  233. package/dist/matter/saved-cards/saved-cards.js +26 -15
  234. package/dist/matter/saved-cards/saved-cards.js.map +1 -1
  235. package/dist/matter/session/session.js +2 -2
  236. package/dist/matter/session/session.js.map +1 -1
  237. package/dist/matter/shipping-address/country-descriptors.d.ts +1 -0
  238. package/dist/matter/shipping-address/country-descriptors.js +23 -1
  239. package/dist/matter/shipping-address/country-descriptors.js.map +1 -1
  240. package/dist/matter/shipping-address/shipping-address.js +62 -14
  241. package/dist/matter/shipping-address/shipping-address.js.map +1 -1
  242. package/dist/matter/shipping-simulator/shipping-simulator.d.ts +2 -2
  243. package/dist/matter/shipping-simulator/shipping-simulator.js +6 -2
  244. package/dist/matter/shipping-simulator/shipping-simulator.js.map +1 -1
  245. package/dist/matter/sign-in-redirect/sign-in-redirect.js +48 -10
  246. package/dist/matter/sign-in-redirect/sign-in-redirect.js.map +1 -1
  247. package/dist/matter/variant-selector/variant-selector.js +2 -2
  248. package/dist/matter/variant-selector/variant-selector.js.map +1 -1
  249. package/dist/particles/address-summary/address-summary.js +2 -2
  250. package/dist/particles/address-summary/address-summary.js.map +1 -1
  251. package/dist/particles/alert-dialog/alert-dialog.d.ts +5 -1
  252. package/dist/particles/alert-dialog/alert-dialog.js +5 -2
  253. package/dist/particles/alert-dialog/alert-dialog.js.map +1 -1
  254. package/dist/particles/binding-switcher/binding-switcher.d.ts +38 -0
  255. package/dist/particles/binding-switcher/binding-switcher.js +183 -0
  256. package/dist/particles/binding-switcher/binding-switcher.js.map +1 -0
  257. package/dist/particles/card-summary/card-network.d.ts +17 -0
  258. package/dist/particles/card-summary/card-network.js +25 -0
  259. package/dist/particles/card-summary/card-network.js.map +1 -0
  260. package/dist/particles/card-summary/card-summary.d.ts +83 -0
  261. package/dist/particles/card-summary/card-summary.js +188 -0
  262. package/dist/particles/card-summary/card-summary.js.map +1 -0
  263. package/dist/particles/copy-text/copy-text.d.ts +28 -0
  264. package/dist/particles/copy-text/copy-text.js +140 -0
  265. package/dist/particles/copy-text/copy-text.js.map +1 -0
  266. package/dist/particles/document-field/document-field.d.ts +34 -5
  267. package/dist/particles/document-field/document-field.js +115 -19
  268. package/dist/particles/document-field/document-field.js.map +1 -1
  269. package/dist/particles/field/field.js +1 -1
  270. package/dist/particles/field/field.js.map +1 -1
  271. package/dist/particles/footer-link-group/footer-link-group.d.ts +6 -2
  272. package/dist/particles/footer-link-group/footer-link-group.js +18 -7
  273. package/dist/particles/footer-link-group/footer-link-group.js.map +1 -1
  274. package/dist/particles/footer-social/footer-social.d.ts +1 -1
  275. package/dist/particles/footer-social/footer-social.js +1 -1
  276. package/dist/particles/footer-social/footer-social.js.map +1 -1
  277. package/dist/particles/incentives/incentives.d.ts +7 -3
  278. package/dist/particles/incentives/incentives.js +8 -3
  279. package/dist/particles/incentives/incentives.js.map +1 -1
  280. package/dist/particles/logo/logo.d.ts +3 -2
  281. package/dist/particles/logo/logo.js +20 -8
  282. package/dist/particles/logo/logo.js.map +1 -1
  283. package/dist/particles/navigation-link/navigation-link.d.ts +2 -3
  284. package/dist/particles/navigation-link/navigation-link.js +4 -4
  285. package/dist/particles/navigation-link/navigation-link.js.map +1 -1
  286. package/dist/particles/navigation-link-list/navigation-link-list.d.ts +6 -2
  287. package/dist/particles/navigation-link-list/navigation-link-list.js +18 -7
  288. package/dist/particles/navigation-link-list/navigation-link-list.js.map +1 -1
  289. package/dist/particles/order-details-back/order-details-back.d.ts +28 -0
  290. package/dist/particles/order-details-back/order-details-back.js +52 -0
  291. package/dist/particles/order-details-back/order-details-back.js.map +1 -0
  292. package/dist/particles/order-status-badge/order-status-badge.d.ts +19 -0
  293. package/dist/particles/order-status-badge/order-status-badge.js +50 -0
  294. package/dist/particles/order-status-badge/order-status-badge.js.map +1 -0
  295. package/dist/particles/payment-methods/payment-methods.d.ts +1 -1
  296. package/dist/particles/payment-methods/payment-methods.js +1 -1
  297. package/dist/particles/payment-methods/payment-methods.js.map +1 -1
  298. package/dist/particles/phone-field/phone-field.js +2 -2
  299. package/dist/particles/phone-field/phone-field.js.map +1 -1
  300. package/dist/particles/select/select.js +6 -6
  301. package/dist/particles/select/select.js.map +1 -1
  302. package/dist/particles/toast/toast.d.ts +21 -7
  303. package/dist/particles/toast/toast.js +76 -8
  304. package/dist/particles/toast/toast.js.map +1 -1
  305. package/dist/schema.graphql +1376 -126
  306. package/dist/sdk/actions/address-action-provider.d.ts +16 -0
  307. package/dist/sdk/actions/address-action-provider.js +20 -0
  308. package/dist/sdk/actions/address-action-provider.js.map +1 -0
  309. package/dist/sdk/actions/analytics-action-provider.js +1 -1
  310. package/dist/sdk/actions/index.d.ts +5 -1
  311. package/dist/sdk/actions/index.js +5 -1
  312. package/dist/sdk/actions/locale-action-provider.d.ts +16 -0
  313. package/dist/sdk/actions/locale-action-provider.js +20 -0
  314. package/dist/sdk/actions/locale-action-provider.js.map +1 -0
  315. package/dist/sdk/actions/order-action-provider.d.ts +12 -0
  316. package/dist/sdk/actions/order-action-provider.js +16 -0
  317. package/dist/sdk/actions/order-action-provider.js.map +1 -0
  318. package/dist/sdk/actions/saved-card-action-provider.d.ts +12 -0
  319. package/dist/sdk/actions/saved-card-action-provider.js +16 -0
  320. package/dist/sdk/actions/saved-card-action-provider.js.map +1 -0
  321. package/dist/sdk/actions/types.d.ts +10 -2
  322. package/dist/sdk/address/address-fields.js +38 -0
  323. package/dist/sdk/address/address-fields.js.map +1 -0
  324. package/dist/sdk/address/checkout-readiness.js +17 -0
  325. package/dist/sdk/address/checkout-readiness.js.map +1 -0
  326. package/dist/sdk/address/constants.js +60 -0
  327. package/dist/sdk/address/constants.js.map +1 -0
  328. package/dist/sdk/address/country-display.js +19 -0
  329. package/dist/sdk/address/country-display.js.map +1 -0
  330. package/dist/sdk/address/country-rules.js +30 -0
  331. package/dist/sdk/address/country-rules.js.map +1 -0
  332. package/dist/sdk/address/format-address.js +18 -0
  333. package/dist/sdk/address/format-address.js.map +1 -0
  334. package/dist/sdk/address/iso-countries.js +262 -0
  335. package/dist/sdk/address/iso-countries.js.map +1 -0
  336. package/dist/sdk/address/layouts.js +56 -0
  337. package/dist/sdk/address/layouts.js.map +1 -0
  338. package/dist/sdk/address/messages.js +97 -0
  339. package/dist/sdk/address/messages.js.map +1 -0
  340. package/dist/sdk/address/operations.d.ts +54 -0
  341. package/dist/sdk/address/operations.js +83 -0
  342. package/dist/sdk/address/operations.js.map +1 -0
  343. package/dist/sdk/address/postal-code-lookup-operations.js +23 -0
  344. package/dist/sdk/address/postal-code-lookup-operations.js.map +1 -0
  345. package/dist/sdk/address/postal-code.js +11 -0
  346. package/dist/sdk/address/postal-code.js.map +1 -0
  347. package/dist/sdk/address/schema.js +14 -0
  348. package/dist/sdk/address/schema.js.map +1 -0
  349. package/dist/sdk/address/server-fields.js +25 -0
  350. package/dist/sdk/address/server-fields.js.map +1 -0
  351. package/dist/sdk/address/switch-country.js +57 -0
  352. package/dist/sdk/address/switch-country.js.map +1 -0
  353. package/dist/sdk/address/types.d.ts +26 -0
  354. package/dist/sdk/address/use-address-actions.d.ts +29 -0
  355. package/dist/sdk/address/use-address-actions.js +247 -0
  356. package/dist/sdk/address/use-address-actions.js.map +1 -0
  357. package/dist/sdk/address/use-address-by-postal-code.js +57 -0
  358. package/dist/sdk/address/use-address-by-postal-code.js.map +1 -0
  359. package/dist/sdk/address/validate-address.js +62 -0
  360. package/dist/sdk/address/validate-address.js.map +1 -0
  361. package/dist/sdk/cache/storefront-cache.js +30 -4
  362. package/dist/sdk/cache/storefront-cache.js.map +1 -1
  363. package/dist/sdk/cart/operations.d.ts +2 -1
  364. package/dist/sdk/cart/operations.js +55 -1
  365. package/dist/sdk/cart/operations.js.map +1 -1
  366. package/dist/sdk/cart/optimistic.d.ts +30 -7
  367. package/dist/sdk/cart/optimistic.js +78 -41
  368. package/dist/sdk/cart/optimistic.js.map +1 -1
  369. package/dist/sdk/cart/use-cart-actions.d.ts +7 -0
  370. package/dist/sdk/cart/use-cart-actions.js +61 -15
  371. package/dist/sdk/cart/use-cart-actions.js.map +1 -1
  372. package/dist/sdk/cart/use-cart-sync-sales-channel.js +70 -0
  373. package/dist/sdk/cart/use-cart-sync-sales-channel.js.map +1 -0
  374. package/dist/sdk/client/fetch-page-data.d.ts +11 -0
  375. package/dist/sdk/client/fetch-page-data.js +12 -1
  376. package/dist/sdk/client/fetch-page-data.js.map +1 -1
  377. package/dist/sdk/content/create-content-runtime.js +1 -8
  378. package/dist/sdk/content/create-content-runtime.js.map +1 -1
  379. package/dist/sdk/data/failed-fields.js +23 -0
  380. package/dist/sdk/data/failed-fields.js.map +1 -0
  381. package/dist/sdk/data/index.js +2 -2
  382. package/dist/sdk/data/page-reactive-data-root.d.ts +45 -7
  383. package/dist/sdk/data/page-reactive-data-root.js +88 -12
  384. package/dist/sdk/data/page-reactive-data-root.js.map +1 -1
  385. package/dist/sdk/data/reactive-list-item-data-provider.js +2 -2
  386. package/dist/sdk/generated/fragment-types.js +2 -0
  387. package/dist/sdk/generated/fragment-types.js.map +1 -1
  388. package/dist/sdk/generated/gql.js +17 -11
  389. package/dist/sdk/generated/gql.js.map +1 -1
  390. package/dist/sdk/generated/graphql.d.ts +185 -46
  391. package/dist/sdk/generated/graphql.js +2587 -155
  392. package/dist/sdk/generated/graphql.js.map +1 -1
  393. package/dist/sdk/i18n/messages/en-US.js +157 -1
  394. package/dist/sdk/i18n/messages/en-US.json +157 -1
  395. package/dist/sdk/i18n/messages/en-US.json.d.ts +156 -0
  396. package/dist/sdk/i18n/messages/es-ES.js +157 -1
  397. package/dist/sdk/i18n/messages/es-ES.json +157 -1
  398. package/dist/sdk/i18n/messages/pt-BR.js +157 -1
  399. package/dist/sdk/i18n/messages/pt-BR.json +157 -1
  400. package/dist/sdk/i18n/provider.js +1 -1
  401. package/dist/sdk/i18n/provider.js.map +1 -1
  402. package/dist/sdk/locale/binding-switch-provider.d.ts +13 -0
  403. package/dist/sdk/locale/binding-switch-provider.js +20 -0
  404. package/dist/sdk/locale/binding-switch-provider.js.map +1 -0
  405. package/dist/sdk/locale/use-locale-actions.d.ts +16 -0
  406. package/dist/sdk/locale/use-locale-actions.js +70 -0
  407. package/dist/sdk/locale/use-locale-actions.js.map +1 -0
  408. package/dist/sdk/navigation/binding-switch.d.ts +39 -0
  409. package/dist/sdk/navigation/binding-switch.js +129 -0
  410. package/dist/sdk/navigation/binding-switch.js.map +1 -0
  411. package/dist/sdk/navigation/current-page.js +30 -0
  412. package/dist/sdk/navigation/current-page.js.map +1 -0
  413. package/dist/sdk/navigation/decode-route-params.d.ts +11 -0
  414. package/dist/sdk/navigation/decode-route-params.js +32 -0
  415. package/dist/sdk/navigation/decode-route-params.js.map +1 -0
  416. package/dist/sdk/navigation/not-found-log.d.ts +28 -0
  417. package/dist/sdk/navigation/not-found-log.js +133 -0
  418. package/dist/sdk/navigation/not-found-log.js.map +1 -0
  419. package/dist/sdk/navigation/path.js +25 -0
  420. package/dist/sdk/navigation/path.js.map +1 -0
  421. package/dist/sdk/navigation/redirect.d.ts +11 -0
  422. package/dist/sdk/navigation/redirect.js +22 -0
  423. package/dist/sdk/navigation/redirect.js.map +1 -0
  424. package/dist/sdk/navigation/report.js +70 -0
  425. package/dist/sdk/navigation/report.js.map +1 -0
  426. package/dist/sdk/navigation/resolve-storefront-href.d.ts +12 -0
  427. package/dist/sdk/navigation/resolve-storefront-href.js +214 -0
  428. package/dist/sdk/navigation/resolve-storefront-href.js.map +1 -0
  429. package/dist/sdk/navigation/route-identity-provider.d.ts +11 -0
  430. package/dist/sdk/navigation/route-identity-provider.js +20 -0
  431. package/dist/sdk/navigation/route-identity-provider.js.map +1 -0
  432. package/dist/sdk/navigation/route-map-provider.d.ts +19 -0
  433. package/dist/sdk/navigation/route-map-provider.js +29 -0
  434. package/dist/sdk/navigation/route-map-provider.js.map +1 -0
  435. package/dist/sdk/navigation/storefront-link.d.ts +16 -0
  436. package/dist/sdk/navigation/storefront-link.js +66 -0
  437. package/dist/sdk/navigation/storefront-link.js.map +1 -0
  438. package/dist/sdk/navigation/types.d.ts +45 -0
  439. package/dist/sdk/navigation/use-storefront-navigation.d.ts +26 -0
  440. package/dist/sdk/navigation/use-storefront-navigation.js +73 -0
  441. package/dist/sdk/navigation/use-storefront-navigation.js.map +1 -0
  442. package/dist/sdk/navigation/use-storefront-route.d.ts +26 -0
  443. package/dist/sdk/navigation/use-storefront-route.js +87 -0
  444. package/dist/sdk/navigation/use-storefront-route.js.map +1 -0
  445. package/dist/sdk/navigation/use-storefront-search-params.d.ts +11 -0
  446. package/dist/sdk/navigation/use-storefront-search-params.js +16 -0
  447. package/dist/sdk/navigation/use-storefront-search-params.js.map +1 -0
  448. package/dist/sdk/orders/completion-badge.d.ts +8 -0
  449. package/dist/sdk/orders/completion-badge.js +47 -0
  450. package/dist/sdk/orders/completion-badge.js.map +1 -0
  451. package/dist/sdk/orders/order-operations.d.ts +19 -0
  452. package/dist/sdk/orders/order-operations.js +21 -0
  453. package/dist/sdk/orders/order-operations.js.map +1 -0
  454. package/dist/sdk/orders/use-order-actions.d.ts +23 -0
  455. package/dist/sdk/orders/use-order-actions.js +62 -0
  456. package/dist/sdk/orders/use-order-actions.js.map +1 -0
  457. package/dist/sdk/payment/gateway-origin.js +26 -13
  458. package/dist/sdk/payment/gateway-origin.js.map +1 -1
  459. package/dist/sdk/payment/gateway.d.ts +14 -2
  460. package/dist/sdk/payment/gateway.js +32 -14
  461. package/dist/sdk/payment/gateway.js.map +1 -1
  462. package/dist/sdk/payment/operations.js +11 -0
  463. package/dist/sdk/payment/operations.js.map +1 -1
  464. package/dist/sdk/payment/submission-registry.d.ts +2 -1
  465. package/dist/sdk/payment/submission-registry.js +12 -8
  466. package/dist/sdk/payment/submission-registry.js.map +1 -1
  467. package/dist/sdk/payment/use-payment-actions.d.ts +5 -3
  468. package/dist/sdk/payment/use-payment-actions.js +5 -2
  469. package/dist/sdk/payment/use-payment-actions.js.map +1 -1
  470. package/dist/sdk/payment/use-place-order.d.ts +6 -3
  471. package/dist/sdk/payment/use-place-order.js +333 -143
  472. package/dist/sdk/payment/use-place-order.js.map +1 -1
  473. package/dist/sdk/saved-cards/operations.d.ts +16 -0
  474. package/dist/sdk/saved-cards/operations.js +41 -0
  475. package/dist/sdk/saved-cards/operations.js.map +1 -0
  476. package/dist/sdk/saved-cards/use-saved-card-actions.d.ts +26 -0
  477. package/dist/sdk/saved-cards/use-saved-card-actions.js +86 -0
  478. package/dist/sdk/saved-cards/use-saved-card-actions.js.map +1 -0
  479. package/dist/sdk/seo/page-alternates.d.ts +57 -0
  480. package/dist/sdk/seo/page-alternates.js +127 -0
  481. package/dist/sdk/seo/page-alternates.js.map +1 -0
  482. package/dist/sdk/shipping/shipping-operations.d.ts +1 -1
  483. package/dist/sdk/shipping/shipping-operations.js +13 -0
  484. package/dist/sdk/shipping/shipping-operations.js.map +1 -1
  485. package/dist/sdk/state/cart.d.ts +9 -2
  486. package/dist/sdk/state/cart.js +9 -2
  487. package/dist/sdk/state/cart.js.map +1 -1
  488. package/dist/utils/own-value.js +19 -0
  489. package/dist/utils/own-value.js.map +1 -0
  490. package/dist/utils/safe-href.d.ts +4 -4
  491. package/dist/utils/safe-href.js +31 -22
  492. package/dist/utils/safe-href.js.map +1 -1
  493. package/package.json +5 -1
  494. package/skills/storefront-asset/SKILL.md +111 -0
  495. package/skills/storefront-asset/references/api-side.md +153 -0
  496. package/skills/storefront-asset/references/findings.md +40 -0
  497. package/skills/storefront-asset/references/ui-side.md +94 -0
  498. package/src/styles/tailwind.css +28 -2
@@ -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 { 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\n/** One incentive item, identical in the prop and the content channels. */\nexport type IncentiveItem = NonNullable<IncentivesContentValue['items']>[number];\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: 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 resolveHref(raw: string | undefined, index: number): string | null {\n // Absent and blank are \"no link\", not a rejection, so neither 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($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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"}
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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"}
@@ -1,6 +1,7 @@
1
1
  import { ContentValue } from "../../core/dist/define-content-type.js";
2
2
  import { ComponentDefinition } from "../../core/dist/define-component.js";
3
3
  import "../../core/dist/index.js";
4
+ import { PageRef } from "../../sdk/navigation/types.js";
4
5
  import { ComponentPropsWithRef } from "react";
5
6
  //#region src/particles/logo/logo.d.ts
6
7
  export declare const LogoContent: {
@@ -41,8 +42,8 @@ export type LogoProps = Omit<ComponentPropsWithRef<'div'>, 'children'> & {
41
42
  src?: string;
42
43
  /** Accessible alt text. Optional: the render gate, not the type, requires a resolved value. */
43
44
  alt?: string;
44
- /** Navigation destination; defaults to the configured home. Dropped when it fails the safety check. */
45
- href?: string;
45
+ /** Navigation destination; defaults to the home page. A string that fails the safety check is dropped. */
46
+ href?: PageRef | string;
46
47
  /** Image loading strategy. `eager` suits the above-the-fold header; author `lazy` below it. */
47
48
  loading?: 'lazy' | 'eager';
48
49
  /** Merchant-authored logo, sliced from the page/layout content entry; wins over the props when
@@ -55,8 +55,8 @@ const LogoDefinition = defineComponent({
55
55
  label: "Alt Text"
56
56
  },
57
57
  href: {
58
- type: "string",
59
- default: "/",
58
+ type: "link",
59
+ default: { standard: "home" },
60
60
  label: "Link Destination"
61
61
  },
62
62
  loading: {
@@ -67,13 +67,12 @@ const LogoDefinition = defineComponent({
67
67
  },
68
68
  guideline: "Brand logo linking to the configured home; a CMS entry overrides its src, alt, and href at runtime. Every prop is optional: an element bound to a CMS entry should author none of them, so an unconfigured store renders no logo instead of a placeholder. Author src and alt only where no entry supplies them, never as a fallback beside one. Size it responsively with a swatch on data-fs-logo, and reach the mark itself through data-fs-logo-image. A destination that is not an http, https, mailto, tel, or site-relative URL is dropped and the image renders unlinked. Loads eagerly for the above-the-fold header; set loading to lazy below the fold. Composes the Image and NavigationLink particles."
69
69
  });
70
- function Logo({ src, alt, href = "/", loading = "eager", $content, className, ...props }) {
70
+ function Logo({ src, alt, href = { standard: "home" }, loading = "eager", $content, className, ...props }) {
71
71
  const logoSrc = $content?.src?.trim() ? $content.src : src;
72
72
  const logoAlt = $content?.alt ?? alt;
73
73
  const logoHref = $content?.href?.trim() ? $content.href : href;
74
74
  if (!logoSrc || !logoAlt?.trim()) return null;
75
- const destination = resolveSafeHref(logoHref);
76
- if (!destination.safe) console.warn("[Logo] link destination rejected", { reason: destination.reason });
75
+ const destination = resolveLogoHref(logoHref);
77
76
  const image = /* @__PURE__ */ jsx(Image, {
78
77
  src: logoSrc,
79
78
  alt: logoAlt,
@@ -84,12 +83,25 @@ function Logo({ src, alt, href = "/", loading = "eager", $content, className, ..
84
83
  ...props,
85
84
  className,
86
85
  "data-fs-logo": "",
87
- children: destination.safe ? /* @__PURE__ */ jsx(NavigationLink, {
88
- href: destination.href,
86
+ children: destination === null ? image : /* @__PURE__ */ jsx(NavigationLink, {
87
+ href: destination,
89
88
  children: image
90
- }) : image
89
+ })
91
90
  });
92
91
  }
92
+ /**
93
+ * A page reference skips the string allowlist. A string is checked, and a refusal drops the link.
94
+ * No read-time validator enforces `format` or `pattern`, so this is the only gate on a CMS string.
95
+ */
96
+ function resolveLogoHref(href) {
97
+ if (typeof href !== "string") return href;
98
+ const destination = resolveSafeHref(href);
99
+ if (!destination.safe) {
100
+ console.warn("[Logo] link destination rejected", { reason: destination.reason });
101
+ return null;
102
+ }
103
+ return destination.href;
104
+ }
93
105
  //#endregion
94
106
  export { Logo, LogoContent, LogoDefinition };
95
107
 
@@ -1 +1 @@
1
- {"version":3,"file":"logo.js","names":[],"sources":["../../../src/particles/logo/logo.tsx"],"sourcesContent":["import { type ContentValue, defineComponent, defineContentType } from '@vtex/fs-core';\nimport type { ComponentPropsWithRef } from 'react';\nimport { resolveSafeHref } from '../../utils/safe-href.js';\nimport { Image } from '../image/image.js';\nimport { NavigationLink } from '../navigation-link/navigation-link.js';\n\n// `src` declares no `format`: a store's logo is usually a root-relative path served from its\n// `public/` folder (`/logo.svg`), which JSON Schema's `uri` format rejects.\nexport const LogoContent = defineContentType({\n id: 'logo',\n title: 'Logo',\n description: 'Brand logo asset, alt text, and link destination.',\n schema: {\n type: 'object',\n additionalProperties: false,\n // `src` is optional so an entry authoring `alt` alone still validates: a slice missing a\n // required property is shape-invalid, and the boundary drops the element instead of the field.\n required: ['alt'],\n properties: {\n src: { type: 'string', minLength: 1, title: 'Source URL', widget: { 'ui:widget': 'media-gallery' } },\n alt: { type: 'string', minLength: 1, title: 'Alt text' },\n href: {\n type: 'string',\n minLength: 1,\n title: 'Link destination',\n format: 'uri-reference',\n // The set `resolveSafeHref` admits, so the Admin form cannot reject a destination the render\n // gate would have kept. Author-time only and lookahead-free, as in the sibling particles.\n pattern: '^(?:https?://.*|mailto:.+|tel:.+|/(?:[^/].*)?)$',\n },\n },\n },\n});\n\n/** Props for {@link Logo}. Extends native `<div>` attributes (the styling root). */\nexport type LogoProps = Omit<ComponentPropsWithRef<'div'>, 'children'> & {\n /** Brand logo asset URL. Optional: the render gate, not the type, requires a resolved value. */\n src?: string;\n /** Accessible alt text. Optional: the render gate, not the type, requires a resolved value. */\n alt?: string;\n /** Navigation destination; defaults to the configured home. Dropped when it fails the safety check. */\n href?: string;\n /** Image loading strategy. `eager` suits the above-the-fold header; author `lazy` below it. */\n loading?: 'lazy' | 'eager';\n /** Merchant-authored logo, sliced from the page/layout content entry; wins over the props when\n * present. Absent in a non-CMS store — the props are the fallback. */\n $content?: ContentValue<typeof LogoContent>;\n};\n\nexport const LogoDefinition = defineComponent<LogoProps>({\n name: 'Logo',\n importPath: '@vtex/faststore/components',\n component: Logo,\n content: LogoContent,\n editor: { label: 'Logo', category: 'content' },\n schema: {\n src: { type: 'string', label: 'Source URL' },\n alt: { type: 'string', label: 'Alt Text' },\n href: { type: 'string', default: '/', label: 'Link Destination' },\n loading: { type: ['lazy', 'eager'] as const, default: 'eager', label: 'Loading' },\n },\n guideline:\n 'Brand logo linking to the configured home; a CMS entry overrides its src, alt, and href at runtime. Every prop is optional: an element bound to a CMS entry should author none of them, so an unconfigured store renders no logo instead of a placeholder. Author src and alt only where no entry supplies them, never as a fallback beside one. Size it responsively with a swatch on data-fs-logo, and reach the mark itself through data-fs-logo-image. A destination that is not an http, https, mailto, tel, or site-relative URL is dropped and the image renders unlinked. Loads eagerly for the above-the-fold header; set loading to lazy below the fold. Composes the Image and NavigationLink particles.',\n});\n\nexport function Logo({ src, alt, href = '/', loading = 'eager', $content, className, ...props }: LogoProps) {\n // The optional content fields fall through when blank, so clearing one in the Admin reads as\n // unauthored. `alt` is required in the model, so a blank value is a violation the gate rejects.\n const logoSrc = $content?.src?.trim() ? $content.src : src;\n const logoAlt = $content?.alt ?? alt;\n const logoHref = $content?.href?.trim() ? $content.href : href;\n\n if (!logoSrc || !logoAlt?.trim()) {\n return null;\n }\n\n // No read-time validator enforces `format` or `pattern`, so this is the only gate on the CMS value.\n const destination = resolveSafeHref(logoHref);\n if (!destination.safe) {\n // A bounded reason and no authored value, the convention the content runtime's own log follows.\n console.warn('[Logo] link destination rejected', { reason: destination.reason });\n }\n\n const image = <Image src={logoSrc} alt={logoAlt} loading={loading} data-fs-logo-image=\"\" />;\n\n return (\n <div {...props} className={className} data-fs-logo=\"\">\n {destination.safe ? <NavigationLink href={destination.href}>{image}</NavigationLink> : image}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAQA,MAAa,cAAc,kBAAkB;CAC3C,IAAI;CACJ,OAAO;CACP,aAAa;CACb,QAAQ;EACN,MAAM;EACN,sBAAsB;EAGtB,UAAU,CAAC,KAAK;EAChB,YAAY;GACV,KAAK;IAAE,MAAM;IAAU,WAAW;IAAG,OAAO;IAAc,QAAQ,EAAE,aAAa,gBAAgB;GAAE;GACnG,KAAK;IAAE,MAAM;IAAU,WAAW;IAAG,OAAO;GAAW;GACvD,MAAM;IACJ,MAAM;IACN,WAAW;IACX,OAAO;IACP,QAAQ;IAGR,SAAS;GACX;EACF;CACF;AACF,CAAC;AAiBD,MAAa,iBAAiB,gBAA2B;CACvD,MAAM;CACN,YAAY;CACZ,WAAW;CACX,SAAS;CACT,QAAQ;EAAE,OAAO;EAAQ,UAAU;CAAU;CAC7C,QAAQ;EACN,KAAK;GAAE,MAAM;GAAU,OAAO;EAAa;EAC3C,KAAK;GAAE,MAAM;GAAU,OAAO;EAAW;EACzC,MAAM;GAAE,MAAM;GAAU,SAAS;GAAK,OAAO;EAAmB;EAChE,SAAS;GAAE,MAAM,CAAC,QAAQ,OAAO;GAAY,SAAS;GAAS,OAAO;EAAU;CAClF;CACA,WACE;AACJ,CAAC;AAED,SAAgB,KAAK,EAAE,KAAK,KAAK,OAAO,KAAK,UAAU,SAAS,UAAU,WAAW,GAAG,SAAoB;CAG1G,MAAM,UAAU,UAAU,KAAK,KAAK,IAAI,SAAS,MAAM;CACvD,MAAM,UAAU,UAAU,OAAO;CACjC,MAAM,WAAW,UAAU,MAAM,KAAK,IAAI,SAAS,OAAO;CAE1D,IAAI,CAAC,WAAW,CAAC,SAAS,KAAK,GAC7B,OAAO;CAIT,MAAM,cAAc,gBAAgB,QAAQ;CAC5C,IAAI,CAAC,YAAY,MAEf,QAAQ,KAAK,oCAAoC,EAAE,QAAQ,YAAY,OAAO,CAAC;CAGjF,MAAM,QAAQ,oBAAC,OAAD;EAAO,KAAK;EAAS,KAAK;EAAkB;EAAS,sBAAmB;CAAI,CAAA;CAE1F,OACE,oBAAC,OAAD;EAAK,GAAI;EAAkB;EAAW,gBAAa;EAChD,UAAA,YAAY,OAAO,oBAAC,gBAAD;GAAgB,MAAM,YAAY;GAAO,UAAA;EAAsB,CAAA,IAAI;CACpF,CAAA;AAET"}
1
+ {"version":3,"file":"logo.js","names":[],"sources":["../../../src/particles/logo/logo.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 { Image } from '../image/image.js';\nimport { NavigationLink } from '../navigation-link/navigation-link.js';\n\n// `src` declares no `format`: a store's logo is usually a root-relative path served from its\n// `public/` folder (`/logo.svg`), which JSON Schema's `uri` format rejects.\nexport const LogoContent = defineContentType({\n id: 'logo',\n title: 'Logo',\n description: 'Brand logo asset, alt text, and link destination.',\n schema: {\n type: 'object',\n additionalProperties: false,\n // `src` is optional so an entry authoring `alt` alone still validates: a slice missing a\n // required property is shape-invalid, and the boundary drops the element instead of the field.\n required: ['alt'],\n properties: {\n src: { type: 'string', minLength: 1, title: 'Source URL', widget: { 'ui:widget': 'media-gallery' } },\n alt: { type: 'string', minLength: 1, title: 'Alt text' },\n href: {\n type: 'string',\n minLength: 1,\n title: 'Link destination',\n format: 'uri-reference',\n // The set `resolveSafeHref` admits, so the Admin form cannot reject a destination the render\n // gate would have kept. Author-time only and lookahead-free, as in the sibling particles.\n pattern: '^(?:https?://.*|mailto:.+|tel:.+|/(?:[^/].*)?)$',\n },\n },\n },\n});\n\n/** Props for {@link Logo}. Extends native `<div>` attributes (the styling root). */\nexport type LogoProps = Omit<ComponentPropsWithRef<'div'>, 'children'> & {\n /** Brand logo asset URL. Optional: the render gate, not the type, requires a resolved value. */\n src?: string;\n /** Accessible alt text. Optional: the render gate, not the type, requires a resolved value. */\n alt?: string;\n /** Navigation destination; defaults to the home page. A string that fails the safety check is dropped. */\n href?: PageRef | string;\n /** Image loading strategy. `eager` suits the above-the-fold header; author `lazy` below it. */\n loading?: 'lazy' | 'eager';\n /** Merchant-authored logo, sliced from the page/layout content entry; wins over the props when\n * present. Absent in a non-CMS store — the props are the fallback. */\n $content?: ContentValue<typeof LogoContent>;\n};\n\nexport const LogoDefinition = defineComponent<LogoProps>({\n name: 'Logo',\n importPath: '@vtex/faststore/components',\n component: Logo,\n content: LogoContent,\n editor: { label: 'Logo', category: 'content' },\n schema: {\n src: { type: 'string', label: 'Source URL' },\n alt: { type: 'string', label: 'Alt Text' },\n href: { type: 'link', default: { standard: 'home' }, label: 'Link Destination' },\n loading: { type: ['lazy', 'eager'] as const, default: 'eager', label: 'Loading' },\n },\n guideline:\n 'Brand logo linking to the configured home; a CMS entry overrides its src, alt, and href at runtime. Every prop is optional: an element bound to a CMS entry should author none of them, so an unconfigured store renders no logo instead of a placeholder. Author src and alt only where no entry supplies them, never as a fallback beside one. Size it responsively with a swatch on data-fs-logo, and reach the mark itself through data-fs-logo-image. A destination that is not an http, https, mailto, tel, or site-relative URL is dropped and the image renders unlinked. Loads eagerly for the above-the-fold header; set loading to lazy below the fold. Composes the Image and NavigationLink particles.',\n});\n\nexport function Logo({\n src,\n alt,\n href = { standard: 'home' },\n loading = 'eager',\n $content,\n className,\n ...props\n}: LogoProps) {\n // The optional content fields fall through when blank, so clearing one in the Admin reads as\n // unauthored. `alt` is required in the model, so a blank value is a violation the gate rejects.\n const logoSrc = $content?.src?.trim() ? $content.src : src;\n const logoAlt = $content?.alt ?? alt;\n // A non-blank CMS string wins, and a refusal drops the link rather than falling back to the prop.\n const logoHref = $content?.href?.trim() ? $content.href : href;\n\n if (!logoSrc || !logoAlt?.trim()) {\n return null;\n }\n\n const destination = resolveLogoHref(logoHref);\n const image = <Image src={logoSrc} alt={logoAlt} loading={loading} data-fs-logo-image=\"\" />;\n\n return (\n <div {...props} className={className} data-fs-logo=\"\">\n {destination === null ? image : <NavigationLink href={destination}>{image}</NavigationLink>}\n </div>\n );\n}\n\n/**\n * A page reference skips the string allowlist. A string is checked, and a refusal drops the link.\n * No read-time validator enforces `format` or `pattern`, so this is the only gate on a CMS string.\n */\nfunction resolveLogoHref(href: PageRef | string): PageRef | string | null {\n if (typeof href !== 'string') return href;\n\n const destination = resolveSafeHref(href);\n if (!destination.safe) {\n // A bounded reason and no authored value, the convention the content runtime's own log follows.\n console.warn('[Logo] link destination rejected', { reason: destination.reason });\n return null;\n }\n\n return destination.href;\n}\n"],"mappings":";;;;;;;;AASA,MAAa,cAAc,kBAAkB;CAC3C,IAAI;CACJ,OAAO;CACP,aAAa;CACb,QAAQ;EACN,MAAM;EACN,sBAAsB;EAGtB,UAAU,CAAC,KAAK;EAChB,YAAY;GACV,KAAK;IAAE,MAAM;IAAU,WAAW;IAAG,OAAO;IAAc,QAAQ,EAAE,aAAa,gBAAgB;GAAE;GACnG,KAAK;IAAE,MAAM;IAAU,WAAW;IAAG,OAAO;GAAW;GACvD,MAAM;IACJ,MAAM;IACN,WAAW;IACX,OAAO;IACP,QAAQ;IAGR,SAAS;GACX;EACF;CACF;AACF,CAAC;AAiBD,MAAa,iBAAiB,gBAA2B;CACvD,MAAM;CACN,YAAY;CACZ,WAAW;CACX,SAAS;CACT,QAAQ;EAAE,OAAO;EAAQ,UAAU;CAAU;CAC7C,QAAQ;EACN,KAAK;GAAE,MAAM;GAAU,OAAO;EAAa;EAC3C,KAAK;GAAE,MAAM;GAAU,OAAO;EAAW;EACzC,MAAM;GAAE,MAAM;GAAQ,SAAS,EAAE,UAAU,OAAO;GAAG,OAAO;EAAmB;EAC/E,SAAS;GAAE,MAAM,CAAC,QAAQ,OAAO;GAAY,SAAS;GAAS,OAAO;EAAU;CAClF;CACA,WACE;AACJ,CAAC;AAED,SAAgB,KAAK,EACnB,KACA,KACA,OAAO,EAAE,UAAU,OAAO,GAC1B,UAAU,SACV,UACA,WACA,GAAG,SACS;CAGZ,MAAM,UAAU,UAAU,KAAK,KAAK,IAAI,SAAS,MAAM;CACvD,MAAM,UAAU,UAAU,OAAO;CAEjC,MAAM,WAAW,UAAU,MAAM,KAAK,IAAI,SAAS,OAAO;CAE1D,IAAI,CAAC,WAAW,CAAC,SAAS,KAAK,GAC7B,OAAO;CAGT,MAAM,cAAc,gBAAgB,QAAQ;CAC5C,MAAM,QAAQ,oBAAC,OAAD;EAAO,KAAK;EAAS,KAAK;EAAkB;EAAS,sBAAmB;CAAI,CAAA;CAE1F,OACE,oBAAC,OAAD;EAAK,GAAI;EAAkB;EAAW,gBAAa;EAChD,UAAA,gBAAgB,OAAO,QAAQ,oBAAC,gBAAD;GAAgB,MAAM;GAAc,UAAA;EAAsB,CAAA;CACvF,CAAA;AAET;;;;;AAMA,SAAS,gBAAgB,MAAiD;CACxE,IAAI,OAAO,SAAS,UAAU,OAAO;CAErC,MAAM,cAAc,gBAAgB,IAAI;CACxC,IAAI,CAAC,YAAY,MAAM;EAErB,QAAQ,KAAK,oCAAoC,EAAE,QAAQ,YAAY,OAAO,CAAC;EAC/E,OAAO;CACT;CAEA,OAAO,YAAY;AACrB"}
@@ -1,8 +1,7 @@
1
1
  import { ContentValue } from "../../core/dist/define-content-type.js";
2
2
  import { ComponentDefinition } from "../../core/dist/define-component.js";
3
3
  import "../../core/dist/index.js";
4
- import { ComponentPropsWithRef } from "react";
5
- import Link from "next/link";
4
+ import { StorefrontLinkProps } from "../../sdk/navigation/storefront-link.js";
6
5
  //#region src/particles/navigation-link/navigation-link.d.ts
7
6
  export declare const NavigationLinkContent: {
8
7
  readonly id: "navigation-link";
@@ -32,7 +31,7 @@ export declare const NavigationLinkContent: {
32
31
  };
33
32
  };
34
33
  };
35
- export type NavigationLinkProps = ComponentPropsWithRef<typeof Link> & {
34
+ export type NavigationLinkProps = StorefrontLinkProps & {
36
35
  /** Merchant-authored link, sliced from the page/layout content entry; wins over the props field by
37
36
  * field when present. Absent in a non-CMS store — the props are the fallback. */
38
37
  $content?: ContentValue<typeof NavigationLinkContent>;
@@ -3,8 +3,8 @@ import { defineComponent } from "../../core/dist/define-component.js";
3
3
  import { defineContentType } from "../../core/dist/define-content-type.js";
4
4
  import "../../core/dist/index.js";
5
5
  import { resolveSafeHref } from "../../utils/safe-href.js";
6
+ import { StorefrontLink } from "../../sdk/navigation/storefront-link.js";
6
7
  import { jsx } from "react/jsx-runtime";
7
- import Link from "next/link";
8
8
  //#region src/particles/navigation-link/navigation-link.tsx
9
9
  const NavigationLinkContent = defineContentType({
10
10
  id: "navigation-link",
@@ -45,7 +45,7 @@ const NavigationLinkDefinition = defineComponent({
45
45
  },
46
46
  schema: {
47
47
  href: {
48
- type: "string",
48
+ type: "link",
49
49
  label: "URL"
50
50
  },
51
51
  target: {
@@ -54,7 +54,7 @@ const NavigationLinkDefinition = defineComponent({
54
54
  label: "Target"
55
55
  }
56
56
  },
57
- guideline: "Client-side navigation link built on Next.js Link. Use for all internal navigation to avoid full-page reloads. Set target to _blank to open the destination in a new tab; when you do, write an \"(opens in new tab)\" cue into the link text, because the component adds no announcement of its own. A CMS entry overrides the link text, href, and target at runtime; a destination it supplies that is not an http, https, mailto, tel, or site-relative URL is refused and the authored href stands."
57
+ guideline: "Client-side navigation link that renders StorefrontLink, where href is a page reference or a logical path. Use for all internal navigation to avoid full-page reloads. Set target to _blank to open the destination in a new tab; when you do, write an \"(opens in new tab)\" cue into the link text, because the component adds no announcement of its own. A CMS entry overrides the link text, href, and target at runtime; a destination it supplies that is not an http, https, mailto, tel, or site-relative URL is refused and the authored href stands."
58
58
  });
59
59
  function NavigationLink({ href, className, children, target, rel, $content, ...props }) {
60
60
  const entryDestination = $content?.href?.trim() ? resolveSafeHref($content.href) : null;
@@ -66,7 +66,7 @@ function NavigationLink({ href, className, children, target, rel, $content, ...p
66
66
  const relTokens = rel?.split(/\s+/).filter(Boolean) ?? [];
67
67
  const present = new Set(relTokens.map((token) => token.toLowerCase()));
68
68
  const safeRel = opensNewTab ? [...relTokens, ...["noopener", "noreferrer"].filter((token) => !present.has(token))].join(" ") : rel;
69
- return /* @__PURE__ */ jsx(Link, {
69
+ return /* @__PURE__ */ jsx(StorefrontLink, {
70
70
  ...props,
71
71
  href: linkHref,
72
72
  target: linkTarget,
@@ -1 +1 @@
1
- {"version":3,"file":"navigation-link.js","names":[],"sources":["../../../src/particles/navigation-link/navigation-link.tsx"],"sourcesContent":["'use client';\n\nimport { type ContentValue, defineComponent, defineContentType } from '@vtex/fs-core';\nimport Link from 'next/link';\nimport type { ComponentPropsWithRef } from 'react';\nimport { resolveSafeHref } from '../../utils/safe-href.js';\n\n// Only `text` is required — an entry that just retitles a link should not force the merchant to\n// retype its destination.\nexport const NavigationLinkContent = defineContentType({\n id: 'navigation-link',\n title: 'Navigation Link',\n description: 'Link label, destination, and whether it opens in a new tab.',\n schema: {\n type: 'object',\n additionalProperties: false,\n required: ['text'],\n properties: {\n text: { type: 'string', minLength: 1, title: 'Label' },\n href: {\n type: 'string',\n title: 'Destination',\n // `uri-reference` rather than `uri`, which rejects the root-relative paths most navigation\n // targets are. Author-time guidance only and lookahead-free, as in the sibling particles;\n // the empty alternative is this model's own, since clearing the field has to stay saveable.\n format: 'uri-reference',\n pattern: '^$|^(?:https?://.*|mailto:.+|tel:.+|/(?:[^/].*)?)$',\n },\n target: { type: 'string', enum: ['_self', '_blank'], title: 'Target' },\n },\n },\n});\n\nexport type NavigationLinkProps = ComponentPropsWithRef<typeof Link> & {\n /** Merchant-authored link, sliced from the page/layout content entry; wins over the props field by\n * field when present. Absent in a non-CMS store — the props are the fallback. */\n $content?: ContentValue<typeof NavigationLinkContent>;\n};\n\nexport const NavigationLinkDefinition = defineComponent<NavigationLinkProps>({\n name: 'NavigationLink',\n importPath: '@vtex/faststore/components',\n component: NavigationLink,\n content: NavigationLinkContent,\n editor: { label: 'Navigation Link', category: 'navigation' },\n schema: {\n href: { type: 'string', label: 'URL' },\n target: { type: ['_self', '_blank'] as const, default: '_self', label: 'Target' },\n },\n guideline:\n 'Client-side navigation link built on Next.js Link. Use for all internal navigation to avoid full-page reloads. Set target to _blank to open the destination in a new tab; when you do, write an \"(opens in new tab)\" cue into the link text, because the component adds no announcement of its own. A CMS entry overrides the link text, href, and target at runtime; a destination it supplies that is not an http, https, mailto, tel, or site-relative URL is refused and the authored href stands.',\n});\n\nexport function NavigationLink({ href, className, children, target, rel, $content, ...props }: NavigationLinkProps) {\n // Resolve the entry before deriving anything from `target`: a _blank arriving from the CMS needs the\n // same tab-nabbing defense as an authored one. `href` and `text` fall back on a blank entry field so\n // a cleared value keeps the authored link rather than emitting an empty destination or label.\n // Only the entry's destination goes through the allowlist: the prop comes from the store's own\n // source, while nothing enforces the model's shape on the value the Admin supplies. A refused\n // destination falls back the same way a cleared one does.\n const entryDestination = $content?.href?.trim() ? resolveSafeHref($content.href) : null;\n if (entryDestination && !entryDestination.safe) {\n // A bounded reason and no authored value, the convention the content runtime's own log follows.\n console.warn('[NavigationLink] link destination rejected', { reason: entryDestination.reason });\n }\n const linkHref = entryDestination?.safe ? entryDestination.href : href;\n const linkTarget = $content?.target ?? target;\n const linkText = $content?.text?.trim() ? $content.text : children;\n\n // Target keywords are ASCII case-insensitive per HTML, so _BLANK opens a new tab just like _blank.\n const opensNewTab = linkTarget?.toLowerCase() === '_blank';\n // Append rather than replace: a composing matter may pass its own rel, and the tab-nabbing defense has to survive it.\n // Link types are case-insensitive too, so a caller-passed NOOPENER already counts as noopener.\n const relTokens = rel?.split(/\\s+/).filter(Boolean) ?? [];\n const present = new Set(relTokens.map((token) => token.toLowerCase()));\n const safeRel = opensNewTab\n ? [...relTokens, ...['noopener', 'noreferrer'].filter((token) => !present.has(token))].join(' ')\n : rel;\n\n return (\n <Link {...props} href={linkHref} target={linkTarget} rel={safeRel} className={className} data-fs-navigation-link=\"\">\n {linkText}\n </Link>\n );\n}\n"],"mappings":";;;;;;;;AASA,MAAa,wBAAwB,kBAAkB;CACrD,IAAI;CACJ,OAAO;CACP,aAAa;CACb,QAAQ;EACN,MAAM;EACN,sBAAsB;EACtB,UAAU,CAAC,MAAM;EACjB,YAAY;GACV,MAAM;IAAE,MAAM;IAAU,WAAW;IAAG,OAAO;GAAQ;GACrD,MAAM;IACJ,MAAM;IACN,OAAO;IAIP,QAAQ;IACR,SAAS;GACX;GACA,QAAQ;IAAE,MAAM;IAAU,MAAM,CAAC,SAAS,QAAQ;IAAG,OAAO;GAAS;EACvE;CACF;AACF,CAAC;AAQD,MAAa,2BAA2B,gBAAqC;CAC3E,MAAM;CACN,YAAY;CACZ,WAAW;CACX,SAAS;CACT,QAAQ;EAAE,OAAO;EAAmB,UAAU;CAAa;CAC3D,QAAQ;EACN,MAAM;GAAE,MAAM;GAAU,OAAO;EAAM;EACrC,QAAQ;GAAE,MAAM,CAAC,SAAS,QAAQ;GAAY,SAAS;GAAS,OAAO;EAAS;CAClF;CACA,WACE;AACJ,CAAC;AAED,SAAgB,eAAe,EAAE,MAAM,WAAW,UAAU,QAAQ,KAAK,UAAU,GAAG,SAA8B;CAOlH,MAAM,mBAAmB,UAAU,MAAM,KAAK,IAAI,gBAAgB,SAAS,IAAI,IAAI;CACnF,IAAI,oBAAoB,CAAC,iBAAiB,MAExC,QAAQ,KAAK,8CAA8C,EAAE,QAAQ,iBAAiB,OAAO,CAAC;CAEhG,MAAM,WAAW,kBAAkB,OAAO,iBAAiB,OAAO;CAClE,MAAM,aAAa,UAAU,UAAU;CACvC,MAAM,WAAW,UAAU,MAAM,KAAK,IAAI,SAAS,OAAO;CAG1D,MAAM,cAAc,YAAY,YAAY,MAAM;CAGlD,MAAM,YAAY,KAAK,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO,KAAK,CAAC;CACxD,MAAM,UAAU,IAAI,IAAI,UAAU,KAAK,UAAU,MAAM,YAAY,CAAC,CAAC;CACrE,MAAM,UAAU,cACZ,CAAC,GAAG,WAAW,GAAG,CAAC,YAAY,YAAY,CAAC,CAAC,QAAQ,UAAU,CAAC,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAC7F;CAEJ,OACE,oBAAC,MAAD;EAAM,GAAI;EAAO,MAAM;EAAU,QAAQ;EAAY,KAAK;EAAoB;EAAW,2BAAwB;EAC9G,UAAA;CACG,CAAA;AAEV"}
1
+ {"version":3,"file":"navigation-link.js","names":[],"sources":["../../../src/particles/navigation-link/navigation-link.tsx"],"sourcesContent":["'use client';\n\nimport { type ContentValue, defineComponent, defineContentType } from '@vtex/fs-core';\nimport { StorefrontLink, type StorefrontLinkProps } from '../../sdk/navigation/storefront-link.js';\nimport { resolveSafeHref } from '../../utils/safe-href.js';\n\n// Only `text` is required — an entry that just retitles a link should not force the merchant to\n// retype its destination.\nexport const NavigationLinkContent = defineContentType({\n id: 'navigation-link',\n title: 'Navigation Link',\n description: 'Link label, destination, and whether it opens in a new tab.',\n schema: {\n type: 'object',\n additionalProperties: false,\n required: ['text'],\n properties: {\n text: { type: 'string', minLength: 1, title: 'Label' },\n href: {\n type: 'string',\n title: 'Destination',\n // `uri-reference` rather than `uri`, which rejects the root-relative paths most navigation\n // targets are. Author-time guidance only and lookahead-free, as in the sibling particles;\n // the empty alternative is this model's own, since clearing the field has to stay saveable.\n format: 'uri-reference',\n pattern: '^$|^(?:https?://.*|mailto:.+|tel:.+|/(?:[^/].*)?)$',\n },\n target: { type: 'string', enum: ['_self', '_blank'], title: 'Target' },\n },\n },\n});\n\nexport type NavigationLinkProps = StorefrontLinkProps & {\n /** Merchant-authored link, sliced from the page/layout content entry; wins over the props field by\n * field when present. Absent in a non-CMS store — the props are the fallback. */\n $content?: ContentValue<typeof NavigationLinkContent>;\n};\n\nexport const NavigationLinkDefinition = defineComponent<NavigationLinkProps>({\n name: 'NavigationLink',\n importPath: '@vtex/faststore/components',\n component: NavigationLink,\n content: NavigationLinkContent,\n editor: { label: 'Navigation Link', category: 'navigation' },\n schema: {\n href: { type: 'link', label: 'URL' },\n target: { type: ['_self', '_blank'] as const, default: '_self', label: 'Target' },\n },\n guideline:\n 'Client-side navigation link that renders StorefrontLink, where href is a page reference or a logical path. Use for all internal navigation to avoid full-page reloads. Set target to _blank to open the destination in a new tab; when you do, write an \"(opens in new tab)\" cue into the link text, because the component adds no announcement of its own. A CMS entry overrides the link text, href, and target at runtime; a destination it supplies that is not an http, https, mailto, tel, or site-relative URL is refused and the authored href stands.',\n});\n\nexport function NavigationLink({ href, className, children, target, rel, $content, ...props }: NavigationLinkProps) {\n // Resolve the entry before deriving anything from `target`: a _blank arriving from the CMS needs the\n // same tab-nabbing defense as an authored one. `href` and `text` fall back on a blank entry field so\n // a cleared value keeps the authored link rather than emitting an empty destination or label.\n // Only the entry's destination goes through the allowlist: the prop comes from the store's own\n // source, while nothing enforces the model's shape on the value the Admin supplies. A refused\n // destination falls back the same way a cleared one does.\n const entryDestination = $content?.href?.trim() ? resolveSafeHref($content.href) : null;\n if (entryDestination && !entryDestination.safe) {\n // A bounded reason and no authored value, the convention the content runtime's own log follows.\n console.warn('[NavigationLink] link destination rejected', { reason: entryDestination.reason });\n }\n const linkHref = entryDestination?.safe ? entryDestination.href : href;\n const linkTarget = $content?.target ?? target;\n const linkText = $content?.text?.trim() ? $content.text : children;\n\n // Target keywords are ASCII case-insensitive per HTML, so _BLANK opens a new tab just like _blank.\n const opensNewTab = linkTarget?.toLowerCase() === '_blank';\n // Append rather than replace: a composing matter may pass its own rel, and the tab-nabbing defense has to survive it.\n // Link types are case-insensitive too, so a caller-passed NOOPENER already counts as noopener.\n const relTokens = rel?.split(/\\s+/).filter(Boolean) ?? [];\n const present = new Set(relTokens.map((token) => token.toLowerCase()));\n const safeRel = opensNewTab\n ? [...relTokens, ...['noopener', 'noreferrer'].filter((token) => !present.has(token))].join(' ')\n : rel;\n\n return (\n <StorefrontLink\n {...props}\n href={linkHref}\n target={linkTarget}\n rel={safeRel}\n className={className}\n data-fs-navigation-link=\"\"\n >\n {linkText}\n </StorefrontLink>\n );\n}\n"],"mappings":";;;;;;;;AAQA,MAAa,wBAAwB,kBAAkB;CACrD,IAAI;CACJ,OAAO;CACP,aAAa;CACb,QAAQ;EACN,MAAM;EACN,sBAAsB;EACtB,UAAU,CAAC,MAAM;EACjB,YAAY;GACV,MAAM;IAAE,MAAM;IAAU,WAAW;IAAG,OAAO;GAAQ;GACrD,MAAM;IACJ,MAAM;IACN,OAAO;IAIP,QAAQ;IACR,SAAS;GACX;GACA,QAAQ;IAAE,MAAM;IAAU,MAAM,CAAC,SAAS,QAAQ;IAAG,OAAO;GAAS;EACvE;CACF;AACF,CAAC;AAQD,MAAa,2BAA2B,gBAAqC;CAC3E,MAAM;CACN,YAAY;CACZ,WAAW;CACX,SAAS;CACT,QAAQ;EAAE,OAAO;EAAmB,UAAU;CAAa;CAC3D,QAAQ;EACN,MAAM;GAAE,MAAM;GAAQ,OAAO;EAAM;EACnC,QAAQ;GAAE,MAAM,CAAC,SAAS,QAAQ;GAAY,SAAS;GAAS,OAAO;EAAS;CAClF;CACA,WACE;AACJ,CAAC;AAED,SAAgB,eAAe,EAAE,MAAM,WAAW,UAAU,QAAQ,KAAK,UAAU,GAAG,SAA8B;CAOlH,MAAM,mBAAmB,UAAU,MAAM,KAAK,IAAI,gBAAgB,SAAS,IAAI,IAAI;CACnF,IAAI,oBAAoB,CAAC,iBAAiB,MAExC,QAAQ,KAAK,8CAA8C,EAAE,QAAQ,iBAAiB,OAAO,CAAC;CAEhG,MAAM,WAAW,kBAAkB,OAAO,iBAAiB,OAAO;CAClE,MAAM,aAAa,UAAU,UAAU;CACvC,MAAM,WAAW,UAAU,MAAM,KAAK,IAAI,SAAS,OAAO;CAG1D,MAAM,cAAc,YAAY,YAAY,MAAM;CAGlD,MAAM,YAAY,KAAK,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO,KAAK,CAAC;CACxD,MAAM,UAAU,IAAI,IAAI,UAAU,KAAK,UAAU,MAAM,YAAY,CAAC,CAAC;CACrE,MAAM,UAAU,cACZ,CAAC,GAAG,WAAW,GAAG,CAAC,YAAY,YAAY,CAAC,CAAC,QAAQ,UAAU,CAAC,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAC7F;CAEJ,OACE,oBAAC,gBAAD;EACE,GAAI;EACJ,MAAM;EACN,QAAQ;EACR,KAAK;EACM;EACX,2BAAwB;EAEvB,UAAA;CACa,CAAA;AAEpB"}
@@ -1,6 +1,7 @@
1
1
  import { ContentValue } from "../../core/dist/define-content-type.js";
2
2
  import { ComponentDefinition } from "../../core/dist/define-component.js";
3
3
  import "../../core/dist/index.js";
4
+ import { PageRef } from "../../sdk/navigation/types.js";
4
5
  import { ComponentPropsWithRef } from "react";
5
6
  //#region src/particles/navigation-link-list/navigation-link-list.d.ts
6
7
  export declare const NavigationLinkListContent: {
@@ -45,8 +46,11 @@ export declare const NavigationLinkListContent: {
45
46
  };
46
47
  };
47
48
  type NavigationLinkListContentValue = ContentValue<typeof NavigationLinkListContent>;
48
- /** One authored link. Derived from the model so the prop and content channels cannot drift. */
49
- export type NavigationLinkListLink = NonNullable<NavigationLinkListContentValue['links']>[number];
49
+ type NavigationLinkListContentLink = NonNullable<NavigationLinkListContentValue['links']>[number];
50
+ /** One authored link. The prop accepts a page reference; a CMS href stays a string. */
51
+ export type NavigationLinkListLink = Omit<NavigationLinkListContentLink, 'href'> & {
52
+ href: PageRef | string;
53
+ };
50
54
  export type NavigationLinkListProps = ComponentPropsWithRef<'ul'> & {
51
55
  /** Authored fallback for the link list, used when no CMS entry supplies one. */
52
56
  links?: readonly NavigationLinkListLink[];
@@ -59,25 +59,36 @@ const NavigationLinkListDefinition = defineComponent({
59
59
  } },
60
60
  guideline: "The list of navigation links as one CMS-editable collection, so a merchant adds, removes and reorders them without a deployment. Owns its own <ul>, so compose it directly under NavigationMenuRoot for the desktop bar and inside DrawerPopup for the mobile panel — reference the same element id from both and one entry feeds both surfaces. See navigation-link-list.md."
61
61
  });
62
+ function readLinks(content, authored) {
63
+ if (content !== void 0) return content;
64
+ return authored ?? [];
65
+ }
66
+ function resolveDestination(href, index) {
67
+ if (typeof href !== "string") return href;
68
+ const destination = resolveSafeHref(href);
69
+ if (!destination.safe) {
70
+ console.warn("[NavigationLinkList] link destination rejected", {
71
+ index,
72
+ reason: destination.reason
73
+ });
74
+ return null;
75
+ }
76
+ return destination.href;
77
+ }
62
78
  function resolveLinks(links) {
63
79
  const resolved = [];
64
80
  links.forEach((link, index) => {
65
81
  if (link.label.trim().length === 0) return;
66
- const destination = resolveSafeHref(link.href);
67
- if (!destination.safe) console.warn("[NavigationLinkList] link destination rejected", {
68
- index,
69
- reason: destination.reason
70
- });
71
82
  resolved.push({
72
83
  label: link.label,
73
- href: destination.safe ? destination.href : null,
84
+ href: resolveDestination(link.href, index),
74
85
  target: link.target
75
86
  });
76
87
  });
77
88
  return resolved;
78
89
  }
79
90
  function NavigationLinkList({ links, $content, className, ...props }) {
80
- const resolved = resolveLinks($content?.links ?? links ?? []);
91
+ const resolved = resolveLinks(readLinks($content?.links, links));
81
92
  if (resolved.length === 0) return null;
82
93
  return /* @__PURE__ */ jsx("ul", {
83
94
  ...props,
@@ -1 +1 @@
1
- {"version":3,"file":"navigation-link-list.js","names":[],"sources":["../../../src/particles/navigation-link-list/navigation-link-list.tsx"],"sourcesContent":["import { type ContentValue, defineComponent, defineContentType } from '@vtex/fs-core';\nimport type { ComponentPropsWithRef } from 'react';\nimport { resolveSafeHref } from '../../utils/safe-href.js';\nimport { NavigationLink } from '../navigation-link/navigation-link.js';\n\n// ---------------------------------------------------------------------------\n// Content model\n// ---------------------------------------------------------------------------\n\nexport const NavigationLinkListContent = defineContentType({\n id: 'navigation-link-list',\n title: 'Navigation Links',\n description: 'The ordered list of navigation links, editable without a deployment.',\n schema: {\n type: 'object',\n additionalProperties: false,\n // `links` is not required, for the reason FooterLinkGroup records: a missing element key already\n // omits the component, so requiring it would only cost the prop fallback its shape validity.\n properties: {\n links: {\n type: 'array',\n title: 'Links',\n minItems: 1,\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['label', 'href'],\n properties: {\n label: { type: 'string', minLength: 1, title: 'Label' },\n href: {\n type: 'string',\n minLength: 1,\n title: 'URL',\n format: 'uri-reference',\n // Author-time guidance only, and lookahead-free so the Admin's RE2-family engine takes it.\n pattern: '^(?:https?://.*|mailto:.+|tel:.+|/(?:[^/].*)?)$',\n },\n target: { type: 'string', enum: ['_self', '_blank'], default: '_self', title: 'Open in' },\n },\n },\n },\n },\n },\n});\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype NavigationLinkListContentValue = ContentValue<typeof NavigationLinkListContent>;\n\n/** One authored link. Derived from the model so the prop and content channels cannot drift. */\nexport type NavigationLinkListLink = NonNullable<NavigationLinkListContentValue['links']>[number];\n\nexport type NavigationLinkListProps = ComponentPropsWithRef<'ul'> & {\n /** Authored fallback for the link list, used when no CMS entry supplies one. */\n links?: readonly NavigationLinkListLink[];\n /** Injected by the compiler, sliced from the page's or layout's composite content entry. */\n $content?: NavigationLinkListContentValue;\n};\n\n// ---------------------------------------------------------------------------\n// Definition\n// ---------------------------------------------------------------------------\n\nexport const NavigationLinkListDefinition = defineComponent({\n name: 'NavigationLinkList',\n importPath: '@vtex/faststore/components',\n component: NavigationLinkList,\n content: NavigationLinkListContent,\n editor: { label: 'Navigation Links', category: 'navigation' },\n schema: {\n links: { type: 'array', label: 'Links' },\n },\n guideline:\n 'The list of navigation links as one CMS-editable collection, so a merchant adds, removes and reorders them without a deployment. Owns its own <ul>, so compose it directly under NavigationMenuRoot for the desktop bar and inside DrawerPopup for the mobile panel — reference the same element id from both and one entry feeds both surfaces. See navigation-link-list.md.',\n});\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/** A resolved link, ready to render. A null `href` is a destination the allowlist refused. */\ntype ResolvedLink = {\n label: string;\n href: string | null;\n target: NavigationLinkListLink['target'];\n};\n\nfunction resolveLinks(links: readonly NavigationLinkListLink[]): ResolvedLink[] {\n const resolved: ResolvedLink[] = [];\n\n links.forEach((link, index) => {\n // `minLength` is enforced by neither the runtime slice nor the preflight, so a blank label\n // reaches the component. Drop the link: an anchor with no accessible name is a WCAG failure.\n if (link.label.trim().length === 0) {\n return;\n }\n const destination = resolveSafeHref(link.href);\n if (!destination.safe) {\n console.warn('[NavigationLinkList] link destination rejected', { index, reason: destination.reason });\n }\n resolved.push({\n label: link.label,\n href: destination.safe ? destination.href : null,\n target: link.target,\n });\n });\n\n return resolved;\n}\n\nexport function NavigationLinkList({ links, $content, className, ...props }: NavigationLinkListProps) {\n // An entry supplying `links: []` beats an authored prop: a merchant who cleared the list cleared it.\n // `minItems` guides the Admin form only, so that empty list still reaches this component.\n const resolved = resolveLinks($content?.links ?? links ?? []);\n\n if (resolved.length === 0) {\n return null;\n }\n\n return (\n <ul {...props} className={className} data-fs-navigation-link-list=\"\">\n {resolved.map((link, index) => (\n <li key={index} data-fs-navigation-link-list-item=\"\">\n {link.href === null ? (\n <span data-fs-text=\"\" data-fs-navigation-link-list-text=\"\">\n {link.label}\n </span>\n ) : (\n // `target` reaches NavigationLink as a prop, which is where the tab-nabbing defense\n // lives: it derives rel from the resolved target, so `_blank` carries noopener/noreferrer.\n <NavigationLink href={link.href} target={link.target}>\n {link.label}\n </NavigationLink>\n )}\n </li>\n ))}\n </ul>\n );\n}\n"],"mappings":";;;;;;;AASA,MAAa,4BAA4B,kBAAkB;CACzD,IAAI;CACJ,OAAO;CACP,aAAa;CACb,QAAQ;EACN,MAAM;EACN,sBAAsB;EAGtB,YAAY,EACV,OAAO;GACL,MAAM;GACN,OAAO;GACP,UAAU;GACV,OAAO;IACL,MAAM;IACN,sBAAsB;IACtB,UAAU,CAAC,SAAS,MAAM;IAC1B,YAAY;KACV,OAAO;MAAE,MAAM;MAAU,WAAW;MAAG,OAAO;KAAQ;KACtD,MAAM;MACJ,MAAM;MACN,WAAW;MACX,OAAO;MACP,QAAQ;MAER,SAAS;KACX;KACA,QAAQ;MAAE,MAAM;MAAU,MAAM,CAAC,SAAS,QAAQ;MAAG,SAAS;MAAS,OAAO;KAAU;IAC1F;GACF;EACF,EACF;CACF;AACF,CAAC;AAsBD,MAAa,+BAA+B,gBAAgB;CAC1D,MAAM;CACN,YAAY;CACZ,WAAW;CACX,SAAS;CACT,QAAQ;EAAE,OAAO;EAAoB,UAAU;CAAa;CAC5D,QAAQ,EACN,OAAO;EAAE,MAAM;EAAS,OAAO;CAAQ,EACzC;CACA,WACE;AACJ,CAAC;AAaD,SAAS,aAAa,OAA0D;CAC9E,MAAM,WAA2B,CAAC;CAElC,MAAM,SAAS,MAAM,UAAU;EAG7B,IAAI,KAAK,MAAM,KAAK,CAAC,CAAC,WAAW,GAC/B;EAEF,MAAM,cAAc,gBAAgB,KAAK,IAAI;EAC7C,IAAI,CAAC,YAAY,MACf,QAAQ,KAAK,kDAAkD;GAAE;GAAO,QAAQ,YAAY;EAAO,CAAC;EAEtG,SAAS,KAAK;GACZ,OAAO,KAAK;GACZ,MAAM,YAAY,OAAO,YAAY,OAAO;GAC5C,QAAQ,KAAK;EACf,CAAC;CACH,CAAC;CAED,OAAO;AACT;AAEA,SAAgB,mBAAmB,EAAE,OAAO,UAAU,WAAW,GAAG,SAAkC;CAGpG,MAAM,WAAW,aAAa,UAAU,SAAS,SAAS,CAAC,CAAC;CAE5D,IAAI,SAAS,WAAW,GACtB,OAAO;CAGT,OACE,oBAAC,MAAD;EAAI,GAAI;EAAkB;EAAW,gCAA6B;EAC/D,UAAA,SAAS,KAAK,MAAM,UACnB,oBAAC,MAAD;GAAgB,qCAAkC;GAC/C,UAAA,KAAK,SAAS,OACb,oBAAC,QAAD;IAAM,gBAAa;IAAG,qCAAkC;IACrD,UAAA,KAAK;GACF,CAAA,IAIN,oBAAC,gBAAD;IAAgB,MAAM,KAAK;IAAM,QAAQ,KAAK;IAC3C,UAAA,KAAK;GACQ,CAAA;EAEhB,GAZK,KAYL,CACL;CACC,CAAA;AAER"}
1
+ {"version":3,"file":"navigation-link-list.js","names":[],"sources":["../../../src/particles/navigation-link-list/navigation-link-list.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 { NavigationLink } from '../navigation-link/navigation-link.js';\n\n// ---------------------------------------------------------------------------\n// Content model\n// ---------------------------------------------------------------------------\n\nexport const NavigationLinkListContent = defineContentType({\n id: 'navigation-link-list',\n title: 'Navigation Links',\n description: 'The ordered list of navigation links, editable without a deployment.',\n schema: {\n type: 'object',\n additionalProperties: false,\n // `links` is not required, for the reason FooterLinkGroup records: a missing element key already\n // omits the component, so requiring it would only cost the prop fallback its shape validity.\n properties: {\n links: {\n type: 'array',\n title: 'Links',\n minItems: 1,\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['label', 'href'],\n properties: {\n label: { type: 'string', minLength: 1, title: 'Label' },\n href: {\n type: 'string',\n minLength: 1,\n title: 'URL',\n format: 'uri-reference',\n // Author-time guidance only, and lookahead-free so the Admin's RE2-family engine takes it.\n pattern: '^(?:https?://.*|mailto:.+|tel:.+|/(?:[^/].*)?)$',\n },\n target: { type: 'string', enum: ['_self', '_blank'], default: '_self', title: 'Open in' },\n },\n },\n },\n },\n },\n});\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype NavigationLinkListContentValue = ContentValue<typeof NavigationLinkListContent>;\n\ntype NavigationLinkListContentLink = NonNullable<NavigationLinkListContentValue['links']>[number];\n\n/** One authored link. The prop accepts a page reference; a CMS href stays a string. */\nexport type NavigationLinkListLink = Omit<NavigationLinkListContentLink, 'href'> & {\n href: PageRef | string;\n};\n\nexport type NavigationLinkListProps = ComponentPropsWithRef<'ul'> & {\n /** Authored fallback for the link list, used when no CMS entry supplies one. */\n links?: readonly NavigationLinkListLink[];\n /** Injected by the compiler, sliced from the page's or layout's composite content entry. */\n $content?: NavigationLinkListContentValue;\n};\n\n// ---------------------------------------------------------------------------\n// Definition\n// ---------------------------------------------------------------------------\n\nexport const NavigationLinkListDefinition = defineComponent({\n name: 'NavigationLinkList',\n importPath: '@vtex/faststore/components',\n component: NavigationLinkList,\n content: NavigationLinkListContent,\n editor: { label: 'Navigation Links', category: 'navigation' },\n schema: {\n links: { type: 'array', label: 'Links' },\n },\n guideline:\n 'The list of navigation links as one CMS-editable collection, so a merchant adds, removes and reorders them without a deployment. Owns its own <ul>, so compose it directly under NavigationMenuRoot for the desktop bar and inside DrawerPopup for the mobile panel — reference the same element id from both and one entry feeds both surfaces. See navigation-link-list.md.',\n});\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/** A resolved link, ready to render. A null `href` is a destination the allowlist refused. */\ntype ResolvedLink = {\n label: string;\n href: PageRef | string | null;\n target: NavigationLinkListLink['target'];\n};\n\nfunction readLinks(\n content: readonly NavigationLinkListContentLink[] | undefined,\n authored: readonly NavigationLinkListLink[] | undefined,\n): readonly NavigationLinkListLink[] {\n if (content !== undefined) return content;\n return authored ?? [];\n}\n\nfunction resolveDestination(href: PageRef | string, index: number): PageRef | string | null {\n if (typeof href !== 'string') return href;\n\n const destination = resolveSafeHref(href);\n if (!destination.safe) {\n console.warn('[NavigationLinkList] link destination rejected', { index, reason: destination.reason });\n return null;\n }\n return destination.href;\n}\n\nfunction resolveLinks(links: readonly NavigationLinkListLink[]): ResolvedLink[] {\n const resolved: ResolvedLink[] = [];\n\n links.forEach((link, index) => {\n // `minLength` is enforced by neither the runtime slice nor the preflight, so a blank label\n // reaches the component. Drop the link: an anchor with no accessible name is a WCAG failure.\n if (link.label.trim().length === 0) {\n return;\n }\n resolved.push({\n label: link.label,\n href: resolveDestination(link.href, index),\n target: link.target,\n });\n });\n\n return resolved;\n}\n\nexport function NavigationLinkList({ links, $content, className, ...props }: NavigationLinkListProps) {\n // An entry supplying `links: []` beats an authored prop: a merchant who cleared the list cleared it.\n // `minItems` guides the Admin form only, so that empty list still reaches this component.\n const resolved = resolveLinks(readLinks($content?.links, links));\n\n if (resolved.length === 0) {\n return null;\n }\n\n return (\n <ul {...props} className={className} data-fs-navigation-link-list=\"\">\n {resolved.map((link, index) => (\n <li key={index} data-fs-navigation-link-list-item=\"\">\n {link.href === null ? (\n <span data-fs-text=\"\" data-fs-navigation-link-list-text=\"\">\n {link.label}\n </span>\n ) : (\n // `target` reaches NavigationLink as a prop, which is where the tab-nabbing defense\n // lives: it derives rel from the resolved target, so `_blank` carries noopener/noreferrer.\n <NavigationLink href={link.href} target={link.target}>\n {link.label}\n </NavigationLink>\n )}\n </li>\n ))}\n </ul>\n );\n}\n"],"mappings":";;;;;;;AAUA,MAAa,4BAA4B,kBAAkB;CACzD,IAAI;CACJ,OAAO;CACP,aAAa;CACb,QAAQ;EACN,MAAM;EACN,sBAAsB;EAGtB,YAAY,EACV,OAAO;GACL,MAAM;GACN,OAAO;GACP,UAAU;GACV,OAAO;IACL,MAAM;IACN,sBAAsB;IACtB,UAAU,CAAC,SAAS,MAAM;IAC1B,YAAY;KACV,OAAO;MAAE,MAAM;MAAU,WAAW;MAAG,OAAO;KAAQ;KACtD,MAAM;MACJ,MAAM;MACN,WAAW;MACX,OAAO;MACP,QAAQ;MAER,SAAS;KACX;KACA,QAAQ;MAAE,MAAM;MAAU,MAAM,CAAC,SAAS,QAAQ;MAAG,SAAS;MAAS,OAAO;KAAU;IAC1F;GACF;EACF,EACF;CACF;AACF,CAAC;AA0BD,MAAa,+BAA+B,gBAAgB;CAC1D,MAAM;CACN,YAAY;CACZ,WAAW;CACX,SAAS;CACT,QAAQ;EAAE,OAAO;EAAoB,UAAU;CAAa;CAC5D,QAAQ,EACN,OAAO;EAAE,MAAM;EAAS,OAAO;CAAQ,EACzC;CACA,WACE;AACJ,CAAC;AAaD,SAAS,UACP,SACA,UACmC;CACnC,IAAI,YAAY,KAAA,GAAW,OAAO;CAClC,OAAO,YAAY,CAAC;AACtB;AAEA,SAAS,mBAAmB,MAAwB,OAAwC;CAC1F,IAAI,OAAO,SAAS,UAAU,OAAO;CAErC,MAAM,cAAc,gBAAgB,IAAI;CACxC,IAAI,CAAC,YAAY,MAAM;EACrB,QAAQ,KAAK,kDAAkD;GAAE;GAAO,QAAQ,YAAY;EAAO,CAAC;EACpG,OAAO;CACT;CACA,OAAO,YAAY;AACrB;AAEA,SAAS,aAAa,OAA0D;CAC9E,MAAM,WAA2B,CAAC;CAElC,MAAM,SAAS,MAAM,UAAU;EAG7B,IAAI,KAAK,MAAM,KAAK,CAAC,CAAC,WAAW,GAC/B;EAEF,SAAS,KAAK;GACZ,OAAO,KAAK;GACZ,MAAM,mBAAmB,KAAK,MAAM,KAAK;GACzC,QAAQ,KAAK;EACf,CAAC;CACH,CAAC;CAED,OAAO;AACT;AAEA,SAAgB,mBAAmB,EAAE,OAAO,UAAU,WAAW,GAAG,SAAkC;CAGpG,MAAM,WAAW,aAAa,UAAU,UAAU,OAAO,KAAK,CAAC;CAE/D,IAAI,SAAS,WAAW,GACtB,OAAO;CAGT,OACE,oBAAC,MAAD;EAAI,GAAI;EAAkB;EAAW,gCAA6B;EAC/D,UAAA,SAAS,KAAK,MAAM,UACnB,oBAAC,MAAD;GAAgB,qCAAkC;GAC/C,UAAA,KAAK,SAAS,OACb,oBAAC,QAAD;IAAM,gBAAa;IAAG,qCAAkC;IACrD,UAAA,KAAK;GACF,CAAA,IAIN,oBAAC,gBAAD;IAAgB,MAAM,KAAK;IAAM,QAAQ,KAAK;IAC3C,UAAA,KAAK;GACQ,CAAA;EAEhB,GAZK,KAYL,CACL;CACC,CAAA;AAER"}
@@ -0,0 +1,28 @@
1
+ import { ContentValue } from "../../core/dist/define-content-type.js";
2
+ import { ComponentDefinition } from "../../core/dist/define-component.js";
3
+ import "../../core/dist/index.js";
4
+ import { ComponentPropsWithRef } from "react";
5
+ //#region src/particles/order-details-back/order-details-back.d.ts
6
+ export declare const OrderDetailsBackContent: {
7
+ readonly id: "order-details-back";
8
+ readonly title: "Order Details Back";
9
+ readonly description: "The link back to the order list.";
10
+ readonly schema: {
11
+ readonly type: "object";
12
+ readonly additionalProperties: false;
13
+ readonly properties: {
14
+ readonly label: {
15
+ readonly type: "string";
16
+ readonly title: "Label";
17
+ };
18
+ };
19
+ };
20
+ };
21
+ export type OrderDetailsBackContentValue = ContentValue<typeof OrderDetailsBackContent>;
22
+ export type OrderDetailsBackProps = ComponentPropsWithRef<'a'> & {
23
+ $content?: OrderDetailsBackContentValue;
24
+ };
25
+ export declare const OrderDetailsBackDefinition: ComponentDefinition<OrderDetailsBackProps>;
26
+ export declare function OrderDetailsBack({ className, ref, $content, ...props }: OrderDetailsBackProps): import("react/jsx-runtime").JSX.Element;
27
+ //#endregion
28
+ //# sourceMappingURL=order-details-back.d.ts.map
@@ -0,0 +1,52 @@
1
+ "use client";
2
+ import { defineComponent } from "../../core/dist/define-component.js";
3
+ import { defineContentType } from "../../core/dist/define-content-type.js";
4
+ import "../../core/dist/index.js";
5
+ import { useIntl } from "../../sdk/i18n/client.js";
6
+ import { jsx } from "react/jsx-runtime";
7
+ //#region src/particles/order-details-back/order-details-back.tsx
8
+ const LIST_HREF = "/pvt/orders";
9
+ const OrderDetailsBackContent = defineContentType({
10
+ id: "order-details-back",
11
+ title: "Order Details Back",
12
+ description: "The link back to the order list.",
13
+ schema: {
14
+ type: "object",
15
+ additionalProperties: false,
16
+ properties: { label: {
17
+ type: "string",
18
+ title: "Label"
19
+ } }
20
+ }
21
+ });
22
+ const OrderDetailsBackDefinition = defineComponent({
23
+ name: "OrderDetailsBack",
24
+ importPath: "@vtex/faststore/components",
25
+ component: OrderDetailsBack,
26
+ content: OrderDetailsBackContent,
27
+ editor: {
28
+ label: "Order Details Back",
29
+ category: "action"
30
+ },
31
+ schema: {},
32
+ guideline: "The return from one order to the order list. Place it first on the order page when the layout has no account breadcrumb. The label is content; a blank field keeps the catalogue."
33
+ });
34
+ function authored(value, fallback) {
35
+ return value?.trim() || fallback;
36
+ }
37
+ function OrderDetailsBack({ className, ref, $content, ...props }) {
38
+ const intl = useIntl();
39
+ const label = authored($content?.label, intl.formatMessage({ id: "fs/order.back-to-orders" }));
40
+ return /* @__PURE__ */ jsx("a", {
41
+ ...props,
42
+ ref,
43
+ href: LIST_HREF,
44
+ className,
45
+ "data-fs-order-details-back": "",
46
+ children: label
47
+ });
48
+ }
49
+ //#endregion
50
+ export { OrderDetailsBack, OrderDetailsBackContent, OrderDetailsBackDefinition };
51
+
52
+ //# sourceMappingURL=order-details-back.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"order-details-back.js","names":[],"sources":["../../../src/particles/order-details-back/order-details-back.tsx"],"sourcesContent":["'use client';\n\nimport { type ContentValue, defineComponent, defineContentType } from '@vtex/fs-core';\nimport type { ComponentPropsWithRef } from 'react';\nimport { useIntl } from '../../sdk/i18n/client.js';\n\nconst LIST_HREF = '/pvt/orders';\n\nexport const OrderDetailsBackContent = defineContentType({\n id: 'order-details-back',\n title: 'Order Details Back',\n description: 'The link back to the order list.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n label: { type: 'string', title: 'Label' },\n },\n },\n});\n\nexport type OrderDetailsBackContentValue = ContentValue<typeof OrderDetailsBackContent>;\n\nexport type OrderDetailsBackProps = ComponentPropsWithRef<'a'> & {\n $content?: OrderDetailsBackContentValue;\n};\n\nexport const OrderDetailsBackDefinition = defineComponent<OrderDetailsBackProps>({\n name: 'OrderDetailsBack',\n importPath: '@vtex/faststore/components',\n component: OrderDetailsBack,\n content: OrderDetailsBackContent,\n editor: { label: 'Order Details Back', category: 'action' },\n schema: {},\n guideline:\n 'The return from one order to the order list. Place it first on the order page when the layout has no account breadcrumb. The label is content; a blank field keeps the catalogue.',\n});\n\nfunction authored(value: string | undefined, fallback: string): string {\n const trimmed = value?.trim();\n return trimmed || fallback;\n}\n\nexport function OrderDetailsBack({ className, ref, $content, ...props }: OrderDetailsBackProps) {\n const intl = useIntl();\n const label = authored($content?.label, intl.formatMessage({ id: 'fs/order.back-to-orders' }));\n\n return (\n <a {...props} ref={ref} href={LIST_HREF} className={className} data-fs-order-details-back=\"\">\n {label}\n </a>\n );\n}\n"],"mappings":";;;;;;;AAMA,MAAM,YAAY;AAElB,MAAa,0BAA0B,kBAAkB;CACvD,IAAI;CACJ,OAAO;CACP,aAAa;CACb,QAAQ;EACN,MAAM;EACN,sBAAsB;EACtB,YAAY,EACV,OAAO;GAAE,MAAM;GAAU,OAAO;EAAQ,EAC1C;CACF;AACF,CAAC;AAQD,MAAa,6BAA6B,gBAAuC;CAC/E,MAAM;CACN,YAAY;CACZ,WAAW;CACX,SAAS;CACT,QAAQ;EAAE,OAAO;EAAsB,UAAU;CAAS;CAC1D,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,SAAS,SAAS,OAA2B,UAA0B;CAErE,OADgB,OAAO,KAAK,KACV;AACpB;AAEA,SAAgB,iBAAiB,EAAE,WAAW,KAAK,UAAU,GAAG,SAAgC;CAC9F,MAAM,OAAO,QAAQ;CACrB,MAAM,QAAQ,SAAS,UAAU,OAAO,KAAK,cAAc,EAAE,IAAI,0BAA0B,CAAC,CAAC;CAE7F,OACE,oBAAC,KAAD;EAAG,GAAI;EAAY;EAAK,MAAM;EAAsB;EAAW,8BAA2B;EACvF,UAAA;CACA,CAAA;AAEP"}
@@ -0,0 +1,19 @@
1
+ import { ComponentDefinition } from "../../core/dist/define-component.js";
2
+ import "../../core/dist/index.js";
3
+ import { ComponentPropsWithRef } from "react";
4
+ //#region src/particles/order-status-badge/order-status-badge.d.ts
5
+ export type OrderStatusState = 'ongoing' | 'completed' | 'cancelled';
6
+ export type OrderStatusTone = 'neutral' | 'progress' | 'success' | 'danger';
7
+ export type OrderStatusMessageKey = 'cancelled' | 'cancellation-requested' | 'in-progress' | 'delivered' | 'shipped' | 'ready-for-pickup' | 'collected' | 'placeholder';
8
+ /** Per-label sentences. A blank value keeps the catalogue sentence for that label. */
9
+ export type OrderStatusMessages = Partial<Record<OrderStatusMessageKey, string>>;
10
+ export type OrderStatusBadgeProps = Omit<ComponentPropsWithRef<'span'>, 'children'> & {
11
+ state: OrderStatusState;
12
+ tone: OrderStatusTone;
13
+ label: string;
14
+ messages?: OrderStatusMessages;
15
+ };
16
+ export declare const OrderStatusBadgeDefinition: ComponentDefinition<OrderStatusBadgeProps>;
17
+ export declare function OrderStatusBadge({ state, tone, label, messages, className, ...props }: OrderStatusBadgeProps): import("react/jsx-runtime").JSX.Element;
18
+ //#endregion
19
+ //# sourceMappingURL=order-status-badge.d.ts.map
@@ -0,0 +1,50 @@
1
+ "use client";
2
+ import { defineComponent } from "../../core/dist/define-component.js";
3
+ import "../../core/dist/index.js";
4
+ import { useIntl } from "../../sdk/i18n/client.js";
5
+ import { Badge } from "../badge/badge.js";
6
+ import { jsx } from "react/jsx-runtime";
7
+ //#region src/particles/order-status-badge/order-status-badge.tsx
8
+ const ORDER_STATUS_MESSAGES = {
9
+ cancelled: "fs/order-status.cancelled",
10
+ "cancellation-requested": "fs/order-status.cancellation-requested",
11
+ "in-progress": "fs/order-status.in-progress",
12
+ delivered: "fs/order-status.delivered",
13
+ shipped: "fs/order-status.shipped",
14
+ "ready-for-pickup": "fs/order-status.ready-for-pickup",
15
+ collected: "fs/order-status.collected"
16
+ };
17
+ function orderStatusMessageId(label) {
18
+ if (!Object.hasOwn(ORDER_STATUS_MESSAGES, label)) return "fs/order-status.placeholder";
19
+ return ORDER_STATUS_MESSAGES[label] ?? "fs/order-status.placeholder";
20
+ }
21
+ const OrderStatusBadgeDefinition = defineComponent({
22
+ name: "OrderStatusBadge",
23
+ importPath: "@vtex/faststore/components",
24
+ component: OrderStatusBadge,
25
+ editor: {
26
+ label: "Order Status Badge",
27
+ category: "content"
28
+ },
29
+ schema: {},
30
+ guideline: "Shopper-facing order status. Pass the completion view, the label id, and a tone. A messages entry replaces the catalogue sentence for that label; a blank entry and an unknown label keep the catalogue."
31
+ });
32
+ function messageKey(label) {
33
+ return Object.hasOwn(ORDER_STATUS_MESSAGES, label) ? label : "placeholder";
34
+ }
35
+ function OrderStatusBadge({ state, tone, label, messages, className, ...props }) {
36
+ const intl = useIntl();
37
+ const authored = messages?.[messageKey(label)]?.trim();
38
+ return /* @__PURE__ */ jsx(Badge, {
39
+ ...props,
40
+ className,
41
+ "data-fs-order-status-badge": "",
42
+ "data-fs-order-status-badge-state": state,
43
+ "data-fs-order-status-badge-tone": tone,
44
+ children: authored || intl.formatMessage({ id: orderStatusMessageId(label) })
45
+ });
46
+ }
47
+ //#endregion
48
+ export { OrderStatusBadge, OrderStatusBadgeDefinition };
49
+
50
+ //# sourceMappingURL=order-status-badge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"order-status-badge.js","names":[],"sources":["../../../src/particles/order-status-badge/order-status-badge.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport type { ComponentPropsWithRef } from 'react';\nimport { useIntl } from '../../sdk/i18n/client.js';\nimport type { MessageId } from '../../sdk/i18n/locales.js';\nimport { Badge } from '../badge/badge.js';\n\nexport type OrderStatusState = 'ongoing' | 'completed' | 'cancelled';\n\nexport type OrderStatusTone = 'neutral' | 'progress' | 'success' | 'danger';\n\nexport type OrderStatusMessageKey =\n | 'cancelled'\n | 'cancellation-requested'\n | 'in-progress'\n | 'delivered'\n | 'shipped'\n | 'ready-for-pickup'\n | 'collected'\n | 'placeholder';\n\n/** Per-label sentences. A blank value keeps the catalogue sentence for that label. */\nexport type OrderStatusMessages = Partial<Record<OrderStatusMessageKey, string>>;\n\nexport type OrderStatusBadgeProps = Omit<ComponentPropsWithRef<'span'>, 'children'> & {\n state: OrderStatusState;\n tone: OrderStatusTone;\n label: string;\n messages?: OrderStatusMessages;\n};\n\nconst ORDER_STATUS_MESSAGES: Record<string, MessageId> = {\n cancelled: 'fs/order-status.cancelled',\n 'cancellation-requested': 'fs/order-status.cancellation-requested',\n 'in-progress': 'fs/order-status.in-progress',\n delivered: 'fs/order-status.delivered',\n shipped: 'fs/order-status.shipped',\n 'ready-for-pickup': 'fs/order-status.ready-for-pickup',\n collected: 'fs/order-status.collected',\n};\n\nfunction orderStatusMessageId(label: string): MessageId {\n // Bracket access walks Object.prototype, so \"constructor\" would skip the placeholder.\n if (!Object.hasOwn(ORDER_STATUS_MESSAGES, label)) {\n return 'fs/order-status.placeholder';\n }\n\n return ORDER_STATUS_MESSAGES[label] ?? 'fs/order-status.placeholder';\n}\n\nexport const OrderStatusBadgeDefinition = defineComponent<OrderStatusBadgeProps>({\n name: 'OrderStatusBadge',\n importPath: '@vtex/faststore/components',\n component: OrderStatusBadge,\n editor: { label: 'Order Status Badge', category: 'content' },\n schema: {},\n guideline:\n 'Shopper-facing order status. Pass the completion view, the label id, and a tone. A messages entry replaces the catalogue sentence for that label; a blank entry and an unknown label keep the catalogue.',\n});\n\nfunction messageKey(label: string): OrderStatusMessageKey {\n return Object.hasOwn(ORDER_STATUS_MESSAGES, label) ? (label as OrderStatusMessageKey) : 'placeholder';\n}\n\nexport function OrderStatusBadge({ state, tone, label, messages, className, ...props }: OrderStatusBadgeProps) {\n const intl = useIntl();\n const authored = messages?.[messageKey(label)]?.trim();\n\n return (\n <Badge\n {...props}\n className={className}\n data-fs-order-status-badge=\"\"\n data-fs-order-status-badge-state={state}\n data-fs-order-status-badge-tone={tone}\n >\n {authored || intl.formatMessage({ id: orderStatusMessageId(label) })}\n </Badge>\n );\n}\n"],"mappings":";;;;;;;AAgCA,MAAM,wBAAmD;CACvD,WAAW;CACX,0BAA0B;CAC1B,eAAe;CACf,WAAW;CACX,SAAS;CACT,oBAAoB;CACpB,WAAW;AACb;AAEA,SAAS,qBAAqB,OAA0B;CAEtD,IAAI,CAAC,OAAO,OAAO,uBAAuB,KAAK,GAC7C,OAAO;CAGT,OAAO,sBAAsB,UAAU;AACzC;AAEA,MAAa,6BAA6B,gBAAuC;CAC/E,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAsB,UAAU;CAAU;CAC3D,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,SAAS,WAAW,OAAsC;CACxD,OAAO,OAAO,OAAO,uBAAuB,KAAK,IAAK,QAAkC;AAC1F;AAEA,SAAgB,iBAAiB,EAAE,OAAO,MAAM,OAAO,UAAU,WAAW,GAAG,SAAgC;CAC7G,MAAM,OAAO,QAAQ;CACrB,MAAM,WAAW,WAAW,WAAW,KAAK,EAAE,EAAE,KAAK;CAErD,OACE,oBAAC,OAAD;EACE,GAAI;EACO;EACX,8BAA2B;EAC3B,oCAAkC;EAClC,mCAAiC;EAEhC,UAAA,YAAY,KAAK,cAAc,EAAE,IAAI,qBAAqB,KAAK,EAAE,CAAC;CAC9D,CAAA;AAEX"}
@@ -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
  */