@vtex/faststore 0.0.5 → 0.0.6

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 (232) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/actions.json +181 -0
  3. package/dist/catalog.json +1543 -495
  4. package/dist/client.d.ts +2 -1
  5. package/dist/client.js +2 -1
  6. package/dist/components.d.ts +28 -7
  7. package/dist/components.js +31 -12
  8. package/dist/docs/access-code-sign-in.md +1 -1
  9. package/dist/docs/address-summary.md +24 -3
  10. package/dist/docs/alert-dialog.md +204 -0
  11. package/dist/docs/billing-address.md +284 -0
  12. package/dist/docs/card-entry.md +250 -0
  13. package/dist/docs/cardholder-document.md +142 -0
  14. package/dist/docs/client-preferences-fieldset.md +148 -0
  15. package/dist/docs/client-preferences.md +2 -1
  16. package/dist/docs/client-profile-fieldset.md +184 -0
  17. package/dist/docs/client-profile.md +1 -1
  18. package/dist/docs/document-field.md +58 -4
  19. package/dist/docs/field.md +317 -0
  20. package/dist/docs/form.md +307 -0
  21. package/dist/docs/identity-edit.md +6 -6
  22. package/dist/docs/identity-summary.md +5 -5
  23. package/dist/docs/installments.md +248 -0
  24. package/dist/docs/my-account-profile.md +1 -0
  25. package/dist/docs/order-placed-payment-summary.md +138 -0
  26. package/dist/docs/payment-method-badges.md +232 -0
  27. package/dist/docs/payment-methods.md +1 -0
  28. package/dist/docs/payment.md +218 -0
  29. package/dist/docs/phone-field.md +58 -4
  30. package/dist/docs/place-order.md +157 -0
  31. package/dist/docs/save-card.md +186 -0
  32. package/dist/docs/saved-cards.md +218 -0
  33. package/dist/docs/select.md +465 -0
  34. package/dist/docs/shipping-address.md +110 -258
  35. package/dist/index.d.ts +4 -1
  36. package/dist/internal/client.d.ts +3 -1
  37. package/dist/internal/client.js +3 -1
  38. package/dist/matter/access-code-sign-in/access-code-sign-in.js +9 -8
  39. package/dist/matter/access-code-sign-in/access-code-sign-in.js.map +1 -1
  40. package/dist/matter/billing-address/billing-address.d.ts +165 -0
  41. package/dist/matter/billing-address/billing-address.js +408 -0
  42. package/dist/matter/billing-address/billing-address.js.map +1 -0
  43. package/dist/matter/card-entry/card-capture.d.ts +25 -0
  44. package/dist/matter/card-entry/card-capture.js +25 -0
  45. package/dist/matter/card-entry/card-capture.js.map +1 -0
  46. package/dist/matter/card-entry/card-entry.d.ts +120 -0
  47. package/dist/matter/card-entry/card-entry.js +832 -0
  48. package/dist/matter/card-entry/card-entry.js.map +1 -0
  49. package/dist/matter/cardholder-document/cardholder-document.d.ts +45 -0
  50. package/dist/matter/cardholder-document/cardholder-document.js +70 -0
  51. package/dist/matter/cardholder-document/cardholder-document.js.map +1 -0
  52. package/dist/matter/client-preferences/client-preferences-fieldset.d.ts +77 -0
  53. package/dist/matter/client-preferences/client-preferences-fieldset.js +299 -0
  54. package/dist/matter/client-preferences/client-preferences-fieldset.js.map +1 -0
  55. package/dist/matter/client-preferences/client-preferences.d.ts +3 -5
  56. package/dist/matter/client-preferences/client-preferences.js +6 -6
  57. package/dist/matter/client-preferences/client-preferences.js.map +1 -1
  58. package/dist/matter/client-profile/client-profile.d.ts +4 -6
  59. package/dist/matter/client-profile/client-profile.js +9 -6
  60. package/dist/matter/client-profile/client-profile.js.map +1 -1
  61. package/dist/matter/client-profile-form/client-profile-fieldset.d.ts +119 -0
  62. package/dist/matter/client-profile-form/client-profile-fieldset.js +488 -0
  63. package/dist/matter/client-profile-form/client-profile-fieldset.js.map +1 -0
  64. package/dist/matter/identity-edit/identity-edit.js +2 -2
  65. package/dist/matter/identity-edit/identity-edit.js.map +1 -1
  66. package/dist/matter/identity-summary/identity-summary.js +2 -2
  67. package/dist/matter/identity-summary/identity-summary.js.map +1 -1
  68. package/dist/matter/installments/installments-select.d.ts +42 -0
  69. package/dist/matter/installments/installments-select.js +167 -0
  70. package/dist/matter/installments/installments-select.js.map +1 -0
  71. package/dist/matter/installments/installments.d.ts +58 -0
  72. package/dist/matter/installments/installments.js +176 -0
  73. package/dist/matter/installments/installments.js.map +1 -0
  74. package/dist/matter/installments/use-installments-selection.d.ts +29 -0
  75. package/dist/matter/installments/use-installments-selection.js +95 -0
  76. package/dist/matter/installments/use-installments-selection.js.map +1 -0
  77. package/dist/matter/my-account-profile/my-account-profile.js +4 -4
  78. package/dist/matter/my-account-profile/my-account-profile.js.map +1 -1
  79. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.d.ts +63 -0
  80. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js +305 -0
  81. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js.map +1 -0
  82. package/dist/matter/payment/payment-card-capture.d.ts +19 -0
  83. package/dist/matter/payment/payment-card-capture.js +129 -0
  84. package/dist/matter/payment/payment-card-capture.js.map +1 -0
  85. package/dist/matter/payment/payment-controller.d.ts +24 -0
  86. package/dist/matter/payment/payment-controller.js +107 -0
  87. package/dist/matter/payment/payment-controller.js.map +1 -0
  88. package/dist/matter/payment/payment-method-badges.d.ts +57 -0
  89. package/dist/matter/payment/payment-method-badges.js +192 -0
  90. package/dist/matter/payment/payment-method-badges.js.map +1 -0
  91. package/dist/matter/payment/payment.d.ts +89 -0
  92. package/dist/matter/payment/payment.js +263 -0
  93. package/dist/matter/payment/payment.js.map +1 -0
  94. package/dist/matter/payment/use-payment-method-selection.d.ts +302 -0
  95. package/dist/matter/payment/use-payment-method-selection.js +458 -0
  96. package/dist/matter/payment/use-payment-method-selection.js.map +1 -0
  97. package/dist/matter/payment/use-payment-submit-gate.d.ts +111 -0
  98. package/dist/matter/payment/use-payment-submit-gate.js +144 -0
  99. package/dist/matter/payment/use-payment-submit-gate.js.map +1 -0
  100. package/dist/matter/place-order/place-order.d.ts +74 -0
  101. package/dist/matter/place-order/place-order.js +379 -0
  102. package/dist/matter/place-order/place-order.js.map +1 -0
  103. package/dist/matter/product-list/product-list.d.ts +1 -0
  104. package/dist/matter/product-list/product-list.js.map +1 -1
  105. package/dist/matter/product-shelf/product-shelf.d.ts +1 -0
  106. package/dist/matter/product-shelf/product-shelf.js +2 -0
  107. package/dist/matter/product-shelf/product-shelf.js.map +1 -1
  108. package/dist/matter/region/region.js +1 -1
  109. package/dist/matter/region/region.js.map +1 -1
  110. package/dist/matter/save-card/save-card.d.ts +49 -0
  111. package/dist/matter/save-card/save-card.js +188 -0
  112. package/dist/matter/save-card/save-card.js.map +1 -0
  113. package/dist/matter/saved-cards/saved-cards.d.ts +79 -0
  114. package/dist/matter/saved-cards/saved-cards.js +387 -0
  115. package/dist/matter/saved-cards/saved-cards.js.map +1 -0
  116. package/dist/matter/shipping-address/country-descriptors.js +20 -10
  117. package/dist/matter/shipping-address/country-descriptors.js.map +1 -1
  118. package/dist/matter/shipping-address/shipping-address.d.ts +113 -174
  119. package/dist/matter/shipping-address/shipping-address.js +480 -791
  120. package/dist/matter/shipping-address/shipping-address.js.map +1 -1
  121. package/dist/matter/variant-selector/variant-selector.js +1 -1
  122. package/dist/particles/address-summary/address-summary.d.ts +24 -0
  123. package/dist/particles/address-summary/address-summary.js +83 -2
  124. package/dist/particles/address-summary/address-summary.js.map +1 -1
  125. package/dist/particles/alert-dialog/alert-dialog.d.ts +68 -0
  126. package/dist/particles/alert-dialog/alert-dialog.js +187 -0
  127. package/dist/particles/alert-dialog/alert-dialog.js.map +1 -0
  128. package/dist/particles/checkbox/checkbox.d.ts +7 -3
  129. package/dist/particles/checkbox/checkbox.js +4 -1
  130. package/dist/particles/checkbox/checkbox.js.map +1 -1
  131. package/dist/particles/document-field/document-field.d.ts +48 -5
  132. package/dist/particles/document-field/document-field.js +189 -32
  133. package/dist/particles/document-field/document-field.js.map +1 -1
  134. package/dist/particles/field/bound-field-registry.js +52 -0
  135. package/dist/particles/field/bound-field-registry.js.map +1 -0
  136. package/dist/particles/field/field.d.ts +116 -0
  137. package/dist/particles/field/field.js +497 -6
  138. package/dist/particles/field/field.js.map +1 -1
  139. package/dist/particles/fieldset/fieldset.js +2 -2
  140. package/dist/particles/fieldset/fieldset.js.map +1 -1
  141. package/dist/particles/form/form.d.ts +104 -0
  142. package/dist/particles/form/form.js +485 -0
  143. package/dist/particles/form/form.js.map +1 -0
  144. package/dist/particles/phone-field/phone-field.d.ts +38 -10
  145. package/dist/particles/phone-field/phone-field.js +183 -39
  146. package/dist/particles/phone-field/phone-field.js.map +1 -1
  147. package/dist/particles/postal-code-field/postal-code-field.js +2 -2
  148. package/dist/particles/postal-code-field/postal-code-field.js.map +1 -1
  149. package/dist/particles/select/field-select.d.ts +43 -0
  150. package/dist/particles/select/field-select.js +150 -0
  151. package/dist/particles/select/field-select.js.map +1 -0
  152. package/dist/particles/select/select.d.ts +2 -0
  153. package/dist/particles/select/select.js.map +1 -1
  154. package/dist/particles/shared/compose-refs.js +21 -0
  155. package/dist/particles/shared/compose-refs.js.map +1 -0
  156. package/dist/schema.graphql +701 -2
  157. package/dist/sdk/actions/analytics-action-provider.d.ts +15 -0
  158. package/dist/sdk/actions/analytics-action-provider.js +19 -0
  159. package/dist/sdk/actions/analytics-action-provider.js.map +1 -0
  160. package/dist/sdk/actions/index.d.ts +3 -1
  161. package/dist/sdk/actions/index.js +3 -1
  162. package/dist/sdk/actions/payment-action-provider.d.ts +22 -0
  163. package/dist/sdk/actions/payment-action-provider.js +26 -0
  164. package/dist/sdk/actions/payment-action-provider.js.map +1 -0
  165. package/dist/sdk/actions/types.d.ts +6 -2
  166. package/dist/sdk/analytics/analytics-destination.d.ts +43 -0
  167. package/dist/sdk/analytics/analytics-destination.js +90 -0
  168. package/dist/sdk/analytics/analytics-destination.js.map +1 -0
  169. package/dist/sdk/analytics/use-analytics-actions.d.ts +57 -0
  170. package/dist/sdk/analytics/use-analytics-actions.js +108 -0
  171. package/dist/sdk/analytics/use-analytics-actions.js.map +1 -0
  172. package/dist/sdk/cart/use-cart-actions.js +1 -1
  173. package/dist/sdk/data/entity-resolution-context.js +31 -0
  174. package/dist/sdk/data/entity-resolution-context.js.map +1 -0
  175. package/dist/sdk/data/entity-subscriber.js +18 -8
  176. package/dist/sdk/data/entity-subscriber.js.map +1 -1
  177. package/dist/sdk/data/index.js +1 -0
  178. package/dist/sdk/data/page-reactive-data-root.d.ts +36 -0
  179. package/dist/sdk/data/page-reactive-data-root.js +4 -2
  180. package/dist/sdk/data/page-reactive-data-root.js.map +1 -1
  181. package/dist/sdk/data/reactive-data-provider.js +7 -3
  182. package/dist/sdk/data/reactive-data-provider.js.map +1 -1
  183. package/dist/sdk/data/reactive-list-item-data-provider.js +12 -4
  184. package/dist/sdk/data/reactive-list-item-data-provider.js.map +1 -1
  185. package/dist/sdk/data/use-reactive-data-settled.js +39 -0
  186. package/dist/sdk/data/use-reactive-data-settled.js.map +1 -0
  187. package/dist/sdk/data/use-reactive-entity.js +5 -1
  188. package/dist/sdk/data/use-reactive-entity.js.map +1 -1
  189. package/dist/sdk/generated/fragment-types.js +2 -0
  190. package/dist/sdk/generated/fragment-types.js.map +1 -1
  191. package/dist/sdk/generated/graphql.js.map +1 -1
  192. package/dist/sdk/i18n/messages/en-US.js +3 -0
  193. package/dist/sdk/i18n/messages/en-US.json +3 -0
  194. package/dist/sdk/i18n/messages/en-US.json.d.ts +3 -0
  195. package/dist/sdk/i18n/messages/es-ES.js +3 -0
  196. package/dist/sdk/i18n/messages/es-ES.json +3 -0
  197. package/dist/sdk/i18n/messages/pt-BR.js +3 -0
  198. package/dist/sdk/i18n/messages/pt-BR.json +3 -0
  199. package/dist/sdk/identity/use-identity-actions.js +3 -2
  200. package/dist/sdk/identity/use-identity-actions.js.map +1 -1
  201. package/dist/sdk/payment/gateway-origin.js +35 -0
  202. package/dist/sdk/payment/gateway-origin.js.map +1 -0
  203. package/dist/sdk/payment/gateway.d.ts +85 -0
  204. package/dist/sdk/payment/gateway.js +183 -0
  205. package/dist/sdk/payment/gateway.js.map +1 -0
  206. package/dist/sdk/payment/operations.d.ts +52 -0
  207. package/dist/sdk/payment/operations.js +223 -0
  208. package/dist/sdk/payment/operations.js.map +1 -0
  209. package/dist/sdk/payment/security-code-mask.js +71 -0
  210. package/dist/sdk/payment/security-code-mask.js.map +1 -0
  211. package/dist/sdk/payment/submission-registry.d.ts +33 -0
  212. package/dist/sdk/payment/submission-registry.js +58 -0
  213. package/dist/sdk/payment/submission-registry.js.map +1 -0
  214. package/dist/sdk/payment/use-payment-actions.d.ts +78 -0
  215. package/dist/sdk/payment/use-payment-actions.js +276 -0
  216. package/dist/sdk/payment/use-payment-actions.js.map +1 -0
  217. package/dist/sdk/payment/use-place-order.d.ts +65 -0
  218. package/dist/sdk/payment/use-place-order.js +640 -0
  219. package/dist/sdk/payment/use-place-order.js.map +1 -0
  220. package/dist/sdk/shipping/use-shipping-actions.js +1 -1
  221. package/dist/utils/loggable-error-name.js +49 -0
  222. package/dist/utils/loggable-error-name.js.map +1 -0
  223. package/dist/utils/resolve-authored-copy.js +18 -0
  224. package/dist/utils/resolve-authored-copy.js.map +1 -0
  225. package/dist/utils/substitute-template.js +33 -0
  226. package/dist/utils/substitute-template.js.map +1 -0
  227. package/package.json +2 -1
  228. package/src/styles/tailwind.css +34 -0
  229. package/dist/docs/client-profile-form.md +0 -192
  230. package/dist/matter/client-profile-form/client-profile-form.d.ts +0 -126
  231. package/dist/matter/client-profile-form/client-profile-form.js +0 -662
  232. package/dist/matter/client-profile-form/client-profile-form.js.map +0 -1
