@vtex/faststore 0.0.8 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/dist/actions.json +42 -0
  3. package/dist/catalog.json +731 -16
  4. package/dist/cli/index.js +1 -1
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/components.d.ts +9 -4
  7. package/dist/components.js +11 -6
  8. package/dist/content-types.json +300 -0
  9. package/dist/core/dist/define-component.d.ts +6 -1
  10. package/dist/docs/account-entry-point.md +4 -4
  11. package/dist/docs/account-menu.md +2 -2
  12. package/dist/docs/account-saved-cards.md +301 -0
  13. package/dist/docs/binding-switcher.md +154 -0
  14. package/dist/docs/card-entry.md +3 -3
  15. package/dist/docs/card-summary.md +175 -0
  16. package/dist/docs/cart-destination.md +238 -0
  17. package/dist/docs/cart-item.md +21 -3
  18. package/dist/docs/cart-totalizer.md +1 -1
  19. package/dist/docs/cart.md +118 -11
  20. package/dist/docs/footer-social.md +1 -1
  21. package/dist/docs/free-shipping-progress.md +13 -9
  22. package/dist/docs/incentives.md +2 -2
  23. package/dist/docs/logo.md +1 -1
  24. package/dist/docs/payment-method-badges.md +2 -2
  25. package/dist/docs/payment-methods.md +1 -1
  26. package/dist/docs/product-information.md +2 -2
  27. package/dist/docs/product-shelf.md +7 -0
  28. package/dist/docs/select.md +3 -4
  29. package/dist/docs/sign-in-redirect.md +1 -1
  30. package/dist/docs/toast.md +30 -12
  31. package/dist/index.d.ts +2 -1
  32. package/dist/internal/client.d.ts +4 -1
  33. package/dist/internal/client.js +4 -1
  34. package/dist/internal/server.d.ts +4 -2
  35. package/dist/internal/server.js +4 -2
  36. package/dist/matter/account-saved-cards/account-saved-cards.d.ts +383 -0
  37. package/dist/matter/account-saved-cards/account-saved-cards.js +956 -0
  38. package/dist/matter/account-saved-cards/account-saved-cards.js.map +1 -0
  39. package/dist/matter/billing-address/billing-address.js.map +1 -1
  40. package/dist/matter/cart/cart-namespace.d.ts +5 -1
  41. package/dist/matter/cart/cart-namespace.js +5 -1
  42. package/dist/matter/cart/cart-namespace.js.map +1 -1
  43. package/dist/matter/cart/cart.d.ts +34 -4
  44. package/dist/matter/cart/cart.js +238 -28
  45. package/dist/matter/cart/cart.js.map +1 -1
  46. package/dist/matter/cart-destination/cart-destination.d.ts +222 -0
  47. package/dist/matter/cart-destination/cart-destination.js +613 -0
  48. package/dist/matter/cart-destination/cart-destination.js.map +1 -0
  49. package/dist/matter/cart-item/cart-item.d.ts +4 -2
  50. package/dist/matter/cart-item/cart-item.js +75 -16
  51. package/dist/matter/cart-item/cart-item.js.map +1 -1
  52. package/dist/matter/cart-totalizer/cart-totalizer.js +3 -2
  53. package/dist/matter/cart-totalizer/cart-totalizer.js.map +1 -1
  54. package/dist/matter/free-shipping-progress/free-shipping-progress.d.ts +6 -3
  55. package/dist/matter/free-shipping-progress/free-shipping-progress.js +76 -33
  56. package/dist/matter/free-shipping-progress/free-shipping-progress.js.map +1 -1
  57. package/dist/matter/pricing/pricing.js.map +1 -1
  58. package/dist/matter/product-shelf/product-shelf.d.ts +7 -0
  59. package/dist/matter/product-shelf/product-shelf.js +115 -1
  60. package/dist/matter/product-shelf/product-shelf.js.map +1 -1
  61. package/dist/matter/shipping-address/shipping-address.js.map +1 -1
  62. package/dist/matter/shipping-simulator/shipping-simulator.d.ts +1 -1
  63. package/dist/matter/shipping-simulator/shipping-simulator.js +6 -2
  64. package/dist/matter/shipping-simulator/shipping-simulator.js.map +1 -1
  65. package/dist/particles/binding-switcher/binding-switcher.d.ts +38 -0
  66. package/dist/particles/binding-switcher/binding-switcher.js +183 -0
  67. package/dist/particles/binding-switcher/binding-switcher.js.map +1 -0
  68. package/dist/particles/card-summary/card-network.d.ts +17 -0
  69. package/dist/particles/card-summary/card-network.js +25 -0
  70. package/dist/particles/card-summary/card-network.js.map +1 -0
  71. package/dist/particles/card-summary/card-summary.d.ts +83 -0
  72. package/dist/particles/card-summary/card-summary.js +188 -0
  73. package/dist/particles/card-summary/card-summary.js.map +1 -0
  74. package/dist/particles/footer-social/footer-social.d.ts +1 -1
  75. package/dist/particles/footer-social/footer-social.js +1 -1
  76. package/dist/particles/footer-social/footer-social.js.map +1 -1
  77. package/dist/particles/incentives/incentives.d.ts +1 -1
  78. package/dist/particles/incentives/incentives.js +1 -1
  79. package/dist/particles/incentives/incentives.js.map +1 -1
  80. package/dist/particles/payment-methods/payment-methods.d.ts +1 -1
  81. package/dist/particles/payment-methods/payment-methods.js +1 -1
  82. package/dist/particles/payment-methods/payment-methods.js.map +1 -1
  83. package/dist/particles/toast/toast.d.ts +20 -6
  84. package/dist/particles/toast/toast.js +49 -7
  85. package/dist/particles/toast/toast.js.map +1 -1
  86. package/dist/schema.graphql +298 -34
  87. package/dist/sdk/actions/index.d.ts +3 -1
  88. package/dist/sdk/actions/index.js +3 -1
  89. package/dist/sdk/actions/locale-action-provider.d.ts +16 -0
  90. package/dist/sdk/actions/locale-action-provider.js +20 -0
  91. package/dist/sdk/actions/locale-action-provider.js.map +1 -0
  92. package/dist/sdk/actions/saved-card-action-provider.d.ts +12 -0
  93. package/dist/sdk/actions/saved-card-action-provider.js +16 -0
  94. package/dist/sdk/actions/saved-card-action-provider.js.map +1 -0
  95. package/dist/sdk/actions/types.d.ts +6 -2
  96. package/dist/sdk/cache/storefront-cache.js +2 -1
  97. package/dist/sdk/cache/storefront-cache.js.map +1 -1
  98. package/dist/sdk/cart/operations.d.ts +2 -1
  99. package/dist/sdk/cart/operations.js +55 -1
  100. package/dist/sdk/cart/operations.js.map +1 -1
  101. package/dist/sdk/cart/optimistic.d.ts +30 -7
  102. package/dist/sdk/cart/optimistic.js +78 -41
  103. package/dist/sdk/cart/optimistic.js.map +1 -1
  104. package/dist/sdk/cart/use-cart-actions.d.ts +7 -0
  105. package/dist/sdk/cart/use-cart-actions.js +61 -15
  106. package/dist/sdk/cart/use-cart-actions.js.map +1 -1
  107. package/dist/sdk/cart/use-cart-sync-sales-channel.js +70 -0
  108. package/dist/sdk/cart/use-cart-sync-sales-channel.js.map +1 -0
  109. package/dist/sdk/generated/gql.js +15 -11
  110. package/dist/sdk/generated/gql.js.map +1 -1
  111. package/dist/sdk/generated/graphql.d.ts +177 -46
  112. package/dist/sdk/generated/graphql.js +2314 -93
  113. package/dist/sdk/generated/graphql.js.map +1 -1
  114. package/dist/sdk/i18n/messages/en-US.js +19 -0
  115. package/dist/sdk/i18n/messages/en-US.json +19 -0
  116. package/dist/sdk/i18n/messages/en-US.json.d.ts +19 -0
  117. package/dist/sdk/i18n/messages/es-ES.js +19 -0
  118. package/dist/sdk/i18n/messages/es-ES.json +19 -0
  119. package/dist/sdk/i18n/messages/pt-BR.js +19 -0
  120. package/dist/sdk/i18n/messages/pt-BR.json +19 -0
  121. package/dist/sdk/i18n/provider.js +1 -1
  122. package/dist/sdk/i18n/provider.js.map +1 -1
  123. package/dist/sdk/locale/binding-switch-provider.d.ts +13 -0
  124. package/dist/sdk/locale/binding-switch-provider.js +20 -0
  125. package/dist/sdk/locale/binding-switch-provider.js.map +1 -0
  126. package/dist/sdk/locale/use-locale-actions.d.ts +16 -0
  127. package/dist/sdk/locale/use-locale-actions.js +70 -0
  128. package/dist/sdk/locale/use-locale-actions.js.map +1 -0
  129. package/dist/sdk/navigation/binding-switch.d.ts +39 -0
  130. package/dist/sdk/navigation/binding-switch.js +129 -0
  131. package/dist/sdk/navigation/binding-switch.js.map +1 -0
  132. package/dist/sdk/navigation/decode-route-params.d.ts +11 -0
  133. package/dist/sdk/navigation/decode-route-params.js +32 -0
  134. package/dist/sdk/navigation/decode-route-params.js.map +1 -0
  135. package/dist/sdk/saved-cards/operations.d.ts +16 -0
  136. package/dist/sdk/saved-cards/operations.js +41 -0
  137. package/dist/sdk/saved-cards/operations.js.map +1 -0
  138. package/dist/sdk/saved-cards/use-saved-card-actions.d.ts +26 -0
  139. package/dist/sdk/saved-cards/use-saved-card-actions.js +86 -0
  140. package/dist/sdk/saved-cards/use-saved-card-actions.js.map +1 -0
  141. package/dist/sdk/seo/page-alternates.d.ts +5 -3
  142. package/dist/sdk/seo/page-alternates.js +7 -12
  143. package/dist/sdk/seo/page-alternates.js.map +1 -1
  144. package/dist/sdk/shipping/shipping-operations.d.ts +1 -1
  145. package/dist/sdk/shipping/shipping-operations.js +13 -0
  146. package/dist/sdk/shipping/shipping-operations.js.map +1 -1
  147. package/dist/sdk/state/cart.d.ts +9 -2
  148. package/dist/sdk/state/cart.js +9 -2
  149. package/dist/sdk/state/cart.js.map +1 -1
  150. package/package.json +2 -2
  151. package/src/styles/tailwind.css +1 -2
@@ -0,0 +1,613 @@
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 { useActionsOverride } from "../../sdk/actions/actions-override-context.js";
6
+ import { useDataOverride } from "../../sdk/data/data-override-context.js";
7
+ import { ReactiveRootContext } from "../../sdk/data/page-reactive-data-root.js";
8
+ import { getCountryDescriptor } from "../shipping-address/country-descriptors.js";
9
+ import { awaitCartWrites } from "../../sdk/cart/write-gate.js";
10
+ import { PostalCodeField, isPostalCodeComplete } from "../../particles/postal-code-field/postal-code-field.js";
11
+ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
12
+ import { jsx, jsxs } from "react/jsx-runtime";
13
+ //#region src/matter/cart-destination/cart-destination.tsx
14
+ const fragment = `
15
+ fragment CartDestinationData on Query {
16
+ shipping {
17
+ __typename
18
+ id
19
+ address {
20
+ __typename
21
+ id
22
+ postalCode
23
+ country
24
+ city
25
+ state
26
+ }
27
+ }
28
+ }
29
+ `;
30
+ const CartDestinationValueContent = defineContentType({
31
+ id: "cart-destination-value",
32
+ title: "Cart Destination Value",
33
+ description: "Copy that precedes the destination in read mode.",
34
+ schema: {
35
+ type: "object",
36
+ additionalProperties: false,
37
+ properties: { prefix: {
38
+ type: "string",
39
+ title: "Prefix"
40
+ } }
41
+ }
42
+ });
43
+ const CartDestinationChangeContent = defineContentType({
44
+ id: "cart-destination-change",
45
+ title: "Cart Destination Change",
46
+ description: "Copy for the control that opens edit mode.",
47
+ schema: {
48
+ type: "object",
49
+ additionalProperties: false,
50
+ properties: { label: {
51
+ type: "string",
52
+ title: "Button label"
53
+ } }
54
+ }
55
+ });
56
+ const CartDestinationSubmitContent = defineContentType({
57
+ id: "cart-destination-submit",
58
+ title: "Cart Destination Submit",
59
+ description: "Copy for the control that writes the destination.",
60
+ schema: {
61
+ type: "object",
62
+ additionalProperties: false,
63
+ properties: {
64
+ label: {
65
+ type: "string",
66
+ title: "Button label"
67
+ },
68
+ pendingLabel: {
69
+ type: "string",
70
+ title: "Pending label"
71
+ }
72
+ }
73
+ }
74
+ });
75
+ const CartDestinationCancelContent = defineContentType({
76
+ id: "cart-destination-cancel",
77
+ title: "Cart Destination Cancel",
78
+ description: "Copy for the control that closes edit mode without writing.",
79
+ schema: {
80
+ type: "object",
81
+ additionalProperties: false,
82
+ properties: { label: {
83
+ type: "string",
84
+ title: "Button label"
85
+ } }
86
+ }
87
+ });
88
+ const CartDestinationErrorContent = defineContentType({
89
+ id: "cart-destination-error",
90
+ title: "Cart Destination Error",
91
+ description: "Message shown when the destination write fails.",
92
+ schema: {
93
+ type: "object",
94
+ additionalProperties: false,
95
+ properties: { genericMessage: {
96
+ type: "string",
97
+ title: "Failure message"
98
+ } }
99
+ }
100
+ });
101
+ const CART_DESTINATION_CONTEXT_UNSET = Symbol("CartDestinationContextUnset");
102
+ const CartDestinationContextInternal = createContext(CART_DESTINATION_CONTEXT_UNSET);
103
+ /**
104
+ * Read the destination and the edit-mode state from the nearest `CartDestinationRoot`. Throws outside
105
+ * one: a misplaced part is a composition bug the author should see at once.
106
+ */
107
+ function useCartDestination() {
108
+ const ctx = useContext(CartDestinationContextInternal);
109
+ if (ctx === CART_DESTINATION_CONTEXT_UNSET) throw new Error("CartDestination components must be used inside a <CartDestinationRoot> subtree.");
110
+ return ctx;
111
+ }
112
+ /** Whether a `CartDestinationForm` encloses the subtree, so `CartDestinationSubmit` knows its form owner. */
113
+ const CartDestinationFormContext = createContext(false);
114
+ const DEFAULT_COUNTRY = "BRA";
115
+ /** Mask per country; the field's own default (the Brazilian CEP) covers any country not listed. */
116
+ const DEFAULT_MASK = "99999-999";
117
+ const COUNTRY_MASKS = {
118
+ BRA: DEFAULT_MASK,
119
+ USA: "99999|99999-9999"
120
+ };
121
+ function maskFor(country) {
122
+ return COUNTRY_MASKS[country] ?? DEFAULT_MASK;
123
+ }
124
+ /** Letters and digits only, so `01310100` and `01310-100` compare equal. */
125
+ function normalizePostalCode(value) {
126
+ return value.replaceAll(/[^0-9A-Za-z]/g, "").toUpperCase();
127
+ }
128
+ function isDraftValid(draft, country, mask) {
129
+ if (!isPostalCodeComplete(draft, mask)) return false;
130
+ const pattern = getCountryDescriptor(country)?.fields.find((field) => field.name === "postalCode")?.rule?.pattern;
131
+ if (!pattern) return true;
132
+ return (pattern instanceof RegExp ? pattern : pattern.value).test(draft);
133
+ }
134
+ const CartDestinationRootDefinition = defineComponent({
135
+ name: "CartDestinationRoot",
136
+ importPath: "@vtex/faststore/components",
137
+ component: CartDestinationRoot,
138
+ fragment,
139
+ fetchStrategy: "reactive",
140
+ providesContext: "cartDestination",
141
+ actions: { shipping: ["setAddress"] },
142
+ editor: {
143
+ label: "Cart Destination Root",
144
+ category: "content"
145
+ },
146
+ schema: { defaultCountry: {
147
+ type: "string",
148
+ default: "BRA",
149
+ label: "Default country (ISO alpha-3)"
150
+ } },
151
+ guideline: "Where the cart is delivered, with a postal-code-only form that writes the cart destination so the free-shipping bar follows. Compose CartDestinationRead (CartDestinationValue, CartDestinationUnset, CartDestinationChange) for read mode and CartDestinationForm (PostalCodeControl, Submit, Cancel, Error) for edit mode. Starts in read mode; the store cannot open edit mode from outside. See cart-destination.md."
152
+ });
153
+ const CartDestinationReadDefinition = defineComponent({
154
+ name: "CartDestinationRead",
155
+ importPath: "@vtex/faststore/components",
156
+ component: CartDestinationRead,
157
+ requiresContext: "cartDestination",
158
+ editor: {
159
+ label: "Cart Destination Read",
160
+ category: "layout"
161
+ },
162
+ schema: {},
163
+ guideline: "Wraps what read mode shows: the value, the invitation when no destination is set, and the change control. Renders nothing while edit mode is open. Must be inside CartDestinationRoot."
164
+ });
165
+ const CartDestinationValueDefinition = defineComponent({
166
+ name: "CartDestinationValue",
167
+ importPath: "@vtex/faststore/components",
168
+ component: CartDestinationValue,
169
+ content: CartDestinationValueContent,
170
+ requiresContext: "cartDestination",
171
+ editor: {
172
+ label: "Cart Destination Value",
173
+ category: "content"
174
+ },
175
+ schema: { prefix: {
176
+ type: "string",
177
+ label: "Prefix"
178
+ } },
179
+ guideline: "The cart destination as postal code, city and state, each in its own element. Renders nothing while loading or when no destination is set. Must be inside CartDestinationRoot."
180
+ });
181
+ const CartDestinationUnsetDefinition = defineComponent({
182
+ name: "CartDestinationUnset",
183
+ importPath: "@vtex/faststore/components",
184
+ component: CartDestinationUnset,
185
+ requiresContext: "cartDestination",
186
+ editor: {
187
+ label: "Cart Destination Unset",
188
+ category: "content"
189
+ },
190
+ schema: {},
191
+ guideline: "Renders its children only once the destination resolved and none is set. Place the store-authored invitation (for example a Text reading \"Informe seu CEP\") inside it, within CartDestinationRead. Must be inside CartDestinationRoot."
192
+ });
193
+ const CartDestinationChangeDefinition = defineComponent({
194
+ name: "CartDestinationChange",
195
+ importPath: "@vtex/faststore/components",
196
+ component: CartDestinationChange,
197
+ content: CartDestinationChangeContent,
198
+ requiresContext: "cartDestination",
199
+ editor: {
200
+ label: "Cart Destination Change",
201
+ category: "action"
202
+ },
203
+ schema: { label: {
204
+ type: "string",
205
+ label: "Button label"
206
+ } },
207
+ guideline: "Button that opens edit mode, styled as a link by the store. Place it inside CartDestinationRead. Must be inside CartDestinationRoot."
208
+ });
209
+ const CartDestinationFormDefinition = defineComponent({
210
+ name: "CartDestinationForm",
211
+ importPath: "@vtex/faststore/components",
212
+ component: CartDestinationForm,
213
+ requiresContext: "cartDestination",
214
+ editor: {
215
+ label: "Cart Destination Form",
216
+ category: "action"
217
+ },
218
+ schema: {},
219
+ guideline: "Edit mode: renders only while editing and routes Enter to the submit. Holds the postal code control, Submit, Cancel and Error, and any help text the store places. Must be inside CartDestinationRoot."
220
+ });
221
+ const CartDestinationPostalCodeControlDefinition = defineComponent({
222
+ name: "CartDestinationPostalCodeControl",
223
+ importPath: "@vtex/faststore/components",
224
+ component: CartDestinationPostalCodeControl,
225
+ requiresContext: "cartDestination",
226
+ editor: {
227
+ label: "Cart Destination Postal Code Control",
228
+ category: "content"
229
+ },
230
+ schema: {
231
+ mask: {
232
+ type: "string",
233
+ label: "Mask pattern"
234
+ },
235
+ label: {
236
+ type: "string",
237
+ label: "Label"
238
+ },
239
+ placeholder: {
240
+ type: "string",
241
+ label: "Placeholder"
242
+ },
243
+ invalidMessage: {
244
+ type: "string",
245
+ label: "Invalid-value message"
246
+ }
247
+ },
248
+ guideline: "The postal code input of edit mode, empty each time edit mode opens and focused on open. The mask follows the cart country unless set. Must be inside CartDestinationForm."
249
+ });
250
+ const CartDestinationSubmitDefinition = defineComponent({
251
+ name: "CartDestinationSubmit",
252
+ importPath: "@vtex/faststore/components",
253
+ component: CartDestinationSubmit,
254
+ content: CartDestinationSubmitContent,
255
+ requiresContext: "cartDestination",
256
+ editor: {
257
+ label: "Cart Destination Submit",
258
+ category: "action"
259
+ },
260
+ schema: {
261
+ label: {
262
+ type: "string",
263
+ label: "Button label"
264
+ },
265
+ pendingLabel: {
266
+ type: "string",
267
+ label: "Pending label"
268
+ }
269
+ },
270
+ guideline: "Writes the typed postal code. Disabled until it is complete for the country, valid and different from the current one, and while a write is pending (aria-busy). Must be inside CartDestinationForm."
271
+ });
272
+ const CartDestinationCancelDefinition = defineComponent({
273
+ name: "CartDestinationCancel",
274
+ importPath: "@vtex/faststore/components",
275
+ component: CartDestinationCancel,
276
+ content: CartDestinationCancelContent,
277
+ requiresContext: "cartDestination",
278
+ editor: {
279
+ label: "Cart Destination Cancel",
280
+ category: "action"
281
+ },
282
+ schema: { label: {
283
+ type: "string",
284
+ label: "Button label"
285
+ } },
286
+ guideline: "Closes edit mode, discards what was typed and writes nothing. Disabled while a write is pending. Must be inside CartDestinationForm."
287
+ });
288
+ const CartDestinationErrorDefinition = defineComponent({
289
+ name: "CartDestinationError",
290
+ importPath: "@vtex/faststore/components",
291
+ component: CartDestinationError,
292
+ content: CartDestinationErrorContent,
293
+ requiresContext: "cartDestination",
294
+ editor: {
295
+ label: "Cart Destination Error",
296
+ category: "content"
297
+ },
298
+ schema: { genericMessage: {
299
+ type: "string",
300
+ label: "Failure message"
301
+ } },
302
+ guideline: "Announces why the destination write failed, as an alert, and renders nothing otherwise. Author one message covering a refused postal code and a network failure: both reach the client as the same error. Must be inside CartDestinationForm."
303
+ });
304
+ /**
305
+ * Reads the cart destination reactively and publishes it, the edit-mode flag, the draft and the write
306
+ * state as `cartDestination` context.
307
+ *
308
+ * The destination is never copied into state: the Value renders only what `$data` resolves, and a
309
+ * successful write updates it solely by the mutation payload normalizing into the Apollo cache. A
310
+ * rejected write therefore needs no rollback.
311
+ */
312
+ function CartDestinationRoot({ defaultCountry = DEFAULT_COUNTRY, $data: $dataProp, $actions: $actionsProp, className, children, ...props }) {
313
+ const $data = useDataOverride($dataProp);
314
+ const $actions = useActionsOverride($actionsProp);
315
+ const address = $data?.address ?? null;
316
+ const { loading: rootLoading } = useContext(ReactiveRootContext);
317
+ const postalCode = address?.postalCode ?? null;
318
+ const loading = rootLoading && postalCode == null;
319
+ const country = address?.country ?? defaultCountry;
320
+ const [editing, setEditing] = useState(false);
321
+ const [draft, setDraft] = useState("");
322
+ const [pending, setPending] = useState(false);
323
+ const [errors, setErrors] = useState([]);
324
+ const [invalid, setInvalid] = useState(false);
325
+ const inFlightRef = useRef(false);
326
+ const rootRef = useRef(null);
327
+ const wasEditingRef = useRef(false);
328
+ /**
329
+ * Opening edit mode moves focus to the field; leaving it moves focus back to the change control,
330
+ * which only mounts once read mode renders again, hence an effect on the flag rather than a call at
331
+ * the event.
332
+ */
333
+ useEffect(() => {
334
+ if (wasEditingRef.current === editing) return;
335
+ wasEditingRef.current = editing;
336
+ const selector = editing ? "[data-fs-postal-code-field-input]" : "[data-fs-cart-destination-change]";
337
+ rootRef.current?.querySelector(selector)?.focus();
338
+ }, [editing]);
339
+ const [registeredMask, setRegisteredMask] = useState(null);
340
+ const mask = registeredMask ?? maskFor(country);
341
+ const canSubmit = !pending && isDraftValid(draft, country, mask) && normalizePostalCode(draft) !== normalizePostalCode(postalCode ?? "");
342
+ const startEditing = useCallback(() => setEditing(true), []);
343
+ const cancel = useCallback(() => {
344
+ setEditing(false);
345
+ setDraft("");
346
+ setErrors([]);
347
+ setInvalid(false);
348
+ }, []);
349
+ /** Typing is the shopper's answer to a refusal, so the message and the invalid flag go with it. */
350
+ const updateDraft = useCallback((value) => {
351
+ setDraft(value);
352
+ setErrors((current) => current.length === 0 ? current : []);
353
+ setInvalid(false);
354
+ }, []);
355
+ const submit = useCallback(() => {
356
+ if (inFlightRef.current) return;
357
+ const setAddress = $actions?.shipping?.setAddress;
358
+ if (!setAddress) return;
359
+ if (!canSubmit) {
360
+ setInvalid(true);
361
+ return;
362
+ }
363
+ inFlightRef.current = true;
364
+ setPending(true);
365
+ setErrors([]);
366
+ setInvalid(false);
367
+ awaitCartWrites().then(() => setAddress({
368
+ postalCode: draft,
369
+ country
370
+ })).then((writeErrors) => {
371
+ if (writeErrors.length > 0) {
372
+ setErrors(writeErrors);
373
+ return;
374
+ }
375
+ setEditing(false);
376
+ setDraft("");
377
+ }).finally(() => {
378
+ inFlightRef.current = false;
379
+ setPending(false);
380
+ });
381
+ }, [
382
+ $actions,
383
+ canSubmit,
384
+ draft,
385
+ country
386
+ ]);
387
+ const value = useMemo(() => ({
388
+ postalCode,
389
+ city: address?.city ?? null,
390
+ state: address?.state ?? null,
391
+ country,
392
+ loading,
393
+ editing,
394
+ startEditing,
395
+ cancel,
396
+ draft,
397
+ setDraft: updateDraft,
398
+ canSubmit,
399
+ pending,
400
+ errors,
401
+ invalid,
402
+ submit,
403
+ registerMask: setRegisteredMask
404
+ }), [
405
+ postalCode,
406
+ address?.city,
407
+ address?.state,
408
+ country,
409
+ loading,
410
+ editing,
411
+ startEditing,
412
+ cancel,
413
+ draft,
414
+ updateDraft,
415
+ canSubmit,
416
+ pending,
417
+ errors,
418
+ invalid,
419
+ submit
420
+ ]);
421
+ return /* @__PURE__ */ jsx(CartDestinationContextInternal, {
422
+ value,
423
+ children: /* @__PURE__ */ jsx("div", {
424
+ ...props,
425
+ ref: rootRef,
426
+ className,
427
+ "data-fs-cart-destination": "",
428
+ ...pending ? { "data-fs-cart-destination-pending": "" } : {},
429
+ ...editing ? { "data-fs-cart-destination-editing": "" } : {},
430
+ children
431
+ })
432
+ });
433
+ }
434
+ function CartDestinationRead({ className, children, ...props }) {
435
+ const { editing } = useCartDestination();
436
+ if (editing) return null;
437
+ return /* @__PURE__ */ jsx("div", {
438
+ ...props,
439
+ className,
440
+ "data-fs-cart-destination-read": "",
441
+ children
442
+ });
443
+ }
444
+ function pickAuthored(content, prop) {
445
+ if (content?.trim()) return content;
446
+ if (prop?.trim()) return prop;
447
+ return null;
448
+ }
449
+ function CartDestinationValue({ prefix, $content, className, ...props }) {
450
+ const { postalCode, city, state, loading } = useCartDestination();
451
+ if (loading || postalCode == null) return null;
452
+ const resolvedPrefix = pickAuthored($content?.prefix, prefix);
453
+ return /* @__PURE__ */ jsxs("span", {
454
+ ...props,
455
+ className,
456
+ "data-fs-cart-destination-value": "",
457
+ children: [
458
+ resolvedPrefix ? /* @__PURE__ */ jsx("span", {
459
+ "data-fs-cart-destination-value-prefix": "",
460
+ children: resolvedPrefix
461
+ }) : null,
462
+ resolvedPrefix ? " " : null,
463
+ /* @__PURE__ */ jsx("span", {
464
+ "data-fs-cart-destination-value-postal-code": "",
465
+ children: postalCode
466
+ }),
467
+ city || state ? " · " : null,
468
+ city ? /* @__PURE__ */ jsx("span", {
469
+ "data-fs-cart-destination-value-city": "",
470
+ children: city
471
+ }) : null,
472
+ city && state ? ", " : null,
473
+ state ? /* @__PURE__ */ jsx("span", {
474
+ "data-fs-cart-destination-value-state": "",
475
+ children: state
476
+ }) : null
477
+ ]
478
+ });
479
+ }
480
+ function CartDestinationUnset({ className, children, ...props }) {
481
+ const { postalCode, loading } = useCartDestination();
482
+ if (loading || postalCode != null) return null;
483
+ return /* @__PURE__ */ jsx("div", {
484
+ ...props,
485
+ className,
486
+ "data-fs-cart-destination-unset": "",
487
+ children
488
+ });
489
+ }
490
+ function CartDestinationChange({ label, $content, className, children, ...props }) {
491
+ const { editing, startEditing } = useCartDestination();
492
+ const resolvedLabel = pickAuthored($content?.label, label);
493
+ return /* @__PURE__ */ jsx("button", {
494
+ ...props,
495
+ type: "button",
496
+ onClick: startEditing,
497
+ "aria-expanded": editing,
498
+ className,
499
+ "data-fs-cart-destination-change": "",
500
+ children: children ?? resolvedLabel
501
+ });
502
+ }
503
+ function CartDestinationForm({ className, children, ...props }) {
504
+ const { editing, submit } = useCartDestination();
505
+ if (!editing) return null;
506
+ return /* @__PURE__ */ jsx(CartDestinationFormContext, {
507
+ value: true,
508
+ children: /* @__PURE__ */ jsx("form", {
509
+ ...props,
510
+ className,
511
+ "data-fs-cart-destination-form": "",
512
+ onKeyDown: (event) => {
513
+ if (event.key !== "Enter" || !(event.target instanceof HTMLInputElement)) return;
514
+ event.preventDefault();
515
+ submit();
516
+ },
517
+ onSubmit: (event) => {
518
+ event.preventDefault();
519
+ submit();
520
+ },
521
+ children
522
+ })
523
+ });
524
+ }
525
+ function CartDestinationPostalCodeControl({ className, mask, label, placeholder, invalidMessage }) {
526
+ const { country, draft, setDraft, errors, invalid, pending, registerMask } = useCartDestination();
527
+ const rootRef = useRef(null);
528
+ const refused = errors.length > 0;
529
+ const effectiveMask = mask ?? maskFor(country);
530
+ useEffect(() => {
531
+ registerMask(effectiveMask);
532
+ return () => registerMask(null);
533
+ }, [effectiveMask, registerMask]);
534
+ /**
535
+ * Disabling the input while the write is in flight drops focus to the body, so a refusal otherwise
536
+ * lands with nothing focused. Waits for `pending` to clear: focusing a disabled input is a no-op.
537
+ */
538
+ useEffect(() => {
539
+ if (!refused || pending) return;
540
+ rootRef.current?.querySelector("[data-fs-postal-code-field-input]")?.focus();
541
+ }, [refused, pending]);
542
+ return /* @__PURE__ */ jsx(PostalCodeField, {
543
+ ref: rootRef,
544
+ className,
545
+ "data-fs-cart-destination-postal-code-control": "",
546
+ mask: effectiveMask,
547
+ label,
548
+ placeholder,
549
+ errorMessage: refused ? void 0 : invalidMessage,
550
+ value: draft,
551
+ onValueChange: setDraft,
552
+ invalid: invalid || refused,
553
+ disabled: pending
554
+ });
555
+ }
556
+ function CartDestinationSubmit({ label, pendingLabel, $content, className, children, ...props }) {
557
+ const { canSubmit, pending, submit } = useCartDestination();
558
+ const inForm = useContext(CartDestinationFormContext);
559
+ const resolvedLabel = pickAuthored($content?.label, label);
560
+ const resolvedPendingLabel = pickAuthored($content?.pendingLabel, pendingLabel);
561
+ return /* @__PURE__ */ jsx("button", {
562
+ ...props,
563
+ type: inForm ? "submit" : "button",
564
+ onClick: inForm ? props.onClick : submit,
565
+ disabled: !canSubmit || props.disabled,
566
+ "aria-busy": pending || void 0,
567
+ className,
568
+ "data-fs-cart-destination-submit": "",
569
+ ...pending ? { "data-fs-cart-destination-submit-pending": "" } : {},
570
+ children: children ?? (pending ? resolvedPendingLabel ?? resolvedLabel : resolvedLabel)
571
+ });
572
+ }
573
+ function CartDestinationCancel({ label, $content, className, children, ...props }) {
574
+ const { cancel, pending } = useCartDestination();
575
+ const resolvedLabel = pickAuthored($content?.label, label);
576
+ return /* @__PURE__ */ jsx("button", {
577
+ ...props,
578
+ type: "button",
579
+ onClick: cancel,
580
+ disabled: pending || props.disabled,
581
+ className,
582
+ "data-fs-cart-destination-cancel": "",
583
+ children: children ?? resolvedLabel
584
+ });
585
+ }
586
+ function CartDestinationError({ genericMessage, $content, className, ...props }) {
587
+ const { errors } = useCartDestination();
588
+ if (errors.length === 0) return null;
589
+ const authored = pickAuthored($content?.genericMessage, genericMessage);
590
+ return /* @__PURE__ */ jsx("div", {
591
+ ...props,
592
+ role: "alert",
593
+ className,
594
+ "data-fs-cart-destination-error": "",
595
+ children: authored ?? errors[0]?.message
596
+ });
597
+ }
598
+ const CartDestination = {
599
+ Root: CartDestinationRoot,
600
+ Read: CartDestinationRead,
601
+ Value: CartDestinationValue,
602
+ Unset: CartDestinationUnset,
603
+ Change: CartDestinationChange,
604
+ Form: CartDestinationForm,
605
+ PostalCodeControl: CartDestinationPostalCodeControl,
606
+ Submit: CartDestinationSubmit,
607
+ Cancel: CartDestinationCancel,
608
+ Error: CartDestinationError
609
+ };
610
+ //#endregion
611
+ export { CartDestination, CartDestinationCancel, CartDestinationCancelContent, CartDestinationCancelDefinition, CartDestinationChange, CartDestinationChangeContent, CartDestinationChangeDefinition, CartDestinationError, CartDestinationErrorContent, CartDestinationErrorDefinition, CartDestinationForm, CartDestinationFormDefinition, CartDestinationPostalCodeControl, CartDestinationPostalCodeControlDefinition, CartDestinationRead, CartDestinationReadDefinition, CartDestinationRoot, CartDestinationRootDefinition, CartDestinationSubmit, CartDestinationSubmitContent, CartDestinationSubmitDefinition, CartDestinationUnset, CartDestinationUnsetDefinition, CartDestinationValue, CartDestinationValueContent, CartDestinationValueDefinition, useCartDestination };
612
+
613
+ //# sourceMappingURL=cart-destination.js.map