@@ -1,23 +1,37 @@
1
1
  "use client";
2
2
  import { defineComponent } from "../../core/dist/define-component.js";
3
3
  import "../../core/dist/index.js";
4
- import { Field } from "../../particles/field/field.js";
4
+ import { Field, FieldBinding, FieldsetFieldContext, useFieldsetField, useFieldsetFieldNames, useNoAncestorFieldset } from "../../particles/field/field.js";
5
5
  import { useActionsOverride } from "../../sdk/actions/actions-override-context.js";
6
6
  import { useDataOverride } from "../../sdk/data/data-override-context.js";
7
- import { Select } from "../../particles/select/select.js";
8
- import { AddressSummary, AddressSummaryRoot } from "../../particles/address-summary/address-summary.js";
9
- import "../../sdk/shipping/shipping-operations.js";
7
+ import { Fieldset } from "../../particles/fieldset/fieldset.js";
10
8
  import { getAvailableCountries, getCountryDescriptor } from "./country-descriptors.js";
11
- import { cloneElement, createContext, isValidElement, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
9
+ import { useFormRegistry } from "../../particles/form/form.js";
10
+ import { useEntityDataResolved } from "../../sdk/data/entity-resolution-context.js";
11
+ import { useReactiveDataSettled } from "../../sdk/data/use-reactive-data-settled.js";
12
+ import { FieldSelect } from "../../particles/select/field-select.js";
13
+ import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
12
14
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
15
+ import { useWatch } from "react-hook-form";
13
16
  //#region src/matter/shipping-address/shipping-address.tsx
14
17
  /**
15
18
  * Shipping address step (T2 — Enter and save the delivery address).
16
19
  * Single-file matter per ADR-0001; batches form state and writes once via shipping.setAddress.
17
20
  * Controls are thin context-bound components that read/write draft state (docs/storefront-core/reference-cart.md § Delivery).
18
21
  */
19
- const fragment = `
20
- fragment ShippingAddressData on Query {
22
+ /**
23
+ * The fieldset's own fragment. Drops `savedAddresses` — the fieldset has no saved-address UI of
24
+ * its own (Decision 1 of specs/shipping-address-fieldset.md).
25
+ *
26
+ * `clientProfile` selects `id` even though nothing renders it. The reactive runtime resolves each
27
+ * root field to a cache reference through `cache.identify`, which needs `__typename` and `id`; with
28
+ * no `id` in the composed page query the profile never normalises, `useReactiveEntity` keeps
29
+ * returning the server's `fallbackData`, and a page composing this fieldset without
30
+ * `ClientProfileFieldset` would show a logged-in shopper a blank recipient name instead of the one
31
+ * US-4 fills in from their profile.
32
+ */
33
+ const fieldsetFragment = `
34
+ fragment ShippingAddressFieldsetData on Query {
21
35
  shipping {
22
36
  __typename
23
37
  id
@@ -34,370 +48,346 @@ const fragment = `
34
48
  city
35
49
  state
36
50
  }
37
- savedAddresses {
38
- __typename
39
- id
40
- recipientName
41
- postalCode
42
- country
43
- street
44
- number
45
- complement
46
- neighborhood
47
- city
48
- state
49
- }
50
51
  }
51
52
  clientProfile {
52
53
  __typename
54
+ id
53
55
  firstName
54
56
  lastName
55
57
  }
56
58
  }
57
59
  `;
58
- const SHIPPING_ADDRESS_CONTEXT_UNSET = Symbol("ShippingAddressContextUnset");
59
- const ShippingAddressContext = createContext(SHIPPING_ADDRESS_CONTEXT_UNSET);
60
- function useShippingAddressContext() {
61
- const ctx = useContext(ShippingAddressContext);
62
- if (ctx === SHIPPING_ADDRESS_CONTEXT_UNSET) throw new Error("ShippingAddress components must be used inside ShippingAddressRoot.");
60
+ /** The react-hook-form namespace every field of this entity registers under. */
61
+ const SHIPPING_ADDRESS_NAMESPACE = "shippingAddress";
62
+ const ShippingAddressFieldsetContext = createContext(null);
63
+ /**
64
+ * Read before any field hook (Decision 4): a control calls this before looking up its own
65
+ * descriptor entry, so a field the active descriptor does not declare never calls
66
+ * `useFieldsetField`/`useController` and so never registers with react-hook-form at all. Throws
67
+ * when rendered with no `ShippingAddressFieldset` above it (Decision 2), naming the required
68
+ * composition.
69
+ */
70
+ function useShippingAddressFieldsetContext() {
71
+ const ctx = useContext(ShippingAddressFieldsetContext);
72
+ if (!ctx) throw new Error("ShippingAddress*Control components must be used inside ShippingAddressFieldset.");
63
73
  return ctx;
64
74
  }
65
- const SHIPPING_ADDRESS_DRAWER_CONTEXT_UNSET = Symbol("ShippingAddressDrawerContextUnset");
66
- const ShippingAddressDrawerContext = createContext(SHIPPING_ADDRESS_DRAWER_CONTEXT_UNSET);
67
- function useShippingAddressDrawerContext() {
68
- const ctx = useContext(ShippingAddressDrawerContext);
69
- if (ctx === SHIPPING_ADDRESS_DRAWER_CONTEXT_UNSET) throw new Error("ShippingAddressSavedAddressDrawerController and ShippingAddressSavedAddressSubmit must be used inside ShippingAddressRoot.");
70
- return ctx;
75
+ /**
76
+ * Rows `ShippingAddressFieldsetSkeleton` draws when nobody says otherwise: one per Control in the
77
+ * full anatomy, the nine of them. It is a default and not a derivation — the skeleton renders before
78
+ * any address has resolved, so it cannot read the live descriptor, and the composed Controls are
79
+ * opaque children. A page that composes fewer than nine sets `skeletonCount` to what it composed,
80
+ * or the form jumps by the difference when the Controls take the skeleton's place.
81
+ */
82
+ const SHIPPING_ADDRESS_SKELETON_ROWS = 9;
83
+ /** `autoComplete` tokens per field (US-3, WCAG 2.1 SC 1.3.5). `number` has no standard token. */
84
+ const FIELD_AUTOCOMPLETE = {
85
+ recipientName: "name",
86
+ postalCode: "postal-code",
87
+ country: "country",
88
+ street: "address-line1",
89
+ number: void 0,
90
+ complement: "address-line2",
91
+ neighborhood: "address-level3",
92
+ city: "address-level2",
93
+ state: "address-level1"
94
+ };
95
+ /**
96
+ * Merges the descriptor's own `rule` under a generated `required`, which is this fieldset's whole
97
+ * declaration of the baseline. The blank check it used to hand-write alongside lives in
98
+ * `resolveRequiredness` now, installed for every bound control rather than only for this one.
99
+ */
100
+ function buildRule(field, label) {
101
+ if (!field.required) return field.rule;
102
+ return {
103
+ ...field.rule,
104
+ required: `${label} é obrigatório`
105
+ };
71
106
  }
72
- const SHIPPING_ADDRESS_SECTION_CONTEXT_UNSET = Symbol("ShippingAddressSectionContextUnset");
73
- const ShippingAddressSectionContext = createContext(SHIPPING_ADDRESS_SECTION_CONTEXT_UNSET);
74
- function useShippingAddressSectionContext() {
75
- const ctx = useContext(ShippingAddressSectionContext);
76
- if (ctx === SHIPPING_ADDRESS_SECTION_CONTEXT_UNSET) throw new Error("ShippingAddressSummaryView, ShippingAddressEditForm, ShippingAddressEditButton and ShippingAddressCancelButton must be used inside ShippingAddressEditController.");
77
- return ctx;
107
+ /** Keeps only the AddressDraft keys `values` actually defines — a field no control composed is absent, not `undefined`. */
108
+ function pickDefinedAddressFields(values) {
109
+ const result = {};
110
+ for (const key of Object.keys(values)) {
111
+ const value = values[key];
112
+ if (value !== void 0) result[key] = value;
113
+ }
114
+ return result;
78
115
  }
79
- const ShippingAddressRootDefinition = defineComponent({
80
- name: "ShippingAddressRoot",
116
+ const ShippingAddressFieldsetDefinition = defineComponent({
117
+ name: "ShippingAddressFieldset",
81
118
  importPath: "@vtex/faststore/components",
82
- component: ShippingAddressRoot,
83
- fragment,
119
+ component: ShippingAddressFieldset,
120
+ fragment: fieldsetFragment,
84
121
  fetchStrategy: "reactive",
85
- providesContext: "shipping-address",
122
+ skeleton: ShippingAddressFieldsetSkeleton,
86
123
  actions: { shipping: ["setAddress"] },
124
+ requiresContext: "form",
125
+ providesContext: "shipping-address",
87
126
  editor: {
88
- label: "Shipping Address Root",
89
- category: "content"
127
+ label: "Shipping Address Fieldset",
128
+ category: "commerce"
90
129
  },
91
- schema: { defaultCountry: {
92
- type: "string",
93
- default: "BRA",
94
- label: "Default country (ISO-3)"
95
- } },
96
- guideline: "Country-adaptive single-address delivery form; batches all fields and writes once via shipping.setAddress. See shipping-address.md."
130
+ schema: {
131
+ defaultCountry: {
132
+ type: "string",
133
+ default: "BRA",
134
+ label: "Default country (ISO-3)"
135
+ },
136
+ skeletonCount: {
137
+ type: "number",
138
+ default: 9,
139
+ label: "Skeleton rows"
140
+ },
141
+ loadingLabel: {
142
+ type: "string",
143
+ default: "Loading your delivery address",
144
+ label: "Loading announcement"
145
+ },
146
+ loadedLabel: {
147
+ type: "string",
148
+ default: "Your delivery address is ready",
149
+ label: "Loaded announcement"
150
+ },
151
+ groupLabel: {
152
+ type: "string",
153
+ default: "Your delivery address",
154
+ label: "Accessible group name"
155
+ }
156
+ },
157
+ guideline: "Composable delivery-address entity: registers under \"shippingAddress\" with the enclosing FormRoot and dispatches shipping.setAddress only when the shopper changed something. Compose the nine ShippingAddress*Control parts inside it; place FormSubmit/FormEnterSubmit/FormError as siblings of the fieldset, not inside it — this fieldset renders no submit or error part of its own. The Controls mount only once the address has resolved, with skeletonCount placeholder rows in their place until then — set it to the number of Controls composed, or the form jumps by the difference. Style the announcement sr-only. groupLabel names the group as its accessible name; blank emits no aria-label, and a composed FieldsetLegend wins over it. See shipping-address.md."
97
158
  });
159
+ const ADDRESS_TEXT_CONTROL_GUIDELINE_SUFFIX = "Must be a descendant of ShippingAddressFieldset, which provides the shipping-address context this control requires. Recomposable: compose Field.Label, Field.Control and Field.Error as children, in any order, to reorder or extend the anatomy; a childless composition keeps the default label-control-error order.";
98
160
  const ShippingAddressRecipientNameControlDefinition = defineComponent({
99
161
  name: "ShippingAddressRecipientNameControl",
100
162
  importPath: "@vtex/faststore/components",
101
163
  component: ShippingAddressRecipientNameControl,
102
164
  requiresContext: "shipping-address",
165
+ providesContext: "field",
103
166
  editor: {
104
167
  label: "Recipient Name Control",
105
168
  category: "action"
106
169
  },
107
- schema: {},
108
- guideline: "Recipient-name input field; required for submit. Must be a descendant of ShippingAddressRoot."
170
+ schema: {
171
+ label: {
172
+ type: "string",
173
+ label: "Field label"
174
+ },
175
+ placeholder: {
176
+ type: "string",
177
+ label: "Placeholder"
178
+ }
179
+ },
180
+ guideline: `Recipient-name input field; required for submit. ${ADDRESS_TEXT_CONTROL_GUIDELINE_SUFFIX}`
109
181
  });
110
182
  const ShippingAddressStreetControlDefinition = defineComponent({
111
183
  name: "ShippingAddressStreetControl",
112
184
  importPath: "@vtex/faststore/components",
113
185
  component: ShippingAddressStreetControl,
114
186
  requiresContext: "shipping-address",
187
+ providesContext: "field",
115
188
  editor: {
116
189
  label: "Street Control",
117
190
  category: "action"
118
191
  },
119
- schema: {},
120
- guideline: "Street name input field. Must be a descendant of ShippingAddressRoot."
192
+ schema: {
193
+ label: {
194
+ type: "string",
195
+ label: "Field label"
196
+ },
197
+ placeholder: {
198
+ type: "string",
199
+ label: "Placeholder"
200
+ }
201
+ },
202
+ guideline: `Street name input field. ${ADDRESS_TEXT_CONTROL_GUIDELINE_SUFFIX}`
121
203
  });
122
204
  const ShippingAddressNumberControlDefinition = defineComponent({
123
205
  name: "ShippingAddressNumberControl",
124
206
  importPath: "@vtex/faststore/components",
125
207
  component: ShippingAddressNumberControl,
126
208
  requiresContext: "shipping-address",
209
+ providesContext: "field",
127
210
  editor: {
128
211
  label: "Number Control",
129
212
  category: "action"
130
213
  },
131
- schema: {},
132
- guideline: "Street number input field. Must be a descendant of ShippingAddressRoot."
214
+ schema: {
215
+ label: {
216
+ type: "string",
217
+ label: "Field label"
218
+ },
219
+ placeholder: {
220
+ type: "string",
221
+ label: "Placeholder"
222
+ }
223
+ },
224
+ guideline: `Street number input field. ${ADDRESS_TEXT_CONTROL_GUIDELINE_SUFFIX}`
133
225
  });
134
226
  const ShippingAddressCityControlDefinition = defineComponent({
135
227
  name: "ShippingAddressCityControl",
136
228
  importPath: "@vtex/faststore/components",
137
229
  component: ShippingAddressCityControl,
138
230
  requiresContext: "shipping-address",
231
+ providesContext: "field",
139
232
  editor: {
140
233
  label: "City Control",
141
234
  category: "action"
142
235
  },
143
- schema: {},
144
- guideline: "City name input field. Must be a descendant of ShippingAddressRoot."
236
+ schema: {
237
+ label: {
238
+ type: "string",
239
+ label: "Field label"
240
+ },
241
+ placeholder: {
242
+ type: "string",
243
+ label: "Placeholder"
244
+ }
245
+ },
246
+ guideline: `City name input field. ${ADDRESS_TEXT_CONTROL_GUIDELINE_SUFFIX}`
145
247
  });
146
248
  const ShippingAddressStateControlDefinition = defineComponent({
147
249
  name: "ShippingAddressStateControl",
148
250
  importPath: "@vtex/faststore/components",
149
251
  component: ShippingAddressStateControl,
150
252
  requiresContext: "shipping-address",
253
+ providesContext: "field",
151
254
  editor: {
152
255
  label: "State Control",
153
256
  category: "action"
154
257
  },
155
- schema: {},
156
- guideline: "State/province input field. Must be a descendant of ShippingAddressRoot."
258
+ schema: {
259
+ label: {
260
+ type: "string",
261
+ label: "Field label"
262
+ },
263
+ placeholder: {
264
+ type: "string",
265
+ label: "Placeholder"
266
+ }
267
+ },
268
+ guideline: `State/province input field. ${ADDRESS_TEXT_CONTROL_GUIDELINE_SUFFIX}`
157
269
  });
158
270
  const ShippingAddressPostalCodeControlDefinition = defineComponent({
159
271
  name: "ShippingAddressPostalCodeControl",
160
272
  importPath: "@vtex/faststore/components",
161
273
  component: ShippingAddressPostalCodeControl,
162
274
  requiresContext: "shipping-address",
275
+ providesContext: "field",
163
276
  editor: {
164
277
  label: "Postal Code Control",
165
278
  category: "action"
166
279
  },
167
- schema: {},
168
- guideline: "Postal code input field. Must be a descendant of ShippingAddressRoot."
280
+ schema: {
281
+ label: {
282
+ type: "string",
283
+ label: "Field label"
284
+ },
285
+ placeholder: {
286
+ type: "string",
287
+ label: "Placeholder"
288
+ }
289
+ },
290
+ guideline: `Postal code input field. ${ADDRESS_TEXT_CONTROL_GUIDELINE_SUFFIX}`
169
291
  });
170
292
  const ShippingAddressComplementControlDefinition = defineComponent({
171
293
  name: "ShippingAddressComplementControl",
172
294
  importPath: "@vtex/faststore/components",
173
295
  component: ShippingAddressComplementControl,
174
296
  requiresContext: "shipping-address",
297
+ providesContext: "field",
175
298
  editor: {
176
299
  label: "Complement Control",
177
300
  category: "action"
178
301
  },
179
- schema: {},
180
- guideline: "Complement (apartment, suite, etc.) input field. Must be a descendant of ShippingAddressRoot."
181
- });
182
- const ShippingAddressNeighborhoodControlDefinition = defineComponent({
183
- name: "ShippingAddressNeighborhoodControl",
184
- importPath: "@vtex/faststore/components",
185
- component: ShippingAddressNeighborhoodControl,
186
- requiresContext: "shipping-address",
187
- editor: {
188
- label: "Neighborhood Control",
189
- category: "action"
190
- },
191
- schema: {},
192
- guideline: "Neighborhood (bairro) input field; shown only for countries that have this field (BR in v1). Must be a descendant of ShippingAddressRoot."
193
- });
194
- const ShippingAddressCountryControlDefinition = defineComponent({
195
- name: "ShippingAddressCountryControl",
196
- importPath: "@vtex/faststore/components",
197
- component: ShippingAddressCountryControl,
198
- requiresContext: "shipping-address",
199
- editor: {
200
- label: "Country Control",
201
- category: "action"
202
- },
203
- schema: { label: {
204
- type: "string",
205
- default: "país",
206
- label: "Field label"
207
- } },
208
- guideline: "Country selector; lists only countries with an implemented descriptor. Must be a descendant of ShippingAddressRoot."
209
- });
210
- const ShippingAddressSavedListDefinition = defineComponent({
211
- name: "ShippingAddressSavedList",
212
- importPath: "@vtex/faststore/components",
213
- component: ShippingAddressSavedList,
214
- requiresContext: "shipping-address",
215
- editor: {
216
- label: "Saved Address List",
217
- category: "commerce"
218
- },
219
- schema: {},
220
- guideline: "List of saved addresses for logged-in shoppers; renders only if savedAddresses is non-empty. Must be a descendant of ShippingAddressRoot."
221
- });
222
- const ShippingAddressSavedOptionDefinition = defineComponent({
223
- name: "ShippingAddressSavedOption",
224
- importPath: "@vtex/faststore/components",
225
- component: ShippingAddressSavedOption,
226
- requiresContext: "shipping-address",
227
- editor: {
228
- label: "Saved Address Option",
229
- category: "commerce"
230
- },
231
- schema: { id: {
232
- type: "string",
233
- label: "Saved address ID"
234
- } },
235
- guideline: "A single saved address option; renders its address via the same AddressSummary particle (through its own AddressSummaryRoot) used by ShippingAddressSelectedSummary, so both share one formatting implementation. Must be a descendant of ShippingAddressSavedList."
236
- });
237
- const ShippingAddressSubmitDefinition = defineComponent({
238
- name: "ShippingAddressSubmit",
239
- importPath: "@vtex/faststore/components",
240
- component: ShippingAddressSubmit,
241
- requiresContext: "shipping-address",
242
- editor: {
243
- label: "Shipping Address Submit",
244
- category: "action"
245
- },
246
302
  schema: {
247
303
  label: {
248
304
  type: "string",
249
- default: "salvar endereço",
250
- label: "Button label"
305
+ label: "Field label"
251
306
  },
252
- pendingLabel: {
307
+ placeholder: {
253
308
  type: "string",
254
- default: "salvando...",
255
- label: "Pending label"
309
+ label: "Placeholder"
256
310
  }
257
311
  },
258
- guideline: "Submit button; dispatches the batched address write. Disabled until recipient name is non-blank. Must be a descendant of ShippingAddressRoot."
312
+ guideline: `Complement (apartment, suite, etc.) input field. ${ADDRESS_TEXT_CONTROL_GUIDELINE_SUFFIX}`
259
313
  });
260
- const ShippingAddressErrorDefinition = defineComponent({
261
- name: "ShippingAddressError",
262
- importPath: "@vtex/faststore/components",
263
- component: ShippingAddressError,
264
- requiresContext: "shipping-address",
265
- editor: {
266
- label: "Shipping Address Error",
267
- category: "commerce"
268
- },
269
- schema: {},
270
- guideline: "Error banner; shows domain validation errors inline or a generic upstream-error state. Must be a descendant of ShippingAddressRoot."
271
- });
272
- const ShippingAddressSavedAddressDrawerControllerDefinition = defineComponent({
273
- name: "ShippingAddressSavedAddressDrawerController",
274
- importPath: "@vtex/faststore/components",
275
- component: ShippingAddressSavedAddressDrawerController,
276
- requiresContext: "shipping-address",
277
- editor: {
278
- label: "Saved Address Drawer Controller",
279
- category: "action"
280
- },
281
- schema: {},
282
- guideline: "Wraps a single DrawerRoot child (the saved-address selection drawer) and makes it controlled by the shipping-address drawer state: open state and blocked-dismissal-while-submitting are handled here, so do not set raw open/onOpenChange on the wrapped DrawerRoot. Compose as the parent of DrawerRoot, with the drawer trigger and popup as DrawerRoot descendants, same pattern as CartOpenOnAdd wrapping the mini-cart DrawerRoot. Must be inside ShippingAddressRoot."
283
- });
284
- const ShippingAddressSavedAddressSubmitDefinition = defineComponent({
285
- name: "ShippingAddressSavedAddressSubmit",
314
+ const ShippingAddressNeighborhoodControlDefinition = defineComponent({
315
+ name: "ShippingAddressNeighborhoodControl",
286
316
  importPath: "@vtex/faststore/components",
287
- component: ShippingAddressSavedAddressSubmit,
317
+ component: ShippingAddressNeighborhoodControl,
288
318
  requiresContext: "shipping-address",
319
+ providesContext: "field",
289
320
  editor: {
290
- label: "Saved Address Submit",
321
+ label: "Neighborhood Control",
291
322
  category: "action"
292
323
  },
293
324
  schema: {
294
325
  label: {
295
326
  type: "string",
296
- default: "selecionar",
297
- label: "Button label"
327
+ label: "Field label"
298
328
  },
299
- pendingLabel: {
329
+ placeholder: {
300
330
  type: "string",
301
- default: "selecionando...",
302
- label: "Pending label"
331
+ label: "Placeholder"
303
332
  }
304
333
  },
305
- guideline: "The drawer's \"selecionar\" submit button; independent submitting state from the main form's Submit (Decision 7); no-ops when the selected saved address is already the current one (Decision 8). Must be inside the saved-address drawer's DrawerPopup and inside ShippingAddressRoot."
334
+ guideline: `Neighborhood (bairro) input field; shown only for countries that have this field (BR in v1). ${ADDRESS_TEXT_CONTROL_GUIDELINE_SUFFIX}`
306
335
  });
307
- const ShippingAddressSelectedSummaryDefinition = defineComponent({
308
- name: "ShippingAddressSelectedSummary",
309
- importPath: "@vtex/faststore/components",
310
- component: ShippingAddressSelectedSummary,
311
- requiresContext: "shipping-address",
312
- providesContext: "address-summary",
313
- editor: {
314
- label: "Selected Address Summary",
315
- category: "commerce"
316
- },
317
- schema: {},
318
- guideline: "Provides the current delivery address to the generic AddressSummary particle via AddressSummaryRoot context, and renders the default AddressSummary when composed with no children. Compose AddressSummaryRecipient / Street / Locality directly to choose the order or drop a line. Self-gates to null when there is no current address yet. Must be a descendant of ShippingAddressRoot."
319
- });
320
- const ShippingAddressEditControllerDefinition = defineComponent({
321
- name: "ShippingAddressEditController",
336
+ const ShippingAddressCountryControlDefinition = defineComponent({
337
+ name: "ShippingAddressCountryControl",
322
338
  importPath: "@vtex/faststore/components",
323
- component: ShippingAddressEditController,
339
+ component: ShippingAddressCountryControl,
324
340
  requiresContext: "shipping-address",
325
- providesContext: "shipping-address-section",
326
- editor: {
327
- label: "Shipping Address Edit Controller",
328
- category: "commerce"
329
- },
330
- schema: {},
331
- guideline: "Owns the view/edit toggle for the delivery address section: defaults to view when a current address exists, edit otherwise; switches back to view automatically whenever setAddress succeeds (main form submit or saved-address drawer submit), without ShippingAddressSubmit/ShippingAddressSavedAddressSubmit needing to know about this mode. Compose a ShippingAddressSummaryView holding SelectedSummary + EditButton and a ShippingAddressEditForm holding the form Controls/Submit/Error. Self-contained: it decides from its own data and the shopper's own presses, so no other section opens it and finishing it opens nothing. Must be inside ShippingAddressRoot."
332
- });
333
- const ShippingAddressSummaryViewDefinition = defineComponent({
334
- name: "ShippingAddressSummaryView",
335
- importPath: "@vtex/faststore/components",
336
- component: ShippingAddressSummaryView,
337
- requiresContext: "shipping-address-section",
338
- editor: {
339
- label: "Shipping Address Summary View",
340
- category: "commerce"
341
- },
342
- schema: {},
343
- guideline: "Renders its children only while the delivery section shows the saved-address summary, and nothing while it shows the form. Compose SelectedSummary and a ShippingAddressEditButton inside. Takes no props: which state it gates is which component it is. Must be inside a ShippingAddressEditController."
344
- });
345
- const ShippingAddressEditFormDefinition = defineComponent({
346
- name: "ShippingAddressEditForm",
347
- importPath: "@vtex/faststore/components",
348
- component: ShippingAddressEditForm,
349
- requiresContext: "shipping-address-section",
350
- editor: {
351
- label: "Shipping Address Edit Form",
352
- category: "commerce"
353
- },
354
- schema: {},
355
- guideline: "Renders its children only while the delivery section shows the address form, and nothing while it shows the summary. Compose the Controls, Submit and Error parts inside. Must be inside a ShippingAddressEditController."
356
- });
357
- const ShippingAddressEditButtonDefinition = defineComponent({
358
- name: "ShippingAddressEditButton",
359
- importPath: "@vtex/faststore/components",
360
- component: ShippingAddressEditButton,
361
- requiresContext: "shipping-address-section",
362
- editor: {
363
- label: "Shipping Address Edit Button",
364
- category: "action"
365
- },
366
- schema: { label: {
367
- type: "string",
368
- default: "editar",
369
- label: "Button label"
370
- } },
371
- guideline: "CTA that switches the view/edit mode to edit. Typically placed inside a ShippingAddressSummaryView, next to SelectedSummary. Must be inside ShippingAddressEditController."
372
- });
373
- const ShippingAddressCancelButtonDefinition = defineComponent({
374
- name: "ShippingAddressCancelButton",
375
- importPath: "@vtex/faststore/components",
376
- component: ShippingAddressCancelButton,
377
- requiresContext: "shipping-address-section",
341
+ providesContext: "field",
378
342
  editor: {
379
- label: "Shipping Address Cancel Button",
343
+ label: "Country Control",
380
344
  category: "action"
381
345
  },
382
346
  schema: { label: {
383
347
  type: "string",
384
- default: "cancelar",
385
- label: "Button label"
348
+ label: "Field label"
386
349
  } },
387
- guideline: "Returns the delivery section to its summary without saving. Place it inside a ShippingAddressEditForm so a shopper who opened the form has a way back that does not depend on a successful write. Must be inside ShippingAddressEditController."
350
+ guideline: "Country selector; lists only countries with an implemented descriptor. Must be a descendant of ShippingAddressFieldset, which provides the shipping-address context this control requires. Recomposable: compose Field.Label, FieldSelect.Root and Field.Error as children, in any order; a childless composition keeps the default label-select-error order."
388
351
  });
389
352
  /**
390
- * ShippingAddressRoot owns the batched form state, manages the current descriptor,
391
- * and provides context to child Controls. It handles submit dispatch and error tracking.
353
+ * ShippingAddressFieldset — this entity's root: same data, same action as a page-level submit
354
+ * would use, dispatched at form-submit time instead of through a button of its own. Registers one
355
+ * `FieldsetRegistration` with the enclosing `FormRoot` under the `shippingAddress` namespace,
356
+ * derives the active country descriptor from react-hook-form (Decision 2), and publishes each
357
+ * field's seed/rule/label through its own context so a bound Control never writes a validation
358
+ * expression. Renders no button and holds no draft of its own.
359
+ *
360
+ * Its controls mount only once the entity has resolved; until then the `Fieldset.Root` carries
361
+ * `-pending` and renders `ShippingAddressFieldsetSkeleton`. "Resolved" is two conditions, because
362
+ * `$data` alone cannot answer it: under the reactive runtime the slice is defined from the first
363
+ * render — `useReactiveEntity` hands down `fallbackData` while the cache read is incomplete — so an
364
+ * empty server answer looks exactly like a resolved one, and every control would mount seeded `''`
365
+ * before the shopper's stored address arrives. Each seed reaches react-hook-form as a field-level
366
+ * `defaultValue`; `useSeedAdoption` re-seeds a clean field with it on every render, but once the
367
+ * field turns dirty no later seed ever reaches it again (Decision 12). Mounting before the answer
368
+ * arrives would seed `''`: a shopper who starts typing before the real value lands makes the field
369
+ * dirty on that empty seed, permanently locking their saved address out of it, and even a shopper who
370
+ * does not type sees the field flash from `''` to the real data. `useReactiveDataSettled` supplies
371
+ * the missing half: the page query's own in-flight state, latched so it only ever goes pending →
372
+ * settled. The `$data === undefined` half still carries
373
+ * trees with no reactive root above them, where the slice arrives as a plain prop.
374
+ *
375
+ * Both ends of that wait are spoken: the fieldset carries `aria-busy` while it lasts and holds a
376
+ * polite live region that announces `loadingLabel` and then `loadedLabel`. Style the region
377
+ * `sr-only` — it is text meant for a screen reader, not for the page.
392
378
  */
393
- function ShippingAddressRoot({ defaultCountry = "BRA", className, children, $data: $dataProp, $actions: $actionsProp }) {
379
+ function ShippingAddressFieldset({ defaultCountry = "BRA", skeletonCount = SHIPPING_ADDRESS_SKELETON_ROWS, loadingLabel = "Loading your delivery address", loadedLabel = "Your delivery address is ready", groupLabel = "Your delivery address", className, children, $data: $dataProp, $actions: $actionsProp }) {
380
+ const { registerFieldset } = useFormRegistry();
381
+ useNoAncestorFieldset("ShippingAddressFieldset");
394
382
  const $data = useDataOverride($dataProp);
395
383
  const $actions = useActionsOverride($actionsProp);
384
+ const settled = useReactiveDataSettled();
385
+ const sliceResolved = useEntityDataResolved();
386
+ const resolved = settled && sliceResolved && $data !== void 0;
396
387
  const currentAddress = $data?.shipping?.address;
397
- const savedAddresses = $data?.shipping?.savedAddresses ?? [];
398
388
  const clientProfile = $data?.clientProfile;
399
389
  const joinedProfileName = [clientProfile?.firstName, clientProfile?.lastName].filter(Boolean).join(" ");
400
- const seedDraft = useCallback(() => ({
390
+ const seed = useMemo(() => ({
401
391
  recipientName: currentAddress?.recipientName ?? joinedProfileName,
402
392
  postalCode: currentAddress?.postalCode ?? "",
403
393
  country: currentAddress?.country ?? defaultCountry,
@@ -412,583 +402,306 @@ function ShippingAddressRoot({ defaultCountry = "BRA", className, children, $dat
412
402
  joinedProfileName,
413
403
  defaultCountry
414
404
  ]);
415
- const [draft, setDraft] = useState(seedDraft);
416
- const [submitting, setSubmitting] = useState(false);
417
- const [errors, setErrors] = useState([]);
418
- const [upstreamError, setUpstreamError] = useState(false);
419
- const [selectedSavedId, setSelectedSavedId] = useState();
420
- const [savedAddressDrawerOpen, setSavedAddressDrawerOpen] = useState(false);
421
- const [drawerSubmitting, setDrawerSubmitting] = useState(false);
422
- const [submitSuccessSignal, setSubmitSuccessSignal] = useState(0);
423
- const [touchedFields, setTouchedFields] = useState(() => /* @__PURE__ */ new Set());
424
- const discardDraft = useCallback(() => {
425
- setTouchedFields(/* @__PURE__ */ new Set());
426
- setDraft(seedDraft());
427
- setErrors([]);
428
- setUpstreamError(false);
429
- }, [seedDraft]);
430
- const currentAddressSnapshot = JSON.stringify(currentAddress ?? null);
431
- const clientProfileSnapshot = JSON.stringify(clientProfile ?? null);
432
- useEffect(() => {
433
- setDraft((prev) => {
434
- return {
435
- recipientName: touchedFields.has("recipientName") ? prev.recipientName : currentAddress?.recipientName ?? (joinedProfileName || prev.recipientName),
436
- postalCode: touchedFields.has("postalCode") ? prev.postalCode : currentAddress?.postalCode ?? prev.postalCode,
437
- country: touchedFields.has("country") ? prev.country : currentAddress?.country ?? prev.country,
438
- street: touchedFields.has("street") ? prev.street : currentAddress?.street ?? prev.street,
439
- number: touchedFields.has("number") ? prev.number : currentAddress?.number ?? prev.number,
440
- complement: touchedFields.has("complement") ? prev.complement : currentAddress?.complement ?? prev.complement,
441
- neighborhood: touchedFields.has("neighborhood") ? prev.neighborhood : currentAddress?.neighborhood ?? prev.neighborhood,
442
- city: touchedFields.has("city") ? prev.city : currentAddress?.city ?? prev.city,
443
- state: touchedFields.has("state") ? prev.state : currentAddress?.state ?? prev.state
444
- };
445
- });
446
- }, [
447
- currentAddressSnapshot,
448
- clientProfileSnapshot,
449
- touchedFields
405
+ const watchedCountry = useWatch({
406
+ name: `${SHIPPING_ADDRESS_NAMESPACE}.country`,
407
+ defaultValue: seed.country
408
+ });
409
+ const activeCountry = typeof watchedCountry === "string" && watchedCountry || seed.country;
410
+ const descriptor = getCountryDescriptor(activeCountry);
411
+ const fieldFor = useCallback((key) => descriptor?.fields.find((f) => f.name === key), [descriptor]);
412
+ const seedFor = useCallback((key) => seed[key], [seed]);
413
+ const ruleFor = useCallback((key, label) => {
414
+ const field = fieldFor(key);
415
+ return field ? buildRule(field, label ?? field.label) : void 0;
416
+ }, [fieldFor]);
417
+ const countryOptions = useMemo(() => getAvailableCountries().map((iso) => ({
418
+ value: iso,
419
+ label: getCountryDescriptor(iso)?.label ?? iso
420
+ })), []);
421
+ const fieldsetContextValue = useMemo(() => ({
422
+ namespace: SHIPPING_ADDRESS_NAMESPACE,
423
+ descriptor,
424
+ fieldFor,
425
+ seedFor,
426
+ ruleFor,
427
+ countryOptions
428
+ }), [
429
+ descriptor,
430
+ fieldFor,
431
+ seedFor,
432
+ ruleFor,
433
+ countryOptions
450
434
  ]);
451
- const currentAddressId = currentAddress?.id ?? void 0;
452
- const formattedCurrentAddress = useMemo(() => {
453
- if (!currentAddress) return void 0;
435
+ const { registry: fieldRegistryValue, getFieldNames } = useFieldsetFieldNames(SHIPPING_ADDRESS_NAMESPACE);
436
+ const toPayload = useCallback((values) => {
437
+ const merged = {
438
+ ...seed,
439
+ ...pickDefinedAddressFields(values)
440
+ };
441
+ const country = String(merged.country || seed.country);
442
+ const active = getCountryDescriptor(country);
443
+ const isActive = (key) => active?.fields.some((f) => f.name === key) ?? false;
454
444
  return {
455
- recipientName: currentAddress.recipientName ?? "",
456
- postalCode: currentAddress.postalCode ?? "",
457
- country: currentAddress.country ?? "",
458
- street: currentAddress.street ?? "",
459
- number: currentAddress.number ?? "",
460
- complement: currentAddress.complement ?? "",
461
- neighborhood: currentAddress.neighborhood ?? "",
462
- city: currentAddress.city ?? "",
463
- state: currentAddress.state ?? ""
445
+ country,
446
+ ...isActive("recipientName") ? { recipientName: String(merged.recipientName ?? "").trim() } : {},
447
+ ...isActive("postalCode") ? { postalCode: merged.postalCode } : {},
448
+ ...isActive("street") ? { street: merged.street } : {},
449
+ ...isActive("number") ? { number: merged.number } : {},
450
+ ...isActive("complement") ? { complement: merged.complement } : {},
451
+ ...isActive("neighborhood") ? { neighborhood: merged.neighborhood } : {},
452
+ ...isActive("city") ? { city: merged.city } : {},
453
+ ...isActive("state") ? { state: merged.state } : {}
464
454
  };
465
- }, [currentAddress]);
466
- const descriptor = getCountryDescriptor(draft.country);
467
- const mappedSavedAddresses = useMemo(() => savedAddresses.map((saved) => ({
468
- id: saved.id ?? "",
469
- address: {
470
- recipientName: saved.recipientName ?? "",
471
- postalCode: saved.postalCode ?? "",
472
- country: saved.country ?? "",
473
- street: saved.street ?? "",
474
- number: saved.number ?? "",
475
- complement: saved.complement ?? "",
476
- neighborhood: saved.neighborhood ?? "",
477
- city: saved.city ?? "",
478
- state: saved.state ?? ""
479
- }
480
- })), [savedAddresses]);
481
- const setDraftField = useCallback((field, value) => {
482
- setTouchedFields((prev) => prev.has(field) ? prev : new Set(prev).add(field));
483
- setDraft((prev) => ({
484
- ...prev,
485
- [field]: value
486
- }));
487
- }, []);
488
- const selectSavedAddress = useCallback((id) => {
489
- setSelectedSavedId(id);
490
- if (id) {
491
- const saved = mappedSavedAddresses.find((s) => s.id === id);
492
- if (saved?.address) {
493
- setTouchedFields((prev) => /* @__PURE__ */ new Set([...prev, ...Object.keys(saved.address)]));
494
- setDraft((prev) => ({
495
- ...prev,
496
- ...saved.address,
497
- recipientName: saved.address.recipientName || joinedProfileName
498
- }));
499
- }
500
- }
501
- }, [mappedSavedAddresses, joinedProfileName]);
502
- const isFormValid = draft.recipientName.trim().length > 0;
503
- const handleSubmit = useCallback(async () => {
504
- if (!isFormValid || submitting) return;
505
- if (!$actions?.shipping?.setAddress) return;
506
- setSubmitting(true);
507
- setErrors([]);
508
- setUpstreamError(false);
509
- try {
510
- const isFieldActive = (field) => descriptor?.fields.some((f) => f.name === field) ?? false;
511
- const addressFields = {
512
- recipientName: draft.recipientName.trim(),
513
- postalCode: draft.postalCode,
514
- country: draft.country,
515
- ...isFieldActive("street") ? { street: draft.street } : {},
516
- ...isFieldActive("number") ? { number: draft.number } : {},
517
- ...isFieldActive("complement") ? { complement: draft.complement } : {},
518
- ...isFieldActive("neighborhood") ? { neighborhood: draft.neighborhood } : {},
519
- ...isFieldActive("city") ? { city: draft.city } : {},
520
- ...isFieldActive("state") ? { state: draft.state } : {}
521
- };
522
- const input = selectedSavedId ? {
523
- savedAddressId: selectedSavedId,
524
- ...addressFields
525
- } : addressFields;
526
- const userErrors = await $actions.shipping.setAddress(input);
527
- if (userErrors.length === 0) {
528
- setErrors([]);
529
- setSubmitSuccessSignal((n) => n + 1);
530
- } else if (userErrors.some((e) => e.code === "NETWORK_ERROR")) {
531
- setErrors([]);
532
- setUpstreamError(true);
533
- } else setErrors(userErrors);
534
- } catch (error) {
535
- console.error("[ShippingAddress] setAddress failed:", error);
536
- setUpstreamError(true);
537
- setErrors([]);
538
- } finally {
539
- setSubmitting(false);
540
- }
541
- }, [
542
- isFormValid,
543
- submitting,
544
- $actions?.shipping?.setAddress,
545
- draft,
546
- selectedSavedId,
547
- descriptor
548
- ]);
549
- const handleDrawerSubmit = useCallback(async () => {
550
- if (!selectedSavedId) return;
551
- if (currentAddressId !== void 0 && selectedSavedId === currentAddressId) {
552
- setSavedAddressDrawerOpen(false);
553
- return;
554
- }
555
- const selectedAddress = mappedSavedAddresses.find((s) => s.id === selectedSavedId);
556
- if (!selectedAddress) return;
557
- if (!selectedAddress.address.recipientName?.trim()) {
558
- setSavedAddressDrawerOpen(false);
559
- return;
560
- }
561
- if (!$actions?.shipping?.setAddress) return;
562
- setDrawerSubmitting(true);
563
- setErrors([]);
564
- setUpstreamError(false);
565
- try {
566
- const input = {
567
- savedAddressId: selectedSavedId,
568
- recipientName: selectedAddress.address.recipientName || "",
569
- postalCode: selectedAddress.address.postalCode || "",
570
- country: selectedAddress.address.country || "",
571
- street: selectedAddress.address.street || "",
572
- number: selectedAddress.address.number || "",
573
- complement: selectedAddress.address.complement || "",
574
- neighborhood: selectedAddress.address.neighborhood || "",
575
- city: selectedAddress.address.city || "",
576
- state: selectedAddress.address.state || ""
577
- };
578
- const userErrors = await $actions.shipping.setAddress(input);
579
- if (userErrors.length === 0) {
580
- setErrors([]);
581
- setSavedAddressDrawerOpen(false);
582
- setSubmitSuccessSignal((n) => n + 1);
583
- } else if (userErrors.some((e) => e.code === "NETWORK_ERROR")) {
584
- setErrors([]);
585
- setUpstreamError(true);
586
- } else setErrors(userErrors);
587
- } catch (error) {
588
- console.error("[ShippingAddress] drawer submit failed:", error);
589
- setUpstreamError(true);
590
- setErrors([]);
591
- } finally {
592
- setDrawerSubmitting(false);
593
- }
594
- }, [
595
- selectedSavedId,
596
- currentAddressId,
597
- mappedSavedAddresses,
598
- $actions?.shipping?.setAddress
455
+ }, [seed]);
456
+ const dispatch = useCallback(async (payload) => {
457
+ if (!$actions?.shipping?.setAddress) throw new Error("ShippingAddressFieldset requires $actions.shipping.setAddress.");
458
+ return $actions.shipping.setAddress(payload);
459
+ }, [$actions]);
460
+ const registration = useMemo(() => ({
461
+ namespace: SHIPPING_ADDRESS_NAMESPACE,
462
+ getFieldNames,
463
+ dispatch,
464
+ toPayload,
465
+ label: "shipping-address"
466
+ }), [
467
+ getFieldNames,
468
+ dispatch,
469
+ toPayload
599
470
  ]);
600
- const contextValue = {
601
- draft,
602
- setDraftField,
603
- descriptor,
604
- savedAddresses: mappedSavedAddresses,
605
- selectedSavedId,
606
- setSelectedSavedId: selectSavedAddress,
607
- submitting,
608
- errors,
609
- upstreamError,
610
- handleSubmit,
611
- currentAddressId,
612
- currentAddress: formattedCurrentAddress,
613
- submitSuccessSignal,
614
- isFormValid,
615
- discardDraft
616
- };
617
- const drawerContextValue = {
618
- savedAddressDrawerOpen,
619
- setSavedAddressDrawerOpen,
620
- drawerSubmitting,
621
- handleDrawerSubmit
622
- };
623
- return /* @__PURE__ */ jsx(ShippingAddressContext.Provider, {
624
- value: contextValue,
625
- children: /* @__PURE__ */ jsx(ShippingAddressDrawerContext.Provider, {
626
- value: drawerContextValue,
627
- children: /* @__PURE__ */ jsx("div", {
628
- className,
629
- "data-fs-shipping-address": "",
630
- children
471
+ useEffect(() => registerFieldset(registration), [registerFieldset, registration]);
472
+ const [announcement, setAnnouncement] = useState("");
473
+ const announced = resolved ? loadedLabel : loadingLabel;
474
+ useEffect(() => {
475
+ setAnnouncement(announced);
476
+ }, [announced]);
477
+ return /* @__PURE__ */ jsxs(Fieldset.Root, {
478
+ className,
479
+ "aria-busy": resolved ? void 0 : true,
480
+ ...groupLabel?.trim() ? { "aria-label": groupLabel } : {},
481
+ "data-fs-shipping-address-fieldset": "",
482
+ ...resolved ? {} : { "data-fs-shipping-address-fieldset-pending": "" },
483
+ children: [/* @__PURE__ */ jsx(ShippingAddressFieldsetContext.Provider, {
484
+ value: fieldsetContextValue,
485
+ children: /* @__PURE__ */ jsx(FieldsetFieldContext.Provider, {
486
+ value: fieldRegistryValue,
487
+ children: resolved ? children : /* @__PURE__ */ jsx(ShippingAddressFieldsetSkeleton, { count: skeletonCount })
631
488
  })
489
+ }), /* @__PURE__ */ jsx("output", {
490
+ "aria-live": "polite",
491
+ "data-fs-shipping-address-fieldset-announcement": "",
492
+ children: announcement
493
+ })]
494
+ });
495
+ }
496
+ /**
497
+ * The loading state of the fieldset, rendered in place of the Controls until the entity resolves and
498
+ * declared as the definition's `skeleton`. Purely visual: the wait is spoken by the fieldset's own
499
+ * live region, so this is hidden from assistive technology rather than announced twice.
500
+ */
501
+ function ShippingAddressFieldsetSkeleton({ count = SHIPPING_ADDRESS_SKELETON_ROWS, className, ...props }) {
502
+ return /* @__PURE__ */ jsx("div", {
503
+ ...props,
504
+ className,
505
+ "data-fs-shipping-address-fieldset-skeleton": "",
506
+ "aria-hidden": "true",
507
+ children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ jsx("span", { "data-fs-shipping-address-fieldset-skeleton-field": "" }, i))
508
+ });
509
+ }
510
+ ShippingAddressFieldset.Skeleton = ShippingAddressFieldsetSkeleton;
511
+ function BoundAddressTextField({ fieldKey, slug, className, label, placeholder, children, fieldsetCtx, fieldDescriptor }) {
512
+ const name = useFieldsetField(SHIPPING_ADDRESS_NAMESPACE, fieldKey);
513
+ const resolvedLabel = label ?? fieldDescriptor.label;
514
+ return /* @__PURE__ */ jsx(FieldBinding, {
515
+ name,
516
+ label: resolvedLabel,
517
+ placeholder,
518
+ defaultValue: fieldsetCtx.seedFor(fieldKey),
519
+ rule: fieldsetCtx.ruleFor(fieldKey, resolvedLabel),
520
+ autoComplete: FIELD_AUTOCOMPLETE[fieldKey],
521
+ children: /* @__PURE__ */ jsx(Field.Root, {
522
+ className,
523
+ "data-fs-shipping-address-field": slug,
524
+ children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
525
+ /* @__PURE__ */ jsx(Field.Label, {}),
526
+ /* @__PURE__ */ jsx(Field.Control, {}),
527
+ /* @__PURE__ */ jsx(Field.Error, {})
528
+ ] })
632
529
  })
633
530
  });
634
531
  }
635
532
  /**
636
- * RecipientNameControl — the only client-validated field (US-4, US-6).
533
+ * RecipientNameControl — the only required field (US-4, US-6); the postal code is client-validated
534
+ * too, against the active country's format.
637
535
  * Pre-filled from clientProfile, editable, required.
638
536
  */
639
- function ShippingAddressRecipientNameControl({ className }) {
640
- const { draft, setDraftField, descriptor, submitting } = useShippingAddressContext();
641
- if (!descriptor?.fields.some((f) => f.name === "recipientName")) return null;
642
- const label = (descriptor?.fields.find((f) => f.name === "recipientName"))?.label ?? "nome do destinatário";
643
- return /* @__PURE__ */ jsxs(Field.Root, {
537
+ function ShippingAddressRecipientNameControl({ className, label, placeholder, children }) {
538
+ const fieldsetCtx = useShippingAddressFieldsetContext();
539
+ const fieldDescriptor = fieldsetCtx.fieldFor("recipientName");
540
+ if (!fieldDescriptor) return null;
541
+ return /* @__PURE__ */ jsx(BoundAddressTextField, {
542
+ fieldKey: "recipientName",
543
+ slug: "recipient-name",
644
544
  className,
645
- "data-fs-shipping-address-field": "recipient-name",
646
- disabled: submitting,
647
- children: [/* @__PURE__ */ jsx(Field.Label, { children: label }), /* @__PURE__ */ jsx(Field.Control, {
648
- value: draft.recipientName,
649
- onChange: (e) => setDraftField("recipientName", e.currentTarget.value)
650
- })]
545
+ label,
546
+ placeholder,
547
+ fieldsetCtx,
548
+ fieldDescriptor,
549
+ children
651
550
  });
652
551
  }
653
552
  /** StreetControl */
654
- function ShippingAddressStreetControl({ className }) {
655
- const { draft, setDraftField, descriptor, submitting } = useShippingAddressContext();
656
- const fieldDescriptor = descriptor?.fields.find((f) => f.name === "street");
553
+ function ShippingAddressStreetControl({ className, label, placeholder, children }) {
554
+ const fieldsetCtx = useShippingAddressFieldsetContext();
555
+ const fieldDescriptor = fieldsetCtx.fieldFor("street");
657
556
  if (!fieldDescriptor) return null;
658
- return /* @__PURE__ */ jsxs(Field.Root, {
557
+ return /* @__PURE__ */ jsx(BoundAddressTextField, {
558
+ fieldKey: "street",
559
+ slug: "street",
659
560
  className,
660
- "data-fs-shipping-address-field": "street",
661
- disabled: submitting,
662
- children: [/* @__PURE__ */ jsx(Field.Label, { children: fieldDescriptor.label }), /* @__PURE__ */ jsx(Field.Control, {
663
- value: draft.street,
664
- onChange: (e) => setDraftField("street", e.currentTarget.value)
665
- })]
561
+ label,
562
+ placeholder,
563
+ fieldsetCtx,
564
+ fieldDescriptor,
565
+ children
666
566
  });
667
567
  }
668
568
  /** NumberControl */
669
- function ShippingAddressNumberControl({ className }) {
670
- const { draft, setDraftField, descriptor, submitting } = useShippingAddressContext();
671
- const fieldDescriptor = descriptor?.fields.find((f) => f.name === "number");
569
+ function ShippingAddressNumberControl({ className, label, placeholder, children }) {
570
+ const fieldsetCtx = useShippingAddressFieldsetContext();
571
+ const fieldDescriptor = fieldsetCtx.fieldFor("number");
672
572
  if (!fieldDescriptor) return null;
673
- return /* @__PURE__ */ jsxs(Field.Root, {
573
+ return /* @__PURE__ */ jsx(BoundAddressTextField, {
574
+ fieldKey: "number",
575
+ slug: "number",
674
576
  className,
675
- "data-fs-shipping-address-field": "number",
676
- disabled: submitting,
677
- children: [/* @__PURE__ */ jsx(Field.Label, { children: fieldDescriptor.label }), /* @__PURE__ */ jsx(Field.Control, {
678
- value: draft.number,
679
- onChange: (e) => setDraftField("number", e.currentTarget.value)
680
- })]
577
+ label,
578
+ placeholder,
579
+ fieldsetCtx,
580
+ fieldDescriptor,
581
+ children
681
582
  });
682
583
  }
683
584
  /** CityControl */
684
- function ShippingAddressCityControl({ className }) {
685
- const { draft, setDraftField, descriptor, submitting } = useShippingAddressContext();
686
- const fieldDescriptor = descriptor?.fields.find((f) => f.name === "city");
585
+ function ShippingAddressCityControl({ className, label, placeholder, children }) {
586
+ const fieldsetCtx = useShippingAddressFieldsetContext();
587
+ const fieldDescriptor = fieldsetCtx.fieldFor("city");
687
588
  if (!fieldDescriptor) return null;
688
- return /* @__PURE__ */ jsxs(Field.Root, {
589
+ return /* @__PURE__ */ jsx(BoundAddressTextField, {
590
+ fieldKey: "city",
591
+ slug: "city",
689
592
  className,
690
- "data-fs-shipping-address-field": "city",
691
- disabled: submitting,
692
- children: [/* @__PURE__ */ jsx(Field.Label, { children: fieldDescriptor.label }), /* @__PURE__ */ jsx(Field.Control, {
693
- value: draft.city,
694
- onChange: (e) => setDraftField("city", e.currentTarget.value)
695
- })]
593
+ label,
594
+ placeholder,
595
+ fieldsetCtx,
596
+ fieldDescriptor,
597
+ children
696
598
  });
697
599
  }
698
600
  /** StateControl */
699
- function ShippingAddressStateControl({ className }) {
700
- const { draft, setDraftField, descriptor, submitting } = useShippingAddressContext();
701
- const fieldDescriptor = descriptor?.fields.find((f) => f.name === "state");
601
+ function ShippingAddressStateControl({ className, label, placeholder, children }) {
602
+ const fieldsetCtx = useShippingAddressFieldsetContext();
603
+ const fieldDescriptor = fieldsetCtx.fieldFor("state");
702
604
  if (!fieldDescriptor) return null;
703
- return /* @__PURE__ */ jsxs(Field.Root, {
605
+ return /* @__PURE__ */ jsx(BoundAddressTextField, {
606
+ fieldKey: "state",
607
+ slug: "state",
704
608
  className,
705
- "data-fs-shipping-address-field": "state",
706
- disabled: submitting,
707
- children: [/* @__PURE__ */ jsx(Field.Label, { children: fieldDescriptor.label }), /* @__PURE__ */ jsx(Field.Control, {
708
- value: draft.state,
709
- onChange: (e) => setDraftField("state", e.currentTarget.value)
710
- })]
609
+ label,
610
+ placeholder,
611
+ fieldsetCtx,
612
+ fieldDescriptor,
613
+ children
711
614
  });
712
615
  }
713
616
  /** PostalCodeControl */
714
- function ShippingAddressPostalCodeControl({ className }) {
715
- const { draft, setDraftField, descriptor, submitting } = useShippingAddressContext();
716
- const fieldDescriptor = descriptor?.fields.find((f) => f.name === "postalCode");
617
+ function ShippingAddressPostalCodeControl({ className, label, placeholder, children }) {
618
+ const fieldsetCtx = useShippingAddressFieldsetContext();
619
+ const fieldDescriptor = fieldsetCtx.fieldFor("postalCode");
717
620
  if (!fieldDescriptor) return null;
718
- return /* @__PURE__ */ jsxs(Field.Root, {
621
+ return /* @__PURE__ */ jsx(BoundAddressTextField, {
622
+ fieldKey: "postalCode",
623
+ slug: "postal-code",
719
624
  className,
720
- "data-fs-shipping-address-field": "postal-code",
721
- disabled: submitting,
722
- children: [/* @__PURE__ */ jsx(Field.Label, { children: fieldDescriptor.label }), /* @__PURE__ */ jsx(Field.Control, {
723
- value: draft.postalCode,
724
- onChange: (e) => setDraftField("postalCode", e.currentTarget.value)
725
- })]
625
+ label,
626
+ placeholder,
627
+ fieldsetCtx,
628
+ fieldDescriptor,
629
+ children
726
630
  });
727
631
  }
728
632
  /** ComplementControl */
729
- function ShippingAddressComplementControl({ className }) {
730
- const { draft, setDraftField, descriptor, submitting } = useShippingAddressContext();
731
- const fieldDescriptor = descriptor?.fields.find((f) => f.name === "complement");
633
+ function ShippingAddressComplementControl({ className, label, placeholder, children }) {
634
+ const fieldsetCtx = useShippingAddressFieldsetContext();
635
+ const fieldDescriptor = fieldsetCtx.fieldFor("complement");
732
636
  if (!fieldDescriptor) return null;
733
- return /* @__PURE__ */ jsxs(Field.Root, {
637
+ return /* @__PURE__ */ jsx(BoundAddressTextField, {
638
+ fieldKey: "complement",
639
+ slug: "complement",
734
640
  className,
735
- "data-fs-shipping-address-field": "complement",
736
- disabled: submitting,
737
- children: [/* @__PURE__ */ jsx(Field.Label, { children: fieldDescriptor.label }), /* @__PURE__ */ jsx(Field.Control, {
738
- value: draft.complement,
739
- onChange: (e) => setDraftField("complement", e.currentTarget.value)
740
- })]
641
+ label,
642
+ placeholder,
643
+ fieldsetCtx,
644
+ fieldDescriptor,
645
+ children
741
646
  });
742
647
  }
743
648
  /** NeighborhoodControl (BR-specific in v1) */
744
- function ShippingAddressNeighborhoodControl({ className }) {
745
- const { draft, setDraftField, descriptor, submitting } = useShippingAddressContext();
746
- const fieldDescriptor = descriptor?.fields.find((f) => f.name === "neighborhood");
649
+ function ShippingAddressNeighborhoodControl({ className, label, placeholder, children }) {
650
+ const fieldsetCtx = useShippingAddressFieldsetContext();
651
+ const fieldDescriptor = fieldsetCtx.fieldFor("neighborhood");
747
652
  if (!fieldDescriptor) return null;
748
- return /* @__PURE__ */ jsxs(Field.Root, {
749
- className,
750
- "data-fs-shipping-address-field": "neighborhood",
751
- disabled: submitting,
752
- children: [/* @__PURE__ */ jsx(Field.Label, { children: fieldDescriptor.label }), /* @__PURE__ */ jsx(Field.Control, {
753
- value: draft.neighborhood,
754
- onChange: (e) => setDraftField("neighborhood", e.currentTarget.value)
755
- })]
756
- });
757
- }
758
- /** CountryControl — lists only countries with a descriptor (US-2, first criterion) */
759
- function ShippingAddressCountryControl({ className, label = "país" }) {
760
- const { draft, setDraftField, setSelectedSavedId, submitting } = useShippingAddressContext();
761
- const countryOptions = getAvailableCountries().map((iso) => {
762
- return {
763
- value: iso,
764
- label: getCountryDescriptor(iso)?.label ?? iso
765
- };
766
- });
767
- const handleCountryChange = (value) => {
768
- if (!value) return;
769
- if (getCountryDescriptor(value)) {
770
- setDraftField("country", value);
771
- setSelectedSavedId(void 0);
772
- }
773
- };
774
- return /* @__PURE__ */ jsxs(Field.Root, {
775
- className,
776
- "data-fs-shipping-address-field": "country",
777
- disabled: submitting,
778
- children: [/* @__PURE__ */ jsx(Field.Label, { children: label }), /* @__PURE__ */ jsxs(Select.Root, {
779
- value: draft.country,
780
- onValueChange: handleCountryChange,
781
- disabled: submitting,
782
- children: [/* @__PURE__ */ jsxs(Select.Trigger, { children: [/* @__PURE__ */ jsx(Select.Value, { children: (value) => value ? getCountryDescriptor(value)?.label ?? value : value }), /* @__PURE__ */ jsx(Select.Icon, { children: "▾" })] }), /* @__PURE__ */ jsx(Select.Portal, { children: /* @__PURE__ */ jsx(Select.Positioner, { children: /* @__PURE__ */ jsx(Select.Popup, { children: /* @__PURE__ */ jsx(Select.List, { children: countryOptions.map((option) => /* @__PURE__ */ jsx(Select.Item, {
783
- value: option.value,
784
- label: option.label,
785
- children: option.label
786
- }, option.value)) }) }) }) })]
787
- })]
788
- });
789
- }
790
- /**
791
- * ShippingAddressSavedList — renders only when savedAddresses is non-empty (logged-in signal).
792
- */
793
- function ShippingAddressSavedList({ className }) {
794
- const { savedAddresses } = useShippingAddressContext();
795
- if (savedAddresses.length === 0) return null;
796
- return /* @__PURE__ */ jsx("div", {
797
- className,
798
- "data-fs-shipping-address-saved-list": "",
799
- children: savedAddresses.map((saved) => /* @__PURE__ */ jsx(ShippingAddressSavedOption, { id: saved.id }, saved.id))
800
- });
801
- }
802
- /**
803
- * ShippingAddressSavedOption — selectable saved address; populates draft on selection.
804
- * Includes isCurrent visual variant to mark the address matching the current cart address (Decision 8).
805
- */
806
- function ShippingAddressSavedOption({ id, className }) {
807
- const { savedAddresses, selectedSavedId, setSelectedSavedId, currentAddressId } = useShippingAddressContext();
808
- const saved = savedAddresses.find((s) => s.id === id);
809
- if (!saved) return null;
810
- const isSelected = selectedSavedId === id;
811
- const isCurrent = currentAddressId !== void 0 && currentAddressId === id;
812
- const hasAddressContent = Boolean(saved.address.recipientName || saved.address.street || saved.address.city || saved.address.neighborhood);
813
- const inputId = `saved-address-${id}`;
814
- return /* @__PURE__ */ jsxs("div", {
815
- className,
816
- "data-fs-shipping-address-saved-option": "",
817
- ...isSelected ? { "data-fs-shipping-address-saved-option-selected": "" } : {},
818
- ...isCurrent ? { "data-fs-shipping-address-saved-option-current": "" } : {},
819
- children: [/* @__PURE__ */ jsx("input", {
820
- id: inputId,
821
- type: "radio",
822
- name: "saved-address",
823
- value: id,
824
- checked: isSelected,
825
- onChange: () => setSelectedSavedId(id)
826
- }), /* @__PURE__ */ jsx("label", {
827
- htmlFor: inputId,
828
- children: hasAddressContent ? /* @__PURE__ */ jsx(AddressSummaryRoot, {
829
- address: saved.address,
830
- children: /* @__PURE__ */ jsx(AddressSummary, {})
831
- }) : `Endereço ${id}`
832
- })]
833
- });
834
- }
835
- /**
836
- * ShippingAddressSubmit — dispatches the batched submit; reflects pending state.
837
- */
838
- function ShippingAddressSubmit({ className, label = "salvar endereço", pendingLabel = "salvando..." }) {
839
- const { submitting, handleSubmit, isFormValid } = useShippingAddressContext();
840
- return /* @__PURE__ */ jsx("button", {
841
- type: "button",
842
- onClick: handleSubmit,
843
- disabled: submitting || !isFormValid,
653
+ return /* @__PURE__ */ jsx(BoundAddressTextField, {
654
+ fieldKey: "neighborhood",
655
+ slug: "neighborhood",
844
656
  className,
845
- "data-fs-shipping-address-submit": "",
846
- ...submitting ? { "data-fs-shipping-address-submit-pending": "" } : {},
847
- children: submitting ? pendingLabel : label
848
- });
849
- }
850
- /**
851
- * ShippingAddressSavedAddressDrawerController — makes the wrapped DrawerRoot controlled by the
852
- * shipping-address context's savedAddressDrawerOpen state, and blocks dismissal (Escape, backdrop
853
- * click, close button) while drawerSubmitting is true by simply not forwarding the close request
854
- * to the setter. Mirrors CartOpenOnAdd's cloneElement mechanism. Expects a single DrawerRoot child.
855
- */
856
- function ShippingAddressSavedAddressDrawerController({ children }) {
857
- const { savedAddressDrawerOpen, setSavedAddressDrawerOpen, drawerSubmitting } = useShippingAddressDrawerContext();
858
- if (!isValidElement(children)) return children;
859
- return cloneElement(children, {
860
- open: savedAddressDrawerOpen,
861
- onOpenChange: (next) => {
862
- if (!next && drawerSubmitting) return;
863
- setSavedAddressDrawerOpen(next);
864
- }
657
+ label,
658
+ placeholder,
659
+ fieldsetCtx,
660
+ fieldDescriptor,
661
+ children
865
662
  });
866
663
  }
867
664
  /**
868
- * ShippingAddressSavedAddressSubmit — the drawer's "selecionar" submit button; independent
869
- * submitting state from the main form's Submit (Decision 7); no-ops when the selected saved
870
- * address is already the current one (Decision 8).
665
+ * CountryControl — moves onto `FieldSelect` (Decision 8): the trigger resolves a country name from
666
+ * the field contract's `options`. Gated on `descriptor`, not on a per-field entry (country has
667
+ * none): a country with no descriptor renders no controls at all (Decision 3/4's "no entry, no
668
+ * control" applied to the field that selects the entry itself).
871
669
  */
872
- function ShippingAddressSavedAddressSubmit({ className, label = "selecionar", pendingLabel = "selecionando..." }) {
873
- const { selectedSavedId } = useShippingAddressContext();
874
- const { drawerSubmitting, handleDrawerSubmit } = useShippingAddressDrawerContext();
875
- return /* @__PURE__ */ jsx("button", {
876
- type: "button",
877
- onClick: handleDrawerSubmit,
878
- disabled: drawerSubmitting || !selectedSavedId,
670
+ function ShippingAddressCountryControl({ className, label, placeholder, children }) {
671
+ const fieldsetCtx = useShippingAddressFieldsetContext();
672
+ if (!fieldsetCtx.descriptor) return null;
673
+ return /* @__PURE__ */ jsx(BoundShippingAddressCountryControl, {
879
674
  className,
880
- "data-fs-shipping-address-saved-address-submit": "",
881
- ...drawerSubmitting ? { "data-fs-shipping-address-saved-address-submit-pending": "" } : {},
882
- children: drawerSubmitting ? pendingLabel : label
675
+ label,
676
+ placeholder,
677
+ fieldsetCtx,
678
+ children
883
679
  });
884
680
  }
885
- /**
886
- * ShippingAddressSelectedSummary — provides the current delivery address via AddressSummaryRoot's
887
- * context and renders it through AddressSummary. Composed lines go through that same wrapper rather
888
- * than replacing it, because AddressSummaryRoot renders no DOM and `className` would have nowhere to
889
- * land. Self-gating on currentAddress: renders null when there is none yet, rather than an empty
890
- * provider with nothing meaningful to show.
891
- */
892
- function ShippingAddressSelectedSummary({ className, children }) {
893
- const { currentAddress } = useShippingAddressContext();
894
- if (!currentAddress) return null;
895
- return /* @__PURE__ */ jsx(AddressSummaryRoot, {
896
- address: currentAddress,
897
- children: /* @__PURE__ */ jsx(AddressSummary, {
681
+ function BoundShippingAddressCountryControl({ className, label, placeholder, children, fieldsetCtx }) {
682
+ const name = useFieldsetField(SHIPPING_ADDRESS_NAMESPACE, "country");
683
+ return /* @__PURE__ */ jsx(FieldBinding, {
684
+ name,
685
+ label: label ?? "país",
686
+ placeholder: placeholder ?? fieldsetCtx.descriptor?.label,
687
+ defaultValue: fieldsetCtx.seedFor("country"),
688
+ options: fieldsetCtx.countryOptions,
689
+ autoComplete: FIELD_AUTOCOMPLETE.country,
690
+ children: /* @__PURE__ */ jsx(Field.Root, {
898
691
  className,
899
- children
692
+ "data-fs-shipping-address-field": "country",
693
+ children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
694
+ /* @__PURE__ */ jsx(Field.Label, {}),
695
+ /* @__PURE__ */ jsx(FieldSelect.Root, {}),
696
+ /* @__PURE__ */ jsx(Field.Error, {})
697
+ ] })
900
698
  })
901
699
  });
902
700
  }
903
- /**
904
- * ShippingAddressEditController — owns the view/edit mode. Defaults to 'view' when a current
905
- * address exists, 'edit' otherwise (derived reactively from currentAddressId, so it self-corrects
906
- * if the address arrives asynchronously after mount, same issue as the draft-sync effect above).
907
- * Once the shopper explicitly toggles (via ShippingAddressEditButton) or a submit succeeds, the
908
- * explicit mode takes over. Does not render any DOM itself; provides ShippingAddressSectionContext
909
- * to ShippingAddressSummaryView/ShippingAddressEditForm/ShippingAddressEditButton descendants.
910
- */
911
- function ShippingAddressEditController({ children }) {
912
- const { currentAddressId, submitSuccessSignal } = useShippingAddressContext();
913
- const [explicitMode, setExplicitMode] = useState(void 0);
914
- const lastHandledSignal = useRef(submitSuccessSignal);
915
- useEffect(() => {
916
- if (submitSuccessSignal !== lastHandledSignal.current) {
917
- lastHandledSignal.current = submitSuccessSignal;
918
- setExplicitMode("view");
919
- }
920
- }, [submitSuccessSignal]);
921
- const mode = explicitMode ?? (currentAddressId !== void 0 ? "view" : "edit");
922
- const modeContextValue = useMemo(() => ({
923
- mode,
924
- setMode: setExplicitMode
925
- }), [mode]);
926
- return /* @__PURE__ */ jsx(ShippingAddressSectionContext.Provider, {
927
- value: modeContextValue,
928
- children
929
- });
930
- }
931
- /** Renders its children only while the section shows the saved-address summary. */
932
- function ShippingAddressSummaryView({ children }) {
933
- const { mode } = useShippingAddressSectionContext();
934
- if (mode !== "view") return null;
935
- return /* @__PURE__ */ jsx(Fragment, { children });
936
- }
937
- /** Renders its children only while the section shows the address form. */
938
- function ShippingAddressEditForm({ children }) {
939
- const { mode } = useShippingAddressSectionContext();
940
- if (mode !== "edit") return null;
941
- return /* @__PURE__ */ jsx(Fragment, { children });
942
- }
943
- /**
944
- * ShippingAddressEditButton — CTA that switches the mode to 'edit'. Has no knowledge of why the
945
- * mode might currently be 'view' (a current address, or a just-completed submit) — it only writes
946
- * the mode forward.
947
- */
948
- function ShippingAddressEditButton({ className, label = "editar", children }) {
949
- const { setMode } = useShippingAddressSectionContext();
950
- return /* @__PURE__ */ jsx("button", {
951
- type: "button",
952
- onClick: () => setMode("edit"),
953
- className,
954
- "data-fs-shipping-address-edit-button": "",
955
- children: children ?? label
956
- });
957
- }
958
- /**
959
- * ShippingAddressCancelButton — returns the section to its summary without writing. Without it a
960
- * shopper who opens the form can only leave through a successful `setAddress`, and blanking the
961
- * recipient disables Submit, which leaves the section stuck open over the summary it replaced.
962
- */
963
- function ShippingAddressCancelButton({ className, label = "cancelar", children }) {
964
- const { setMode } = useShippingAddressSectionContext();
965
- const { discardDraft } = useShippingAddressContext();
966
- return /* @__PURE__ */ jsx("button", {
967
- type: "button",
968
- onClick: () => {
969
- discardDraft();
970
- setMode("view");
971
- },
972
- className,
973
- "data-fs-shipping-address-cancel-button": "",
974
- children: children ?? label
975
- });
976
- }
977
- /**
978
- * ShippingAddressError — renders domain userErrors inline, or generic upstream error state.
979
- */
980
- function ShippingAddressError({ className }) {
981
- const { errors, upstreamError } = useShippingAddressContext();
982
- if (errors.length === 0 && !upstreamError) return null;
983
- return /* @__PURE__ */ jsx("div", {
984
- className,
985
- "data-fs-shipping-address-error": "",
986
- children: upstreamError ? /* @__PURE__ */ jsx("div", { children: "Não foi possível salvar este endereço. Revise os dados e tente novamente." }) : /* @__PURE__ */ jsx("ul", { children: errors.map((error, idx) => /* @__PURE__ */ jsx("li", { children: error.message }, idx)) })
987
- });
988
- }
989
701
  const ShippingAddress = {
990
- Root: ShippingAddressRoot,
991
- RootDefinition: ShippingAddressRootDefinition,
702
+ Fieldset: ShippingAddressFieldset,
703
+ FieldsetDefinition: ShippingAddressFieldsetDefinition,
704
+ FieldsetSkeleton: ShippingAddressFieldsetSkeleton,
992
705
  RecipientNameControl: ShippingAddressRecipientNameControl,
993
706
  RecipientNameControlDefinition: ShippingAddressRecipientNameControlDefinition,
994
707
  StreetControl: ShippingAddressStreetControl,
@@ -1006,33 +719,9 @@ const ShippingAddress = {
1006
719
  NeighborhoodControl: ShippingAddressNeighborhoodControl,
1007
720
  NeighborhoodControlDefinition: ShippingAddressNeighborhoodControlDefinition,
1008
721
  CountryControl: ShippingAddressCountryControl,
1009
- CountryControlDefinition: ShippingAddressCountryControlDefinition,
1010
- SavedList: ShippingAddressSavedList,
1011
- SavedListDefinition: ShippingAddressSavedListDefinition,
1012
- SavedOption: ShippingAddressSavedOption,
1013
- SavedOptionDefinition: ShippingAddressSavedOptionDefinition,
1014
- Submit: ShippingAddressSubmit,
1015
- SubmitDefinition: ShippingAddressSubmitDefinition,
1016
- SavedAddressDrawerController: ShippingAddressSavedAddressDrawerController,
1017
- SavedAddressDrawerControllerDefinition: ShippingAddressSavedAddressDrawerControllerDefinition,
1018
- SavedAddressSubmit: ShippingAddressSavedAddressSubmit,
1019
- SavedAddressSubmitDefinition: ShippingAddressSavedAddressSubmitDefinition,
1020
- SelectedSummary: ShippingAddressSelectedSummary,
1021
- SelectedSummaryDefinition: ShippingAddressSelectedSummaryDefinition,
1022
- EditController: ShippingAddressEditController,
1023
- EditControllerDefinition: ShippingAddressEditControllerDefinition,
1024
- SummaryView: ShippingAddressSummaryView,
1025
- SummaryViewDefinition: ShippingAddressSummaryViewDefinition,
1026
- EditForm: ShippingAddressEditForm,
1027
- EditFormDefinition: ShippingAddressEditFormDefinition,
1028
- EditButton: ShippingAddressEditButton,
1029
- EditButtonDefinition: ShippingAddressEditButtonDefinition,
1030
- CancelButton: ShippingAddressCancelButton,
1031
- CancelButtonDefinition: ShippingAddressCancelButtonDefinition,
1032
- Error: ShippingAddressError,
1033
- ErrorDefinition: ShippingAddressErrorDefinition
722
+ CountryControlDefinition: ShippingAddressCountryControlDefinition
1034
723
  };
1035
724
  //#endregion
1036
- export { ShippingAddress, ShippingAddressCancelButton, ShippingAddressCancelButtonDefinition, ShippingAddressCityControl, ShippingAddressCityControlDefinition, ShippingAddressComplementControl, ShippingAddressComplementControlDefinition, ShippingAddressCountryControl, ShippingAddressCountryControlDefinition, ShippingAddressEditButton, ShippingAddressEditButtonDefinition, ShippingAddressEditController, ShippingAddressEditControllerDefinition, ShippingAddressEditForm, ShippingAddressEditFormDefinition, ShippingAddressError, ShippingAddressErrorDefinition, ShippingAddressNeighborhoodControl, ShippingAddressNeighborhoodControlDefinition, ShippingAddressNumberControl, ShippingAddressNumberControlDefinition, ShippingAddressPostalCodeControl, ShippingAddressPostalCodeControlDefinition, ShippingAddressRecipientNameControl, ShippingAddressRecipientNameControlDefinition, ShippingAddressRoot, ShippingAddressRootDefinition, ShippingAddressSavedAddressDrawerController, ShippingAddressSavedAddressDrawerControllerDefinition, ShippingAddressSavedAddressSubmit, ShippingAddressSavedAddressSubmitDefinition, ShippingAddressSavedList, ShippingAddressSavedListDefinition, ShippingAddressSavedOption, ShippingAddressSavedOptionDefinition, ShippingAddressSelectedSummary, ShippingAddressSelectedSummaryDefinition, ShippingAddressStateControl, ShippingAddressStateControlDefinition, ShippingAddressStreetControl, ShippingAddressStreetControlDefinition, ShippingAddressSubmit, ShippingAddressSubmitDefinition, ShippingAddressSummaryView, ShippingAddressSummaryViewDefinition };
725
+ export { ShippingAddress, ShippingAddressCityControl, ShippingAddressCityControlDefinition, ShippingAddressComplementControl, ShippingAddressComplementControlDefinition, ShippingAddressCountryControl, ShippingAddressCountryControlDefinition, ShippingAddressFieldset, ShippingAddressFieldsetDefinition, ShippingAddressFieldsetSkeleton, ShippingAddressNeighborhoodControl, ShippingAddressNeighborhoodControlDefinition, ShippingAddressNumberControl, ShippingAddressNumberControlDefinition, ShippingAddressPostalCodeControl, ShippingAddressPostalCodeControlDefinition, ShippingAddressRecipientNameControl, ShippingAddressRecipientNameControlDefinition, ShippingAddressStateControl, ShippingAddressStateControlDefinition, ShippingAddressStreetControl, ShippingAddressStreetControlDefinition };
1037
726
 
1038
727
  //# sourceMappingURL=shipping-address.js.map