@vtex/faststore 0.0.2 → 0.0.4

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 (95) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +11 -5
  3. package/dist/catalog.json +556 -265
  4. package/dist/components.d.ts +3 -1
  5. package/dist/components.js +5 -3
  6. package/dist/content-types.json +16 -0
  7. package/dist/core/dist/actions/registry.d.ts +1 -1
  8. package/dist/core/dist/actions/registry.js +1 -1
  9. package/dist/core/dist/actions/registry.js.map +1 -1
  10. package/dist/docs/cart.md +2 -2
  11. package/dist/docs/my-account-profile.md +208 -0
  12. package/dist/docs/session.md +12 -6
  13. package/dist/docs/sign-in-redirect.md +137 -0
  14. package/dist/matter/access-code-sign-in/access-code-sign-in.d.ts +2 -2
  15. package/dist/matter/access-code-sign-in/access-code-sign-in.js +5 -2
  16. package/dist/matter/access-code-sign-in/access-code-sign-in.js.map +1 -1
  17. package/dist/matter/buy-button/buy-button.d.ts +1 -1
  18. package/dist/matter/buy-button/buy-button.js +3 -1
  19. package/dist/matter/buy-button/buy-button.js.map +1 -1
  20. package/dist/matter/cart/cart.d.ts +6 -6
  21. package/dist/matter/cart/cart.js +13 -6
  22. package/dist/matter/cart/cart.js.map +1 -1
  23. package/dist/matter/cart-item/cart-item.d.ts +2 -2
  24. package/dist/matter/cart-item/cart-item.js +5 -2
  25. package/dist/matter/cart-item/cart-item.js.map +1 -1
  26. package/dist/matter/client-preferences/client-preferences.js +3 -1
  27. package/dist/matter/client-preferences/client-preferences.js.map +1 -1
  28. package/dist/matter/client-profile/client-profile.d.ts +1 -1
  29. package/dist/matter/client-profile/client-profile.js +3 -1
  30. package/dist/matter/client-profile/client-profile.js.map +1 -1
  31. package/dist/matter/client-profile-form/client-profile-form.d.ts +1 -1
  32. package/dist/matter/client-profile-form/client-profile-form.js +3 -1
  33. package/dist/matter/client-profile-form/client-profile-form.js.map +1 -1
  34. package/dist/matter/delivery-options/delivery-options-accordion.d.ts +1 -1
  35. package/dist/matter/delivery-options/delivery-options-accordion.js +3 -1
  36. package/dist/matter/delivery-options/delivery-options-accordion.js.map +1 -1
  37. package/dist/matter/delivery-options/delivery-options-controller.d.ts +1 -1
  38. package/dist/matter/delivery-options/delivery-options-controller.js +3 -1
  39. package/dist/matter/delivery-options/delivery-options-controller.js.map +1 -1
  40. package/dist/matter/delivery-options/delivery-options.d.ts +1 -1
  41. package/dist/matter/delivery-options/delivery-options.js +3 -1
  42. package/dist/matter/delivery-options/delivery-options.js.map +1 -1
  43. package/dist/matter/email-identification/email-identification.d.ts +1 -1
  44. package/dist/matter/email-identification/email-identification.js +3 -1
  45. package/dist/matter/email-identification/email-identification.js.map +1 -1
  46. package/dist/matter/identity-summary/identity-summary.d.ts +1 -1
  47. package/dist/matter/identity-summary/identity-summary.js +3 -1
  48. package/dist/matter/identity-summary/identity-summary.js.map +1 -1
  49. package/dist/matter/my-account-profile/my-account-profile.d.ts +291 -0
  50. package/dist/matter/my-account-profile/my-account-profile.js +856 -0
  51. package/dist/matter/my-account-profile/my-account-profile.js.map +1 -0
  52. package/dist/matter/product-list/product-list.d.ts +1 -1
  53. package/dist/matter/product-list/product-list.js +3 -1
  54. package/dist/matter/product-list/product-list.js.map +1 -1
  55. package/dist/matter/region/region.d.ts +1 -1
  56. package/dist/matter/region/region.js +5 -2
  57. package/dist/matter/region/region.js.map +1 -1
  58. package/dist/matter/session/session.js +1 -1
  59. package/dist/matter/session/session.js.map +1 -1
  60. package/dist/matter/shipping-address/shipping-address.d.ts +1 -1
  61. package/dist/matter/shipping-address/shipping-address.js +4 -2
  62. package/dist/matter/shipping-address/shipping-address.js.map +1 -1
  63. package/dist/matter/sign-in-form/sign-in-form.d.ts +1 -1
  64. package/dist/matter/sign-in-form/sign-in-form.js +3 -1
  65. package/dist/matter/sign-in-form/sign-in-form.js.map +1 -1
  66. package/dist/matter/sign-in-redirect/sign-in-redirect.d.ts +45 -0
  67. package/dist/matter/sign-in-redirect/sign-in-redirect.js +127 -0
  68. package/dist/matter/sign-in-redirect/sign-in-redirect.js.map +1 -0
  69. package/dist/sdk/actions/actions-override-context.js +37 -0
  70. package/dist/sdk/actions/actions-override-context.js.map +1 -0
  71. package/dist/sdk/actions/cart-action-provider.d.ts +8 -6
  72. package/dist/sdk/actions/cart-action-provider.js +10 -8
  73. package/dist/sdk/actions/cart-action-provider.js.map +1 -1
  74. package/dist/sdk/actions/identity-action-provider.d.ts +2 -2
  75. package/dist/sdk/actions/identity-action-provider.js +4 -4
  76. package/dist/sdk/actions/identity-action-provider.js.map +1 -1
  77. package/dist/sdk/actions/inject-actions.d.ts +1 -5
  78. package/dist/sdk/actions/inject-actions.js +23 -21
  79. package/dist/sdk/actions/inject-actions.js.map +1 -1
  80. package/dist/sdk/actions/region-action-provider.d.ts +4 -3
  81. package/dist/sdk/actions/region-action-provider.js +6 -5
  82. package/dist/sdk/actions/region-action-provider.js.map +1 -1
  83. package/dist/sdk/actions/session-action-provider.d.ts +4 -3
  84. package/dist/sdk/actions/session-action-provider.js +6 -5
  85. package/dist/sdk/actions/session-action-provider.js.map +1 -1
  86. package/dist/sdk/actions/shipping-action-provider.d.ts +4 -3
  87. package/dist/sdk/actions/shipping-action-provider.js +6 -5
  88. package/dist/sdk/actions/shipping-action-provider.js.map +1 -1
  89. package/dist/sdk/cart/use-cart-actions.js.map +1 -1
  90. package/dist/sdk/client/create-client.js +24 -2
  91. package/dist/sdk/client/create-client.js.map +1 -1
  92. package/dist/sdk/content/create-content-runtime.js +1 -1
  93. package/dist/utils/safe-href.js +74 -4
  94. package/dist/utils/safe-href.js.map +1 -1
  95. package/package.json +1 -18
@@ -0,0 +1 @@
1
+ {"version":3,"file":"my-account-profile.js","names":[],"sources":["../../../src/matter/my-account-profile/my-account-profile.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport {\n type ChangeEvent,\n type ComponentPropsWithRef,\n createContext,\n type ReactNode,\n type SyntheticEvent,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport {\n DocumentField,\n isSupportedDocumentType,\n isValidDocument,\n maskDocumentValue,\n} from '../../particles/document-field/document-field.js';\nimport { Field } from '../../particles/field/field.js';\nimport { Input } from '../../particles/input/input.js';\nimport {\n applyPhoneMask,\n type PhoneCountry,\n PhoneField,\n toE164,\n validatePhoneNumber,\n} from '../../particles/phone-field/phone-field.js';\nimport { Select } from '../../particles/select/select.js';\nimport { useToast } from '../../particles/toast/toast.js';\nimport { useActionsOverride } from '../../sdk/actions/actions-override-context.js';\nimport type { ComponentActions } from '../../sdk/actions/types.js';\nimport { useDataOverride } from '../../sdk/data/data-override-context.js';\nimport type { CustomerProfileUserError } from '../../sdk/identity/operations.js';\nimport { useSession } from '../session/session.js';\n\n/**\n * The `my-account-profile` compound (specs/sfs-3287-my-account-profile-root.md) — the data root\n * plus form mechanics for the shopper's persistent account profile. `Root` owns the saved profile,\n * the draft, the view↔edit toggle, and the single batched Save; it publishes a context whose\n * registration API is the seam every field Control attaches to.\n *\n * Unlike `ClientProfileFormRoot`, the Root does not enumerate the field set: a Control registers\n * its key plus an optional validator and normalizer, and the Root derives which fields to validate,\n * which to send, and which to focus from the live registry only. A field absent from the\n * composition is absent from the payload by construction (Decision 1).\n */\n\n// ---------------------------------------------------------------------------\n// GraphQL fragment — raw literal, outside codegen (Decision 8)\n// ---------------------------------------------------------------------------\n\nconst fragment = /* GraphQL */ `\n fragment MyAccountProfileData on Query {\n customerProfile {\n __typename\n id\n firstName\n lastName\n email\n homePhone\n mobilePhone\n document\n documentType\n birthDate\n gender\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * The `$data` slice forwarded by the reactive runtime — the `customerProfile` entity itself, not the\n * `Query` shape the fragment above is authored against. The compiler derives an entity fragment `on\n * CustomerProfile` from the root-field selection for the reactive path (rfc-data-layer.md §\"Entity\n * fragments and schema introspection\"), and `ReactiveDataProvider` publishes that entity data\n * directly — the same convention `SessionRoot` reads `$data.loggedIn` under, not `$data.session`.\n */\nexport type MyAccountProfileData = {\n id?: string | null;\n firstName?: string | null;\n lastName?: string | null;\n email?: string | null;\n homePhone?: string | null;\n mobilePhone?: string | null;\n document?: string | null;\n documentType?: string | null;\n birthDate?: string | null;\n gender?: string | null;\n} | null;\n\n/**\n * The shopper-editable keys. `email` is absent (login identifier) and `documentType` is absent\n * (read for the tax-ID control's active type; not writable in V0).\n */\nexport type MyAccountProfileFieldKey =\n | 'firstName'\n | 'lastName'\n | 'homePhone'\n | 'mobilePhone'\n | 'document'\n | 'birthDate'\n | 'gender';\n\nconst FIELD_KEYS: readonly MyAccountProfileFieldKey[] = [\n 'firstName',\n 'lastName',\n 'homePhone',\n 'mobilePhone',\n 'document',\n 'birthDate',\n 'gender',\n];\n\nexport type MyAccountProfileMode = 'view' | 'edit';\n\n/** Every editable key, always a string — a `null` upstream value is seeded as `''`. */\nexport type MyAccountProfileValues = Readonly<Record<MyAccountProfileFieldKey, string>>;\n\n/**\n * What a Control hands the Root. `validate` returns a message when the value is unacceptable and\n * `null` when it is fine — requiredness is expressed as a validator that rejects `''`. `normalize`\n * is applied on Save, to dirty values only.\n */\nexport type MyAccountProfileFieldRegistration = Readonly<{\n key: MyAccountProfileFieldKey;\n validate?: (value: string) => string | null;\n normalize?: (value: string) => string;\n}>;\n\nexport type MyAccountProfileFieldErrors = Readonly<Partial<Record<MyAccountProfileFieldKey, string>>>;\n\nexport type MyAccountProfileContextValue = Readonly<{\n mode: MyAccountProfileMode;\n savedValues: MyAccountProfileValues;\n draft: MyAccountProfileValues;\n fieldErrors: MyAccountProfileFieldErrors;\n email: string;\n activeDocumentType: string | undefined;\n pending: boolean;\n registerField: (registration: MyAccountProfileFieldRegistration) => () => void;\n setField: (key: MyAccountProfileFieldKey, value: string) => void;\n edit: () => void;\n cancel: () => void;\n}>;\n\nexport type MyAccountProfileRootProps = Readonly<{\n /** Store-configured tax-ID type fallback, used when the saved `documentType` is not registered. */\n documentType?: string;\n saveFailedMessage?: string;\n savedMessage?: string;\n className?: string;\n children?: ReactNode;\n $data?: MyAccountProfileData;\n /** Compiler-injected action surface; carries `identity.customerProfileUpdate`. */\n $actions?: ComponentActions;\n}>;\n\nexport type MyAccountProfileEditButtonProps = Readonly<{ label?: string; className?: string; children?: ReactNode }>;\nexport type MyAccountProfileSaveButtonProps = Readonly<{ label?: string; className?: string; children?: ReactNode }>;\nexport type MyAccountProfileCancelButtonProps = Readonly<{\n label?: string;\n className?: string;\n children?: ReactNode;\n}>;\nexport type MyAccountProfileEmailProps = Readonly<{ className?: string }>;\n\nconst DEFAULT_SAVE_FAILED_MESSAGE = 'Unable to save your profile. Please try again.';\nconst DEFAULT_SAVED_MESSAGE = 'Your profile has been saved.';\n\nfunction buildSavedValues($data: MyAccountProfileData | undefined): MyAccountProfileValues {\n return {\n firstName: $data?.firstName ?? '',\n lastName: $data?.lastName ?? '',\n homePhone: $data?.homePhone ?? '',\n mobilePhone: $data?.mobilePhone ?? '',\n document: $data?.document ?? '',\n birthDate: $data?.birthDate ?? '',\n gender: $data?.gender ?? '',\n };\n}\n\n/**\n * Native `<input type=\"date\">` values and the profile's stored `birthDate` are both `yyyy-mm-dd`.\n * `toNativeDate` stays defensive against a `yyyy-mm-ddTHH:mm:ss...` upstream value slipping through\n * unnormalized — the resolver already strips the time component, this is a second line of defense —\n * since the native input rejects anything but the date-only form outright.\n */\nfunction toNativeDate(stored: string): string {\n const [datePart] = stored.split('T');\n return datePart ?? '';\n}\nfunction toStoredDate(native: string): string {\n return native;\n}\n\nconst DEFAULT_GENDER_OPTIONS: readonly MyAccountProfileGenderOption[] = [\n { value: 'female', label: 'Female' },\n { value: 'male', label: 'Male' },\n { value: 'other', label: 'Other' },\n { value: 'prefer_not_to_say', label: 'Prefer not to say' },\n];\n\n// ---------------------------------------------------------------------------\n// Context — Root publishes the draft + registration API to its Controls\n// ---------------------------------------------------------------------------\n\nconst MY_ACCOUNT_PROFILE_UNSET = Symbol('MyAccountProfileContextUnset');\n\nconst MyAccountProfileContext = createContext<MyAccountProfileContextValue | typeof MY_ACCOUNT_PROFILE_UNSET>(\n MY_ACCOUNT_PROFILE_UNSET,\n);\n\n/** Throws when used outside `MyAccountProfileRoot`, per ADR-0009. */\nexport function useMyAccountProfile(): MyAccountProfileContextValue {\n const ctx = useContext(MyAccountProfileContext);\n if (ctx === MY_ACCOUNT_PROFILE_UNSET) {\n throw new Error('MyAccountProfile components must be used inside MyAccountProfileRoot.');\n }\n return ctx;\n}\n\n// ---------------------------------------------------------------------------\n// Root\n// ---------------------------------------------------------------------------\n\nexport const MyAccountProfileRootDefinition = defineComponent<MyAccountProfileRootProps>({\n name: 'MyAccountProfileRoot',\n importPath: '@vtex/faststore/components',\n component: MyAccountProfileRoot,\n fragment,\n fetchStrategy: 'reactive',\n requiresContext: 'session',\n providesContext: 'my-account-profile',\n actions: { identity: ['customerProfileUpdate'] },\n skeleton: MyAccountProfileSkeleton,\n editor: { label: 'My Account Profile Root', category: 'identity' },\n schema: {\n documentType: { type: 'string', label: 'Default document type (e.g. CPF)' },\n saveFailedMessage: {\n type: 'string',\n default: DEFAULT_SAVE_FAILED_MESSAGE,\n label: 'Save failure message',\n },\n savedMessage: {\n type: 'string',\n default: DEFAULT_SAVED_MESSAGE,\n label: 'Save success message',\n },\n },\n guideline:\n 'Data root + form mechanics for the persistent My Account profile: owns the saved profile, the draft, the view/edit toggle, and the single batched Save (dispatches only dirty, registered fields; an empty string erases, an omitted field is untouched). Self-gates on the authenticated (loggedIn) session; requires a SessionRoot ancestor and a mounted ToastProvider ancestor — Save success/failure surface as a toast, not an inline part. Field Controls register their key plus an optional validator and normalizer through useMyAccountProfile() — the field set is a merchant composition decision, never enumerated here. Compose EditButton (view state), SaveButton/CancelButton (edit state), and Email as children in any order. See my-account-profile.md.',\n});\n\nexport function MyAccountProfileRoot({\n $data: $dataProp,\n $actions: $actionsProp,\n documentType: documentTypeProp,\n saveFailedMessage = DEFAULT_SAVE_FAILED_MESSAGE,\n savedMessage = DEFAULT_SAVED_MESSAGE,\n className,\n children,\n}: MyAccountProfileRootProps) {\n // Enforces the SessionRoot ancestor: useSession() throws when rendered outside one.\n const { state } = useSession();\n // Enforces the ToastProvider ancestor: useToast() throws when rendered outside one (Decision 10).\n const toast = useToast();\n const $data = useDataOverride($dataProp);\n const $actions = useActionsOverride($actionsProp);\n const resolved = $data != null;\n\n const savedValues = useMemo(() => buildSavedValues($data), [$data]);\n const savedEmail = $data?.email ?? '';\n const savedDocumentType = $data?.documentType?.toUpperCase();\n const activeDocumentType =\n savedDocumentType && isSupportedDocumentType(savedDocumentType) ? savedDocumentType : documentTypeProp;\n\n const [mode, setMode] = useState<MyAccountProfileMode>('view');\n const [draft, setDraft] = useState<MyAccountProfileValues>(savedValues);\n const [fieldErrors, setFieldErrors] = useState<MyAccountProfileFieldErrors>({});\n const [pending, setPending] = useState(false);\n const [registry, setRegistry] = useState<ReadonlyMap<MyAccountProfileFieldKey, MyAccountProfileFieldRegistration>>(\n () => new Map(),\n );\n\n const formRef = useRef<HTMLFormElement>(null);\n\n // Re-seed pristine fields when the saved profile changes underneath an open edit; a field the\n // shopper has touched (draft no longer equals the previous saved value) is never clobbered. Runs\n // during render (previous-saved in state, not a ref) so the re-seed lands in the same commit.\n const [prevSaved, setPrevSaved] = useState(savedValues);\n const changedKeys = FIELD_KEYS.filter((key) => prevSaved[key] !== savedValues[key]);\n if (changedKeys.length > 0) {\n setPrevSaved(savedValues);\n setDraft((prevDraft) => {\n let next = prevDraft;\n for (const key of changedKeys) {\n if (prevDraft[key] !== prevSaved[key]) continue;\n if (next === prevDraft) next = { ...prevDraft };\n (next as Record<MyAccountProfileFieldKey, string>)[key] = savedValues[key];\n }\n return next;\n });\n }\n\n const registerField = useCallback((registration: MyAccountProfileFieldRegistration) => {\n setRegistry((prev) => {\n const next = new Map(prev);\n next.set(registration.key, registration);\n return next;\n });\n return () => {\n setRegistry((prev) => {\n if (!prev.has(registration.key)) return prev;\n const next = new Map(prev);\n next.delete(registration.key);\n return next;\n });\n };\n }, []);\n\n const setField = useCallback((key: MyAccountProfileFieldKey, value: string) => {\n setDraft((prev) => ({ ...prev, [key]: value }));\n setFieldErrors((prev) => {\n if (!prev[key]) return prev;\n const next = { ...prev };\n delete next[key];\n return next;\n });\n }, []);\n\n // Opening an edit always starts from the latest truth, so a value the server normalized (e.g. a\n // reformatted phone) on a previous save is what the shopper sees, not their own stale keystrokes.\n const edit = useCallback(() => {\n setDraft(savedValues);\n setFieldErrors({});\n setMode('edit');\n }, [savedValues]);\n\n const cancel = useCallback(() => {\n setDraft(savedValues);\n setFieldErrors({});\n setMode('view');\n }, [savedValues]);\n\n function focusFirstInvalid(errors: MyAccountProfileFieldErrors) {\n const wrappers = formRef.current?.querySelectorAll<HTMLElement>('[data-fs-my-account-profile-field]');\n if (!wrappers) return;\n for (const wrapper of wrappers) {\n const key = wrapper.dataset.fsMyAccountProfileField as MyAccountProfileFieldKey | undefined;\n if (key && errors[key]) {\n const target = wrapper.querySelector<HTMLElement>('input, select, textarea') ?? wrapper;\n target.focus();\n return;\n }\n }\n }\n\n async function handleSubmit(event: SyntheticEvent<HTMLFormElement>) {\n event.preventDefault();\n if (pending) return;\n\n const dirtyKeys = Array.from(registry.keys()).filter((key) => draft[key] !== savedValues[key]);\n if (dirtyKeys.length === 0) {\n setFieldErrors({});\n setMode('view');\n toast.add({ title: savedMessage, type: 'success' });\n return;\n }\n\n // Validate only the fields the shopper actually changed (Decision 9, product-confirmed): a\n // stale invalid value the shopper never touched must not block an unrelated field's save.\n const errors: Record<string, string> = {};\n for (const key of dirtyKeys) {\n const registration = registry.get(key);\n const message = registration?.validate?.(draft[key]);\n if (message) errors[key] = message;\n }\n if (Object.keys(errors).length > 0) {\n setFieldErrors(errors as MyAccountProfileFieldErrors);\n focusFirstInvalid(errors as MyAccountProfileFieldErrors);\n return;\n }\n setFieldErrors({});\n\n const identity = $actions?.identity;\n if (!identity?.customerProfileUpdate) {\n // A composition/platform gap (the provider chain never delivered customerProfileUpdate) is\n // otherwise indistinguishable from a dead button — surface it the same way a rejected write\n // would, rather than returning silently.\n toast.add({ title: saveFailedMessage, type: 'error' });\n return;\n }\n\n const payload: Partial<Record<MyAccountProfileFieldKey, string>> = {};\n for (const key of dirtyKeys) {\n const registration = registry.get(key);\n const rawValue = draft[key];\n payload[key] = registration?.normalize ? registration.normalize(rawValue) : rawValue;\n }\n\n setPending(true);\n try {\n const userErrors: readonly CustomerProfileUserError[] = await identity.customerProfileUpdate(payload);\n if (userErrors.length > 0) {\n toast.add({ title: userErrors[0]?.message ?? saveFailedMessage, type: 'error' });\n return;\n }\n setMode('view');\n toast.add({ title: savedMessage, type: 'success' });\n } catch (error) {\n // Log only the error name — the message can echo a submitted profile value (PII).\n console.error(\n '[MyAccountProfileRoot] customerProfileUpdate failed',\n error instanceof Error ? error.name : 'UnknownError',\n );\n toast.add({ title: saveFailedMessage, type: 'error' });\n } finally {\n setPending(false);\n }\n }\n\n const contextValue = useMemo<MyAccountProfileContextValue>(\n () => ({\n mode,\n savedValues,\n draft,\n fieldErrors,\n email: savedEmail,\n activeDocumentType,\n pending,\n registerField,\n setField,\n edit,\n cancel,\n }),\n [\n mode,\n savedValues,\n draft,\n fieldErrors,\n savedEmail,\n activeDocumentType,\n pending,\n registerField,\n setField,\n edit,\n cancel,\n ],\n );\n\n // Self-gate: an anonymous or identified-but-not-authenticated session never reaches the DOM,\n // even if the topology's SessionGuard is missing (US-5).\n if (state !== 'loggedIn') return null;\n\n // Empty is a post-resolution state — the skeleton renders while $data is unresolved, never an\n // empty summary.\n if (!resolved) return <MyAccountProfileSkeleton className={className} />;\n\n return (\n <MyAccountProfileContext value={contextValue}>\n <form\n ref={formRef}\n onSubmit={handleSubmit}\n className={className}\n data-fs-my-account-profile=\"\"\n data-fs-my-account-profile-mode={mode}\n {...(pending ? { 'data-fs-my-account-profile-pending': '' } : {})}\n >\n {children}\n </form>\n </MyAccountProfileContext>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Frame parts\n// ---------------------------------------------------------------------------\n\nexport const MyAccountProfileEditButtonDefinition = defineComponent<MyAccountProfileEditButtonProps>({\n name: 'MyAccountProfileEditButton',\n importPath: '@vtex/faststore/components',\n component: MyAccountProfileEditButton,\n requiresContext: 'my-account-profile',\n editor: { label: 'My Account Profile Edit Button', category: 'identity' },\n schema: {\n label: { type: 'string', default: 'Edit', label: 'Button label' },\n },\n guideline:\n 'Switches MyAccountProfileRoot from the view state to the edit state. Renders only in the view state. Must be a descendant of MyAccountProfileRoot.',\n});\n\nexport function MyAccountProfileEditButton({ label = 'Edit', className, children }: MyAccountProfileEditButtonProps) {\n const { mode, edit } = useMyAccountProfile();\n if (mode !== 'view') return null;\n return (\n <button type=\"button\" onClick={edit} className={className} data-fs-my-account-profile-edit=\"\">\n {children ?? label}\n </button>\n );\n}\n\nexport const MyAccountProfileSaveButtonDefinition = defineComponent<MyAccountProfileSaveButtonProps>({\n name: 'MyAccountProfileSaveButton',\n importPath: '@vtex/faststore/components',\n component: MyAccountProfileSaveButton,\n requiresContext: 'my-account-profile',\n editor: { label: 'My Account Profile Save Button', category: 'identity' },\n schema: {\n label: { type: 'string', default: 'Save', label: 'Button label' },\n },\n guideline:\n \"Submits MyAccountProfileRoot's batched Save. Renders only in the edit state; disabled while the write is pending. Must be a descendant of MyAccountProfileRoot.\",\n});\n\nexport function MyAccountProfileSaveButton({ label = 'Save', className, children }: MyAccountProfileSaveButtonProps) {\n const { mode, pending } = useMyAccountProfile();\n if (mode !== 'edit') return null;\n return (\n <button type=\"submit\" disabled={pending} className={className} data-fs-my-account-profile-save=\"\">\n {children ?? label}\n </button>\n );\n}\n\nexport const MyAccountProfileCancelButtonDefinition = defineComponent<MyAccountProfileCancelButtonProps>({\n name: 'MyAccountProfileCancelButton',\n importPath: '@vtex/faststore/components',\n component: MyAccountProfileCancelButton,\n requiresContext: 'my-account-profile',\n editor: { label: 'My Account Profile Cancel Button', category: 'identity' },\n schema: {\n label: { type: 'string', default: 'Cancel', label: 'Button label' },\n },\n guideline:\n 'Reverts every field to its last saved value and returns MyAccountProfileRoot to the view state, without persisting. Renders only in the edit state; disabled while the write is pending. Must be a descendant of MyAccountProfileRoot.',\n});\n\nexport function MyAccountProfileCancelButton({\n label = 'Cancel',\n className,\n children,\n}: MyAccountProfileCancelButtonProps) {\n const { mode, pending, cancel } = useMyAccountProfile();\n if (mode !== 'edit') return null;\n return (\n <button\n type=\"button\"\n disabled={pending}\n onClick={cancel}\n className={className}\n data-fs-my-account-profile-cancel=\"\"\n >\n {children ?? label}\n </button>\n );\n}\n\nexport const MyAccountProfileEmailDefinition = defineComponent<MyAccountProfileEmailProps>({\n name: 'MyAccountProfileEmail',\n importPath: '@vtex/faststore/components',\n component: MyAccountProfileEmail,\n requiresContext: 'my-account-profile',\n editor: { label: 'My Account Profile Email', category: 'identity' },\n schema: {},\n guideline:\n \"The shopper's read-only login identifier, or nothing when absent. Never editable — no edit-state rendering exists for it. Must be a descendant of MyAccountProfileRoot.\",\n});\n\nexport function MyAccountProfileEmail({ className }: MyAccountProfileEmailProps) {\n const { email } = useMyAccountProfile();\n if (!email) return null;\n return (\n <p className={className} data-fs-my-account-profile-email=\"\">\n {email}\n </p>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Skeleton — no context: it is rendered directly by the Root before any Provider exists\n// (mirrors ProductShelfSkeleton / PricingSkeleton; see spec deviation note).\n// ---------------------------------------------------------------------------\n\nexport const MyAccountProfileSkeletonDefinition = defineComponent<ComponentPropsWithRef<'div'>>({\n name: 'MyAccountProfileSkeleton',\n importPath: '@vtex/faststore/components',\n component: MyAccountProfileSkeleton,\n editor: { label: 'My Account Profile Skeleton', category: 'identity' },\n schema: {},\n guideline:\n 'Loading placeholder rendered by MyAccountProfileRoot while $data is unresolved. Declares no context requirement — it renders before the Root has anything to provide.',\n});\n\nexport function MyAccountProfileSkeleton({ className, ...props }: ComponentPropsWithRef<'div'>) {\n return (\n <div {...props} className={className} data-fs-my-account-profile-skeleton=\"\" aria-hidden=\"true\">\n <span data-fs-my-account-profile-skeleton-line=\"\" />\n <span data-fs-my-account-profile-skeleton-line=\"\" />\n <span data-fs-my-account-profile-skeleton-line=\"\" />\n </div>\n );\n}\n\nMyAccountProfileRoot.Skeleton = MyAccountProfileSkeleton;\n\n// ---------------------------------------------------------------------------\n// Field Controls\n//\n// Each Control is a thin binding over an existing presentational particle: it registers with the\n// Root's registration seam in an effect, reads its value off `mode` (savedValues in view, draft in\n// edit), writes through `setField`, and wraps its input in an element carrying\n// `data-fs-my-account-profile-field` — the Root's DOM-order focus seam. None persists on its own.\n// ---------------------------------------------------------------------------\n\nexport type MyAccountProfileFirstNameControlProps = Readonly<{\n label?: string;\n requiredMessage?: string;\n className?: string;\n}>;\n\nexport const MyAccountProfileFirstNameControlDefinition = defineComponent<MyAccountProfileFirstNameControlProps>({\n name: 'MyAccountProfileFirstNameControl',\n importPath: '@vtex/faststore/components',\n component: MyAccountProfileFirstNameControl,\n requiresContext: 'my-account-profile',\n editor: { label: 'My Account Profile First Name Control', category: 'identity' },\n schema: {\n label: { type: 'string', default: 'First name', label: 'Label' },\n requiredMessage: { type: 'string', default: 'First name is required.', label: 'Required message' },\n },\n guideline:\n 'Draft-bound, required first-name field. Blocks Save with an inline message and focus when left empty. Must be a descendant of MyAccountProfileRoot.',\n});\n\nexport function MyAccountProfileFirstNameControl({\n label = 'First name',\n requiredMessage = 'First name is required.',\n className,\n}: MyAccountProfileFirstNameControlProps) {\n const { mode, draft, savedValues, fieldErrors, pending, registerField, setField } = useMyAccountProfile();\n\n useEffect(() => {\n return registerField({ key: 'firstName', validate: (value) => (value.trim() ? null : requiredMessage) });\n }, [requiredMessage, registerField]);\n\n const error = fieldErrors.firstName;\n\n if (mode === 'view') {\n return (\n <p className={className} data-fs-my-account-profile-first-name=\"\" data-fs-my-account-profile-field=\"firstName\">\n {savedValues.firstName}\n </p>\n );\n }\n\n return (\n <Field.Root\n className={className}\n invalid={!!error}\n disabled={pending}\n data-fs-my-account-profile-first-name=\"\"\n data-fs-my-account-profile-field=\"firstName\"\n >\n <Field.Label>{label}</Field.Label>\n <Field.Control\n value={draft.firstName}\n onChange={(event) => setField('firstName', event.target.value)}\n disabled={pending}\n />\n {error ? <Field.Error match>{error}</Field.Error> : null}\n </Field.Root>\n );\n}\n\nexport type MyAccountProfileLastNameControlProps = Readonly<{\n label?: string;\n requiredMessage?: string;\n className?: string;\n}>;\n\nexport const MyAccountProfileLastNameControlDefinition = defineComponent<MyAccountProfileLastNameControlProps>({\n name: 'MyAccountProfileLastNameControl',\n importPath: '@vtex/faststore/components',\n component: MyAccountProfileLastNameControl,\n requiresContext: 'my-account-profile',\n editor: { label: 'My Account Profile Last Name Control', category: 'identity' },\n schema: {\n label: { type: 'string', default: 'Last name', label: 'Label' },\n requiredMessage: { type: 'string', default: 'Last name is required.', label: 'Required message' },\n },\n guideline:\n 'Draft-bound, required last-name field. Blocks Save with an inline message and focus when left empty. Must be a descendant of MyAccountProfileRoot.',\n});\n\nexport function MyAccountProfileLastNameControl({\n label = 'Last name',\n requiredMessage = 'Last name is required.',\n className,\n}: MyAccountProfileLastNameControlProps) {\n const { mode, draft, savedValues, fieldErrors, pending, registerField, setField } = useMyAccountProfile();\n\n useEffect(() => {\n return registerField({ key: 'lastName', validate: (value) => (value.trim() ? null : requiredMessage) });\n }, [requiredMessage, registerField]);\n\n const error = fieldErrors.lastName;\n\n if (mode === 'view') {\n return (\n <p className={className} data-fs-my-account-profile-last-name=\"\" data-fs-my-account-profile-field=\"lastName\">\n {savedValues.lastName}\n </p>\n );\n }\n\n return (\n <Field.Root\n className={className}\n invalid={!!error}\n disabled={pending}\n data-fs-my-account-profile-last-name=\"\"\n data-fs-my-account-profile-field=\"lastName\"\n >\n <Field.Label>{label}</Field.Label>\n <Field.Control\n value={draft.lastName}\n onChange={(event) => setField('lastName', event.target.value)}\n disabled={pending}\n />\n {error ? <Field.Error match>{error}</Field.Error> : null}\n </Field.Root>\n );\n}\n\nexport type MyAccountProfilePhoneControlProps = Readonly<{\n label?: string;\n /** Which draft key this instance binds. Compose the Control twice to expose both numbers. */\n field?: 'homePhone' | 'mobilePhone';\n country?: PhoneCountry;\n invalidMessage?: string;\n className?: string;\n}>;\n\nexport const MyAccountProfilePhoneControlDefinition = defineComponent<MyAccountProfilePhoneControlProps>({\n name: 'MyAccountProfilePhoneControl',\n importPath: '@vtex/faststore/components',\n component: MyAccountProfilePhoneControl,\n requiresContext: 'my-account-profile',\n editor: { label: 'My Account Profile Phone Control', category: 'identity' },\n schema: {\n label: { type: 'string', default: 'Phone', label: 'Label' },\n field: { type: ['homePhone', 'mobilePhone'] as const, default: 'mobilePhone', label: 'Bound field' },\n country: { type: ['US', 'BR'] as const, default: 'US', label: 'Country' },\n invalidMessage: { type: 'string', default: 'Invalid phone number', label: 'Invalid message' },\n },\n guideline:\n \"Draft-bound, optional phone field bound to either homePhone or mobilePhone via the field prop (Decision 1) — compose it twice to expose both numbers. Normalizes to E.164 on the Root's Save. Absent from the composition means absent from the payload. Must be a descendant of MyAccountProfileRoot.\",\n});\n\nexport function MyAccountProfilePhoneControl({\n label = 'Phone',\n field = 'mobilePhone',\n country = 'US',\n invalidMessage = 'Invalid phone number',\n className,\n}: MyAccountProfilePhoneControlProps) {\n const { mode, draft, savedValues, fieldErrors, pending, registerField, setField } = useMyAccountProfile();\n\n useEffect(() => {\n return registerField({\n key: field,\n validate: (value) => (value.trim() && !validatePhoneNumber(value, country) ? invalidMessage : null),\n normalize: (value) => (value.trim() ? toE164(value, country) : value),\n });\n }, [field, country, invalidMessage, registerField]);\n\n const error = fieldErrors[field];\n\n if (mode === 'view') {\n return (\n <p className={className} data-fs-my-account-profile-phone={field} data-fs-my-account-profile-field={field}>\n {savedValues[field] ? applyPhoneMask(savedValues[field], country) : ''}\n </p>\n );\n }\n\n return (\n <div data-fs-my-account-profile-field={field}>\n <PhoneField\n className={className}\n label={label}\n country={country}\n requirement=\"optional\"\n value={draft[field]}\n onValueChange={(value) => setField(field, value)}\n invalid={!!error}\n error={error ?? undefined}\n errorInvalid={invalidMessage}\n isDisabled={pending}\n data-fs-my-account-profile-phone={field}\n />\n </div>\n );\n}\n\nexport type MyAccountProfileTaxIdControlProps = Readonly<{\n label?: string;\n invalidMessage?: string;\n /** Inline reason shown beside the locked saved CPF. */\n lockedReason?: string;\n className?: string;\n}>;\n\nexport const MyAccountProfileTaxIdControlDefinition = defineComponent<MyAccountProfileTaxIdControlProps>({\n name: 'MyAccountProfileTaxIdControl',\n importPath: '@vtex/faststore/components',\n component: MyAccountProfileTaxIdControl,\n requiresContext: 'my-account-profile',\n editor: { label: 'My Account Profile Tax ID Control', category: 'identity' },\n schema: {\n label: { type: 'string', default: 'National tax ID', label: 'Label' },\n invalidMessage: { type: 'string', default: 'Invalid document number.', label: 'Invalid message' },\n lockedReason: { type: 'string', default: \"Your document ID can't be edited.\", label: 'Locked reason' },\n },\n guideline:\n \"Draft-bound, optional national tax-ID field (CPF in V0). Self-gates on the Root's active document type. A saved value renders as a disabled, locked input in the edit state rather than disappearing (Decision 4); an unsaved value renders as an editable field. Must be a descendant of MyAccountProfileRoot.\",\n});\n\nexport function MyAccountProfileTaxIdControl({\n label = 'National tax ID',\n invalidMessage = 'Invalid document number.',\n lockedReason = \"Your document ID can't be edited.\",\n className,\n}: MyAccountProfileTaxIdControlProps) {\n const { mode, draft, savedValues, fieldErrors, pending, activeDocumentType, registerField, setField } =\n useMyAccountProfile();\n const documentType =\n activeDocumentType && isSupportedDocumentType(activeDocumentType) ? activeDocumentType : undefined;\n const hasSavedDocument = savedValues.document.trim() !== '';\n\n useEffect(() => {\n if (!documentType || hasSavedDocument) return;\n return registerField({\n key: 'document',\n validate: (value) => (value.trim() && !isValidDocument(value, documentType) ? invalidMessage : null),\n // DocumentField reports back the masked value (e.g. `111.444.777-35`); strip the punctuation\n // before it reaches the API, matching the checkout form's `stripNonDigits`.\n normalize: (value) => value.replaceAll(/\\D/g, ''),\n });\n }, [documentType, hasSavedDocument, invalidMessage, registerField]);\n\n if (!documentType) return null;\n\n const error = fieldErrors.document;\n\n if (mode === 'view') {\n return (\n <p className={className} data-fs-my-account-profile-tax-id=\"\" data-fs-my-account-profile-field=\"document\">\n {savedValues.document ? maskDocumentValue(savedValues.document, documentType) : ''}\n </p>\n );\n }\n\n if (hasSavedDocument) {\n return (\n <Field.Root\n className={className}\n data-fs-my-account-profile-tax-id=\"\"\n data-fs-my-account-profile-field=\"document\"\n >\n <Field.Label>{label}</Field.Label>\n <Field.Control\n readOnly\n aria-disabled=\"true\"\n title={lockedReason}\n value={maskDocumentValue(savedValues.document, documentType)}\n data-fs-my-account-profile-tax-id-readonly=\"\"\n />\n <Field.Description>{lockedReason}</Field.Description>\n </Field.Root>\n );\n }\n\n return (\n <DocumentField\n className={className}\n label={label}\n documentType={documentType}\n value={draft.document}\n onValueChange={(value) => setField('document', value)}\n invalid={!!error}\n errorMessage={error}\n disabled={pending}\n data-fs-my-account-profile-tax-id=\"\"\n data-fs-my-account-profile-field=\"document\"\n />\n );\n}\n\nexport type MyAccountProfileBirthDateControlProps = Readonly<{ label?: string; className?: string }>;\n\nexport const MyAccountProfileBirthDateControlDefinition = defineComponent<MyAccountProfileBirthDateControlProps>({\n name: 'MyAccountProfileBirthDateControl',\n importPath: '@vtex/faststore/components',\n component: MyAccountProfileBirthDateControl,\n requiresContext: 'my-account-profile',\n editor: { label: 'My Account Profile Birth Date Control', category: 'identity' },\n schema: {\n label: { type: 'string', default: 'Birth date', label: 'Label' },\n },\n guideline:\n 'Draft-bound, optional birth-date field over a native date input. Maps the native yyyy-mm-dd value to the stored profile format on read and write. Must be a descendant of MyAccountProfileRoot.',\n});\n\nexport function MyAccountProfileBirthDateControl({\n label = 'Birth date',\n className,\n}: MyAccountProfileBirthDateControlProps) {\n const { mode, draft, savedValues, pending, registerField, setField } = useMyAccountProfile();\n\n useEffect(() => {\n return registerField({ key: 'birthDate', normalize: (value) => toStoredDate(value) });\n }, [registerField]);\n\n if (mode === 'view') {\n return (\n <p className={className} data-fs-my-account-profile-birth-date=\"\" data-fs-my-account-profile-field=\"birthDate\">\n {savedValues.birthDate}\n </p>\n );\n }\n\n function handleChange(event: ChangeEvent<HTMLInputElement>) {\n setField('birthDate', toStoredDate(event.target.value));\n }\n\n return (\n <Field.Root\n className={className}\n data-fs-my-account-profile-birth-date=\"\"\n data-fs-my-account-profile-field=\"birthDate\"\n >\n <Field.Label>{label}</Field.Label>\n <Input type=\"date\" value={toNativeDate(draft.birthDate)} onChange={handleChange} disabled={pending} />\n </Field.Root>\n );\n}\n\nexport type MyAccountProfileGenderOption = Readonly<{ value: string; label: string }>;\n\nexport type MyAccountProfileGenderControlProps = Readonly<{\n label?: string;\n placeholder?: string;\n clearLabel?: string;\n options?: readonly MyAccountProfileGenderOption[];\n className?: string;\n}>;\n\nexport const MyAccountProfileGenderControlDefinition = defineComponent<MyAccountProfileGenderControlProps>({\n name: 'MyAccountProfileGenderControl',\n importPath: '@vtex/faststore/components',\n component: MyAccountProfileGenderControl,\n requiresContext: 'my-account-profile',\n editor: { label: 'My Account Profile Gender Control', category: 'identity' },\n schema: {\n label: { type: 'string', default: 'Gender', label: 'Label' },\n placeholder: { type: 'string', default: 'Select', label: 'Placeholder' },\n clearLabel: { type: 'string', default: 'Clear', label: 'Clear button label' },\n },\n guideline:\n 'Draft-bound, optional gender field over a store-configurable set of options (the options prop). Free-string storage — no server-side enum. A Clear button erases a selected value back to empty, since the Select itself offers no such option. Must be a descendant of MyAccountProfileRoot.',\n});\n\nexport function MyAccountProfileGenderControl({\n label = 'Gender',\n placeholder = 'Select',\n clearLabel = 'Clear',\n options = DEFAULT_GENDER_OPTIONS,\n className,\n}: MyAccountProfileGenderControlProps) {\n const { mode, draft, savedValues, pending, registerField, setField } = useMyAccountProfile();\n\n useEffect(() => {\n return registerField({ key: 'gender' });\n }, [registerField]);\n\n if (mode === 'view') {\n const selected = options.find((option) => option.value === savedValues.gender);\n return (\n <p className={className} data-fs-my-account-profile-gender=\"\" data-fs-my-account-profile-field=\"gender\">\n {selected?.label ?? savedValues.gender}\n </p>\n );\n }\n\n return (\n <div className={className} data-fs-my-account-profile-gender=\"\" data-fs-my-account-profile-field=\"gender\">\n <Select.Root value={draft.gender} onValueChange={(value) => setField('gender', value ?? '')} disabled={pending}>\n <Select.Label>{label}</Select.Label>\n <Select.Trigger>\n <Select.Value placeholder={placeholder} />\n <Select.Icon />\n </Select.Trigger>\n <Select.Portal>\n <Select.Positioner>\n <Select.Popup>\n <Select.List>\n {options.map((option) => (\n <Select.Item key={option.value} value={option.value}>\n <Select.ItemText>{option.label}</Select.ItemText>\n </Select.Item>\n ))}\n </Select.List>\n </Select.Popup>\n </Select.Positioner>\n </Select.Portal>\n </Select.Root>\n {draft.gender ? (\n <button\n type=\"button\"\n onClick={() => setField('gender', '')}\n disabled={pending}\n data-fs-my-account-profile-gender-clear=\"\"\n >\n {clearLabel}\n </button>\n ) : null}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Namespace export\n// ---------------------------------------------------------------------------\n\nexport const MyAccountProfile = {\n Root: MyAccountProfileRoot,\n RootDefinition: MyAccountProfileRootDefinition,\n EditButton: MyAccountProfileEditButton,\n EditButtonDefinition: MyAccountProfileEditButtonDefinition,\n SaveButton: MyAccountProfileSaveButton,\n SaveButtonDefinition: MyAccountProfileSaveButtonDefinition,\n CancelButton: MyAccountProfileCancelButton,\n CancelButtonDefinition: MyAccountProfileCancelButtonDefinition,\n Email: MyAccountProfileEmail,\n EmailDefinition: MyAccountProfileEmailDefinition,\n Skeleton: MyAccountProfileSkeleton,\n SkeletonDefinition: MyAccountProfileSkeletonDefinition,\n FirstNameControl: MyAccountProfileFirstNameControl,\n FirstNameControlDefinition: MyAccountProfileFirstNameControlDefinition,\n LastNameControl: MyAccountProfileLastNameControl,\n LastNameControlDefinition: MyAccountProfileLastNameControlDefinition,\n PhoneControl: MyAccountProfilePhoneControl,\n PhoneControlDefinition: MyAccountProfilePhoneControlDefinition,\n TaxIdControl: MyAccountProfileTaxIdControl,\n TaxIdControlDefinition: MyAccountProfileTaxIdControlDefinition,\n BirthDateControl: MyAccountProfileBirthDateControl,\n BirthDateControlDefinition: MyAccountProfileBirthDateControlDefinition,\n GenderControl: MyAccountProfileGenderControl,\n GenderControlDefinition: MyAccountProfileGenderControlDefinition,\n};\n"],"mappings":"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"}
@@ -69,7 +69,7 @@ export declare function ProductCardPrice({ className, children, ...props }: Prod
69
69
  export declare function ProductCardListPrice({ className, children, ...props }: ProductCardListPriceProps): import("react/jsx-runtime").JSX.Element | null;
70
70
  export declare function ProductCardTaxLabel({ className, children, ...props }: ProductCardTaxLabelProps): import("react/jsx-runtime").JSX.Element | null;
71
71
  export declare function ProductCardDiscount({ className, children, format, ...props }: ProductCardDiscountProps): import("react/jsx-runtime").JSX.Element | null;
72
- export declare function ProductCardAddToCart({ className, children, onClick, $actions, ...props }: ProductCardAddToCartProps): import("react/jsx-runtime").JSX.Element;
72
+ export declare function ProductCardAddToCart({ className, children, onClick, $actions: $actionsProp, ...props }: ProductCardAddToCartProps): import("react/jsx-runtime").JSX.Element;
73
73
  export declare const ProductList: {
74
74
  Root: typeof ProductListRoot;
75
75
  Grid: typeof ProductListGrid;
@@ -2,6 +2,7 @@
2
2
  import { defineComponent } from "../../core/dist/define-component.js";
3
3
  import "../../core/dist/index.js";
4
4
  import { useStorefrontContext } from "../../sdk/context/storefront-context.js";
5
+ import { useActionsOverride } from "../../sdk/actions/actions-override-context.js";
5
6
  import { Button } from "../../particles/button/button.js";
6
7
  import { Image } from "../../particles/image/image.js";
7
8
  import { Badge } from "../../particles/badge/badge.js";
@@ -307,7 +308,8 @@ function ProductCardDiscount({ className, children, format = "percentage", ...pr
307
308
  children: children ?? label
308
309
  });
309
310
  }
310
- function ProductCardAddToCart({ className, children, onClick, $actions, ...props }) {
311
+ function ProductCardAddToCart({ className, children, onClick, $actions: $actionsProp, ...props }) {
312
+ const $actions = useActionsOverride($actionsProp);
311
313
  const item = useProductListItem();
312
314
  function handleClick(e) {
313
315
  $actions?.cart?.addItem?.({
@@ -1 +1 @@
1
- {"version":3,"file":"product-list.js","names":[],"sources":["../../../src/matter/product-list/product-list.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport { type ComponentPropsWithRef, createContext, useContext, useMemo } from 'react';\nimport { Badge } from '../../particles/badge/badge.js';\nimport { Button } from '../../particles/button/button.js';\nimport { Container } from '../../particles/container/container.js';\nimport { Image } from '../../particles/image/image.js';\nimport { Text } from '../../particles/text/text.js';\nimport type { ComponentActions } from '../../sdk/actions/types.js';\nimport { useStorefrontContext } from '../../sdk/context/storefront-context.js';\n\n// ---------------------------------------------------------------------------\n// Product list config context (tax display)\n// ---------------------------------------------------------------------------\n\ntype ProductListConfig = {\n usePriceWithTaxes: boolean;\n taxesLabel: string;\n};\n\nexport const ProductListConfigContext = createContext<ProductListConfig>({\n usePriceWithTaxes: false,\n taxesLabel: 'Tax included',\n});\n\nfunction useProductListConfig(): ProductListConfig {\n return useContext(ProductListConfigContext);\n}\n\nfunction formatPrice(value: number, locale: string, currency: string): string {\n return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(value);\n}\n\n// Tax rate for the item: the explicit `taxPercentage` (e.g. 0.18) wins; otherwise it is derived\n// from the absolute tax amount over the price. Missing/zero-price data yields 0 (no change).\nfunction taxRate(item: ProductListItem): number {\n if (item.taxPercentage != null) {\n return item.taxPercentage;\n }\n if (item.tax != null && item.price > 0) {\n return item.tax / item.price;\n }\n return 0;\n}\n\nfunction priceWithTaxes(base: number, item: ProductListItem): number {\n return base * (1 + taxRate(item));\n}\n\n// ---------------------------------------------------------------------------\n// Product list item types\n// ---------------------------------------------------------------------------\n\nexport type ProductListItemImage = {\n src: string;\n alt: string;\n};\n\nexport type ProductListItem = {\n id: string;\n name: string;\n image: ProductListItemImage;\n price: number;\n listPrice?: number;\n variant?: string;\n slug?: string;\n // Widened for the Product Shelf (T2): fields the card parts T3–T7 read straight off the\n // per-item context. Optional so the plain-`products` ProductListRoot (PLP) stays valid.\n spotPrice?: number;\n tax?: number;\n taxPercentage?: number | null;\n availableForSale?: boolean;\n availableQuantity?: number;\n currency?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Products context (provided by Root, consumed by Grid)\n// ---------------------------------------------------------------------------\n\nconst ProductListProductsContext = createContext<ProductListItem[]>([]);\n\n// ---------------------------------------------------------------------------\n// Item context (provided by Grid per product)\n// ---------------------------------------------------------------------------\n\nexport const ProductListItemContext = createContext<ProductListItem | null>(null);\n\nfunction useProductListItem(): ProductListItem {\n const item = useContext(ProductListItemContext);\n if (!item) {\n throw new Error('ProductCard components must be used inside ProductListGrid.');\n }\n return item;\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type ProductListRootProps = ComponentPropsWithRef<'div'> & {\n products?: ProductListItem[];\n /** When true, card prices render tax-inclusive (from the item's tax data). Default false. */\n usePriceWithTaxes?: boolean;\n /** Text for the tax label part when tax display is on. Default `'Tax included'`. */\n taxesLabel?: string;\n};\n\nexport type ProductListGridProps = ComponentPropsWithRef<'div'>;\n\nexport type ProductCardProps = ComponentPropsWithRef<'div'> & {\n /** Toggles the card border. Reflects onto data-fs-product-card-bordered; a swatch draws the border. Default true. */\n bordered?: boolean;\n};\n\nexport type ProductCardImageProps = ComponentPropsWithRef<'img'> & {\n objectFit?: 'contain' | 'cover' | 'fill' | 'none';\n /** CSS `aspect-ratio` (e.g. `'1/1'`, `'3/4'`, `'16/9'`); lays out the image box, orthogonal to `objectFit`. */\n aspectRatio?: string;\n};\n\nexport type ProductCardNameProps = ComponentPropsWithRef<'span'>;\n\nexport type ProductCardPriceProps = ComponentPropsWithRef<'span'>;\n\nexport type ProductCardListPriceProps = ComponentPropsWithRef<'span'>;\n\nexport type ProductCardTaxLabelProps = ComponentPropsWithRef<'span'>;\n\nexport type ProductCardDiscountProps = ComponentPropsWithRef<'span'> & {\n format?: 'percentage' | 'absolute';\n};\n\nexport type ProductCardAddToCartProps = ComponentPropsWithRef<'button'> & {\n /** Injected by the compiler from `actions: { cart: ['addItem'] }`; the cart write surface. */\n $actions?: ComponentActions;\n};\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const ProductListRootDefinition = defineComponent<ProductListRootProps>({\n name: 'ProductListRoot',\n importPath: '@vtex/faststore/components',\n component: ProductListRoot,\n editor: { label: 'Product List Root', category: 'commerce' },\n schema: {\n usePriceWithTaxes: { type: 'boolean', default: false, label: 'Use Price With Taxes' },\n taxesLabel: { type: 'string', default: 'Tax included', label: 'Taxes Label' },\n },\n guideline:\n 'Top-level wrapper for the product list; compose as ProductListRoot > ProductListGrid > ProductCard* children. Set usePriceWithTaxes to render tax-inclusive card prices and compose ProductCardTaxLabel to show the taxesLabel.',\n});\n\nexport const ProductListGridDefinition = defineComponent<ProductListGridProps>({\n name: 'ProductListGrid',\n importPath: '@vtex/faststore/components',\n component: ProductListGrid,\n editor: { label: 'Product List Grid', category: 'commerce' },\n schema: {},\n guideline:\n 'Repeating grid that renders the ProductCard* template for each product; must be a direct child of ProductListRoot.',\n});\n\nexport const ProductCardDefinition = defineComponent<ProductCardProps>({\n name: 'ProductCard',\n importPath: '@vtex/faststore/components',\n component: ProductCard,\n editor: { label: 'Product Card', category: 'commerce' },\n schema: {\n bordered: { type: 'boolean', default: true, label: 'Bordered' },\n },\n guideline:\n 'Optional card wrapper for the ProductCard* parts; always emits data-fs-product-card and data-fs-product-card-bordered (true/false, from the bordered toggle), and adds data-fs-product-card-out-of-stock when the product is unavailable (derived from availability, no prop). The border look is a swatch keyed off data-fs-product-card-bordered. Renders all children unchanged. Must be inside ProductListGrid / ProductShelfItems.',\n});\n\nexport const ProductCardImageDefinition = defineComponent<ProductCardImageProps>({\n name: 'ProductCardImage',\n importPath: '@vtex/faststore/components',\n component: ProductCardImage,\n editor: { label: 'Product Card Image', category: 'commerce' },\n schema: {\n objectFit: { type: ['contain', 'cover', 'fill', 'none'] as const, default: 'cover', label: 'Object Fit' },\n aspectRatio: { type: 'string', label: 'Aspect Ratio' },\n },\n guideline:\n 'Renders the product image with a configurable aspectRatio (e.g. 1/1, 3/4, 16/9) and objectFit. Alt text comes from the catalog. Must be inside ProductListGrid.',\n});\n\nexport const ProductCardNameDefinition = defineComponent<ProductCardNameProps>({\n name: 'ProductCardName',\n importPath: '@vtex/faststore/components',\n component: ProductCardName,\n editor: { label: 'Product Card Name', category: 'commerce' },\n schema: {},\n guideline: 'Renders the product name. Must be inside ProductListGrid.',\n});\n\nexport const ProductCardPriceDefinition = defineComponent<ProductCardPriceProps>({\n name: 'ProductCardPrice',\n importPath: '@vtex/faststore/components',\n component: ProductCardPrice,\n editor: { label: 'Product Card Price', category: 'commerce' },\n schema: {},\n guideline:\n 'Displays the selling price formatted from the active storefront context; renders tax-inclusive when usePriceWithTaxes is enabled. Must be inside ProductListGrid.',\n});\n\nexport const ProductCardListPriceDefinition = defineComponent<ProductCardListPriceProps>({\n name: 'ProductCardListPrice',\n importPath: '@vtex/faststore/components',\n component: ProductCardListPrice,\n editor: { label: 'Product Card List Price', category: 'commerce' },\n schema: {},\n guideline:\n 'Displays the original (strikethrough) list price when it exceeds the selling price; renders tax-inclusive when usePriceWithTaxes is enabled. Must be inside ProductListGrid.',\n});\n\nexport const ProductCardTaxLabelDefinition = defineComponent<ProductCardTaxLabelProps>({\n name: 'ProductCardTaxLabel',\n importPath: '@vtex/faststore/components',\n component: ProductCardTaxLabel,\n editor: { label: 'Product Card Tax Label', category: 'commerce' },\n schema: {},\n guideline:\n 'Tax label (e.g. \"Tax included\") shown beside the price. Renders only when the ProductListRoot/ProductShelfRoot config usePriceWithTaxes is enabled (state-driven, no toggle); the text comes from the taxesLabel config. Compose it to place the label, omit it to hide it. Must be inside ProductListGrid / ProductShelfItems.',\n});\n\nexport const ProductCardDiscountDefinition = defineComponent<ProductCardDiscountProps>({\n name: 'ProductCardDiscount',\n importPath: '@vtex/faststore/components',\n component: ProductCardDiscount,\n editor: { label: 'Product Card Discount', category: 'commerce' },\n schema: {\n format: { type: ['percentage', 'absolute'] as const, default: 'percentage', label: 'Discount Format' },\n },\n guideline:\n 'Discount badge for the current product; auto-computes the markdown from listPrice vs price and renders only when a discount exists. Compose it to show the badge, omit it to hide it (no toggle). Must be inside ProductListGrid / ProductShelfItems.',\n});\n\nexport const ProductCardAddToCartDefinition = defineComponent<ProductCardAddToCartProps>({\n name: 'ProductCardAddToCart',\n importPath: '@vtex/faststore/components',\n component: ProductCardAddToCart,\n editor: { label: 'Product Card Add to Cart', category: 'commerce' },\n schema: {},\n actions: { cart: ['addItem'] },\n guideline: 'Add-to-cart button for the current product card; must be inside ProductListGrid.',\n});\n\n// ---------------------------------------------------------------------------\n// Implementations\n// ---------------------------------------------------------------------------\n\nexport function ProductListRoot({\n usePriceWithTaxes = false,\n taxesLabel = 'Tax included',\n products = [],\n className,\n children,\n ...props\n}: ProductListRootProps) {\n const config = useMemo(() => ({ usePriceWithTaxes, taxesLabel }), [usePriceWithTaxes, taxesLabel]);\n\n return (\n <ProductListConfigContext value={config}>\n <ProductListProductsContext value={products}>\n <Container {...props} className={className} data-fs-product-list-root=\"\">\n {children}\n </Container>\n </ProductListProductsContext>\n </ProductListConfigContext>\n );\n}\n\nexport function ProductListGrid({ className, children, ...props }: ProductListGridProps) {\n const products = useContext(ProductListProductsContext);\n\n return (\n <Container {...props} className={className} data-fs-product-list-grid=\"\">\n {products.map((product) => (\n <ProductListItemContext key={product.id} value={product}>\n {children}\n </ProductListItemContext>\n ))}\n </Container>\n );\n}\n\nexport function ProductCard({ className, children, bordered = true, ...props }: ProductCardProps) {\n const item = useProductListItem();\n const outOfStock = item.availableForSale === false || item.availableQuantity === 0;\n\n return (\n <Container\n {...props}\n className={className}\n data-fs-product-card=\"\"\n data-fs-product-card-bordered={String(bordered)}\n data-fs-product-card-out-of-stock={outOfStock ? '' : undefined}\n >\n {children}\n </Container>\n );\n}\n\nexport function ProductCardImage({ className, objectFit = 'cover', aspectRatio, ...props }: ProductCardImageProps) {\n const item = useProductListItem();\n\n return (\n <Image\n {...props}\n src={item.image.src}\n alt={item.image.alt}\n aspectRatio={aspectRatio}\n className={className}\n style={{ objectFit, ...props.style }}\n data-fs-product-card-image=\"\"\n />\n );\n}\n\nexport function ProductCardName({ className, children, ...props }: ProductCardNameProps) {\n const item = useProductListItem();\n\n return (\n <Text {...props} as=\"span\" className={className} data-fs-product-card-name=\"\">\n {children ?? item.name}\n </Text>\n );\n}\n\nexport function ProductCardPrice({ className, children, ...props }: ProductCardPriceProps) {\n const item = useProductListItem();\n const { usePriceWithTaxes } = useProductListConfig();\n const { locale, currency } = useStorefrontContext();\n const value = usePriceWithTaxes ? priceWithTaxes(item.price, item) : item.price;\n\n return (\n <Text {...props} as=\"span\" className={className} data-fs-product-card-price=\"\">\n {children ?? formatPrice(value, locale, item.currency ?? currency)}\n </Text>\n );\n}\n\nexport function ProductCardListPrice({ className, children, ...props }: ProductCardListPriceProps) {\n const item = useProductListItem();\n const { usePriceWithTaxes } = useProductListConfig();\n const { locale, currency } = useStorefrontContext();\n\n if (!item.listPrice || item.listPrice <= item.price) {\n return null;\n }\n\n const value = usePriceWithTaxes ? priceWithTaxes(item.listPrice, item) : item.listPrice;\n\n return (\n <Text {...props} as=\"span\" className={className} data-fs-product-card-list-price=\"\">\n {children ?? formatPrice(value, locale, item.currency ?? currency)}\n </Text>\n );\n}\n\nexport function ProductCardTaxLabel({ className, children, ...props }: ProductCardTaxLabelProps) {\n const { usePriceWithTaxes, taxesLabel } = useProductListConfig();\n\n if (!usePriceWithTaxes) {\n return null;\n }\n\n return (\n <Text {...props} as=\"span\" className={className} data-fs-product-card-tax-label=\"\">\n {children ?? taxesLabel}\n </Text>\n );\n}\n\nexport function ProductCardDiscount({\n className,\n children,\n format = 'percentage',\n ...props\n}: ProductCardDiscountProps) {\n const item = useProductListItem();\n const { usePriceWithTaxes } = useProductListConfig();\n const { locale, currency } = useStorefrontContext();\n\n const { listPrice, price } = item;\n if (!listPrice || listPrice <= price) {\n return null;\n }\n\n const amountOff = listPrice - price;\n const percentOff = Math.round((amountOff / listPrice) * 100);\n const tier = percentOff >= 30 ? 'high' : percentOff >= 15 ? 'medium' : 'low';\n // The percentage is tax-invariant (both prices scale by the same factor), but the absolute amount\n // must match the displayed prices, which are tax-inclusive when usePriceWithTaxes is enabled.\n const displayedAmountOff = usePriceWithTaxes ? priceWithTaxes(amountOff, item) : amountOff;\n const label =\n format === 'absolute'\n ? `-${formatPrice(displayedAmountOff, locale, item.currency ?? currency)}`\n : `-${percentOff}%`;\n\n return (\n <Badge {...props} className={className} data-fs-product-card-discount=\"\" data-fs-product-card-discount-tier={tier}>\n {children ?? label}\n </Badge>\n );\n}\n\nexport function ProductCardAddToCart({ className, children, onClick, $actions, ...props }: ProductCardAddToCartProps) {\n const item = useProductListItem();\n\n function handleClick(e: React.MouseEvent<HTMLButtonElement>) {\n void $actions?.cart?.addItem?.({ variantId: item.id, seller: '1' });\n onClick?.(e);\n }\n\n return (\n <Button {...props} className={className} onClick={handleClick} data-fs-product-card-add-to-cart=\"\">\n {children}\n </Button>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Namespace export\n// ---------------------------------------------------------------------------\n\nexport const ProductList = {\n Root: ProductListRoot,\n Grid: ProductListGrid,\n Card: ProductCard,\n CardImage: ProductCardImage,\n CardName: ProductCardName,\n CardPrice: ProductCardPrice,\n CardListPrice: ProductCardListPrice,\n CardTaxLabel: ProductCardTaxLabel,\n CardDiscount: ProductCardDiscount,\n CardAddToCart: ProductCardAddToCart,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"product-list.js","names":[],"sources":["../../../src/matter/product-list/product-list.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport { type ComponentPropsWithRef, createContext, useContext, useMemo } from 'react';\nimport { Badge } from '../../particles/badge/badge.js';\nimport { Button } from '../../particles/button/button.js';\nimport { Container } from '../../particles/container/container.js';\nimport { Image } from '../../particles/image/image.js';\nimport { Text } from '../../particles/text/text.js';\nimport { useActionsOverride } from '../../sdk/actions/actions-override-context.js';\nimport type { ComponentActions } from '../../sdk/actions/types.js';\nimport { useStorefrontContext } from '../../sdk/context/storefront-context.js';\n\n// ---------------------------------------------------------------------------\n// Product list config context (tax display)\n// ---------------------------------------------------------------------------\n\ntype ProductListConfig = {\n usePriceWithTaxes: boolean;\n taxesLabel: string;\n};\n\nexport const ProductListConfigContext = createContext<ProductListConfig>({\n usePriceWithTaxes: false,\n taxesLabel: 'Tax included',\n});\n\nfunction useProductListConfig(): ProductListConfig {\n return useContext(ProductListConfigContext);\n}\n\nfunction formatPrice(value: number, locale: string, currency: string): string {\n return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(value);\n}\n\n// Tax rate for the item: the explicit `taxPercentage` (e.g. 0.18) wins; otherwise it is derived\n// from the absolute tax amount over the price. Missing/zero-price data yields 0 (no change).\nfunction taxRate(item: ProductListItem): number {\n if (item.taxPercentage != null) {\n return item.taxPercentage;\n }\n if (item.tax != null && item.price > 0) {\n return item.tax / item.price;\n }\n return 0;\n}\n\nfunction priceWithTaxes(base: number, item: ProductListItem): number {\n return base * (1 + taxRate(item));\n}\n\n// ---------------------------------------------------------------------------\n// Product list item types\n// ---------------------------------------------------------------------------\n\nexport type ProductListItemImage = {\n src: string;\n alt: string;\n};\n\nexport type ProductListItem = {\n id: string;\n name: string;\n image: ProductListItemImage;\n price: number;\n listPrice?: number;\n variant?: string;\n slug?: string;\n // Widened for the Product Shelf (T2): fields the card parts T3–T7 read straight off the\n // per-item context. Optional so the plain-`products` ProductListRoot (PLP) stays valid.\n spotPrice?: number;\n tax?: number;\n taxPercentage?: number | null;\n availableForSale?: boolean;\n availableQuantity?: number;\n currency?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Products context (provided by Root, consumed by Grid)\n// ---------------------------------------------------------------------------\n\nconst ProductListProductsContext = createContext<ProductListItem[]>([]);\n\n// ---------------------------------------------------------------------------\n// Item context (provided by Grid per product)\n// ---------------------------------------------------------------------------\n\nexport const ProductListItemContext = createContext<ProductListItem | null>(null);\n\nfunction useProductListItem(): ProductListItem {\n const item = useContext(ProductListItemContext);\n if (!item) {\n throw new Error('ProductCard components must be used inside ProductListGrid.');\n }\n return item;\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type ProductListRootProps = ComponentPropsWithRef<'div'> & {\n products?: ProductListItem[];\n /** When true, card prices render tax-inclusive (from the item's tax data). Default false. */\n usePriceWithTaxes?: boolean;\n /** Text for the tax label part when tax display is on. Default `'Tax included'`. */\n taxesLabel?: string;\n};\n\nexport type ProductListGridProps = ComponentPropsWithRef<'div'>;\n\nexport type ProductCardProps = ComponentPropsWithRef<'div'> & {\n /** Toggles the card border. Reflects onto data-fs-product-card-bordered; a swatch draws the border. Default true. */\n bordered?: boolean;\n};\n\nexport type ProductCardImageProps = ComponentPropsWithRef<'img'> & {\n objectFit?: 'contain' | 'cover' | 'fill' | 'none';\n /** CSS `aspect-ratio` (e.g. `'1/1'`, `'3/4'`, `'16/9'`); lays out the image box, orthogonal to `objectFit`. */\n aspectRatio?: string;\n};\n\nexport type ProductCardNameProps = ComponentPropsWithRef<'span'>;\n\nexport type ProductCardPriceProps = ComponentPropsWithRef<'span'>;\n\nexport type ProductCardListPriceProps = ComponentPropsWithRef<'span'>;\n\nexport type ProductCardTaxLabelProps = ComponentPropsWithRef<'span'>;\n\nexport type ProductCardDiscountProps = ComponentPropsWithRef<'span'> & {\n format?: 'percentage' | 'absolute';\n};\n\nexport type ProductCardAddToCartProps = ComponentPropsWithRef<'button'> & {\n /** Injected by the compiler from `actions: { cart: ['addItem'] }`; the cart write surface. */\n $actions?: ComponentActions;\n};\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const ProductListRootDefinition = defineComponent<ProductListRootProps>({\n name: 'ProductListRoot',\n importPath: '@vtex/faststore/components',\n component: ProductListRoot,\n editor: { label: 'Product List Root', category: 'commerce' },\n schema: {\n usePriceWithTaxes: { type: 'boolean', default: false, label: 'Use Price With Taxes' },\n taxesLabel: { type: 'string', default: 'Tax included', label: 'Taxes Label' },\n },\n guideline:\n 'Top-level wrapper for the product list; compose as ProductListRoot > ProductListGrid > ProductCard* children. Set usePriceWithTaxes to render tax-inclusive card prices and compose ProductCardTaxLabel to show the taxesLabel.',\n});\n\nexport const ProductListGridDefinition = defineComponent<ProductListGridProps>({\n name: 'ProductListGrid',\n importPath: '@vtex/faststore/components',\n component: ProductListGrid,\n editor: { label: 'Product List Grid', category: 'commerce' },\n schema: {},\n guideline:\n 'Repeating grid that renders the ProductCard* template for each product; must be a direct child of ProductListRoot.',\n});\n\nexport const ProductCardDefinition = defineComponent<ProductCardProps>({\n name: 'ProductCard',\n importPath: '@vtex/faststore/components',\n component: ProductCard,\n editor: { label: 'Product Card', category: 'commerce' },\n schema: {\n bordered: { type: 'boolean', default: true, label: 'Bordered' },\n },\n guideline:\n 'Optional card wrapper for the ProductCard* parts; always emits data-fs-product-card and data-fs-product-card-bordered (true/false, from the bordered toggle), and adds data-fs-product-card-out-of-stock when the product is unavailable (derived from availability, no prop). The border look is a swatch keyed off data-fs-product-card-bordered. Renders all children unchanged. Must be inside ProductListGrid / ProductShelfItems.',\n});\n\nexport const ProductCardImageDefinition = defineComponent<ProductCardImageProps>({\n name: 'ProductCardImage',\n importPath: '@vtex/faststore/components',\n component: ProductCardImage,\n editor: { label: 'Product Card Image', category: 'commerce' },\n schema: {\n objectFit: { type: ['contain', 'cover', 'fill', 'none'] as const, default: 'cover', label: 'Object Fit' },\n aspectRatio: { type: 'string', label: 'Aspect Ratio' },\n },\n guideline:\n 'Renders the product image with a configurable aspectRatio (e.g. 1/1, 3/4, 16/9) and objectFit. Alt text comes from the catalog. Must be inside ProductListGrid.',\n});\n\nexport const ProductCardNameDefinition = defineComponent<ProductCardNameProps>({\n name: 'ProductCardName',\n importPath: '@vtex/faststore/components',\n component: ProductCardName,\n editor: { label: 'Product Card Name', category: 'commerce' },\n schema: {},\n guideline: 'Renders the product name. Must be inside ProductListGrid.',\n});\n\nexport const ProductCardPriceDefinition = defineComponent<ProductCardPriceProps>({\n name: 'ProductCardPrice',\n importPath: '@vtex/faststore/components',\n component: ProductCardPrice,\n editor: { label: 'Product Card Price', category: 'commerce' },\n schema: {},\n guideline:\n 'Displays the selling price formatted from the active storefront context; renders tax-inclusive when usePriceWithTaxes is enabled. Must be inside ProductListGrid.',\n});\n\nexport const ProductCardListPriceDefinition = defineComponent<ProductCardListPriceProps>({\n name: 'ProductCardListPrice',\n importPath: '@vtex/faststore/components',\n component: ProductCardListPrice,\n editor: { label: 'Product Card List Price', category: 'commerce' },\n schema: {},\n guideline:\n 'Displays the original (strikethrough) list price when it exceeds the selling price; renders tax-inclusive when usePriceWithTaxes is enabled. Must be inside ProductListGrid.',\n});\n\nexport const ProductCardTaxLabelDefinition = defineComponent<ProductCardTaxLabelProps>({\n name: 'ProductCardTaxLabel',\n importPath: '@vtex/faststore/components',\n component: ProductCardTaxLabel,\n editor: { label: 'Product Card Tax Label', category: 'commerce' },\n schema: {},\n guideline:\n 'Tax label (e.g. \"Tax included\") shown beside the price. Renders only when the ProductListRoot/ProductShelfRoot config usePriceWithTaxes is enabled (state-driven, no toggle); the text comes from the taxesLabel config. Compose it to place the label, omit it to hide it. Must be inside ProductListGrid / ProductShelfItems.',\n});\n\nexport const ProductCardDiscountDefinition = defineComponent<ProductCardDiscountProps>({\n name: 'ProductCardDiscount',\n importPath: '@vtex/faststore/components',\n component: ProductCardDiscount,\n editor: { label: 'Product Card Discount', category: 'commerce' },\n schema: {\n format: { type: ['percentage', 'absolute'] as const, default: 'percentage', label: 'Discount Format' },\n },\n guideline:\n 'Discount badge for the current product; auto-computes the markdown from listPrice vs price and renders only when a discount exists. Compose it to show the badge, omit it to hide it (no toggle). Must be inside ProductListGrid / ProductShelfItems.',\n});\n\nexport const ProductCardAddToCartDefinition = defineComponent<ProductCardAddToCartProps>({\n name: 'ProductCardAddToCart',\n importPath: '@vtex/faststore/components',\n component: ProductCardAddToCart,\n editor: { label: 'Product Card Add to Cart', category: 'commerce' },\n schema: {},\n actions: { cart: ['addItem'] },\n guideline: 'Add-to-cart button for the current product card; must be inside ProductListGrid.',\n});\n\n// ---------------------------------------------------------------------------\n// Implementations\n// ---------------------------------------------------------------------------\n\nexport function ProductListRoot({\n usePriceWithTaxes = false,\n taxesLabel = 'Tax included',\n products = [],\n className,\n children,\n ...props\n}: ProductListRootProps) {\n const config = useMemo(() => ({ usePriceWithTaxes, taxesLabel }), [usePriceWithTaxes, taxesLabel]);\n\n return (\n <ProductListConfigContext value={config}>\n <ProductListProductsContext value={products}>\n <Container {...props} className={className} data-fs-product-list-root=\"\">\n {children}\n </Container>\n </ProductListProductsContext>\n </ProductListConfigContext>\n );\n}\n\nexport function ProductListGrid({ className, children, ...props }: ProductListGridProps) {\n const products = useContext(ProductListProductsContext);\n\n return (\n <Container {...props} className={className} data-fs-product-list-grid=\"\">\n {products.map((product) => (\n <ProductListItemContext key={product.id} value={product}>\n {children}\n </ProductListItemContext>\n ))}\n </Container>\n );\n}\n\nexport function ProductCard({ className, children, bordered = true, ...props }: ProductCardProps) {\n const item = useProductListItem();\n const outOfStock = item.availableForSale === false || item.availableQuantity === 0;\n\n return (\n <Container\n {...props}\n className={className}\n data-fs-product-card=\"\"\n data-fs-product-card-bordered={String(bordered)}\n data-fs-product-card-out-of-stock={outOfStock ? '' : undefined}\n >\n {children}\n </Container>\n );\n}\n\nexport function ProductCardImage({ className, objectFit = 'cover', aspectRatio, ...props }: ProductCardImageProps) {\n const item = useProductListItem();\n\n return (\n <Image\n {...props}\n src={item.image.src}\n alt={item.image.alt}\n aspectRatio={aspectRatio}\n className={className}\n style={{ objectFit, ...props.style }}\n data-fs-product-card-image=\"\"\n />\n );\n}\n\nexport function ProductCardName({ className, children, ...props }: ProductCardNameProps) {\n const item = useProductListItem();\n\n return (\n <Text {...props} as=\"span\" className={className} data-fs-product-card-name=\"\">\n {children ?? item.name}\n </Text>\n );\n}\n\nexport function ProductCardPrice({ className, children, ...props }: ProductCardPriceProps) {\n const item = useProductListItem();\n const { usePriceWithTaxes } = useProductListConfig();\n const { locale, currency } = useStorefrontContext();\n const value = usePriceWithTaxes ? priceWithTaxes(item.price, item) : item.price;\n\n return (\n <Text {...props} as=\"span\" className={className} data-fs-product-card-price=\"\">\n {children ?? formatPrice(value, locale, item.currency ?? currency)}\n </Text>\n );\n}\n\nexport function ProductCardListPrice({ className, children, ...props }: ProductCardListPriceProps) {\n const item = useProductListItem();\n const { usePriceWithTaxes } = useProductListConfig();\n const { locale, currency } = useStorefrontContext();\n\n if (!item.listPrice || item.listPrice <= item.price) {\n return null;\n }\n\n const value = usePriceWithTaxes ? priceWithTaxes(item.listPrice, item) : item.listPrice;\n\n return (\n <Text {...props} as=\"span\" className={className} data-fs-product-card-list-price=\"\">\n {children ?? formatPrice(value, locale, item.currency ?? currency)}\n </Text>\n );\n}\n\nexport function ProductCardTaxLabel({ className, children, ...props }: ProductCardTaxLabelProps) {\n const { usePriceWithTaxes, taxesLabel } = useProductListConfig();\n\n if (!usePriceWithTaxes) {\n return null;\n }\n\n return (\n <Text {...props} as=\"span\" className={className} data-fs-product-card-tax-label=\"\">\n {children ?? taxesLabel}\n </Text>\n );\n}\n\nexport function ProductCardDiscount({\n className,\n children,\n format = 'percentage',\n ...props\n}: ProductCardDiscountProps) {\n const item = useProductListItem();\n const { usePriceWithTaxes } = useProductListConfig();\n const { locale, currency } = useStorefrontContext();\n\n const { listPrice, price } = item;\n if (!listPrice || listPrice <= price) {\n return null;\n }\n\n const amountOff = listPrice - price;\n const percentOff = Math.round((amountOff / listPrice) * 100);\n const tier = percentOff >= 30 ? 'high' : percentOff >= 15 ? 'medium' : 'low';\n // The percentage is tax-invariant (both prices scale by the same factor), but the absolute amount\n // must match the displayed prices, which are tax-inclusive when usePriceWithTaxes is enabled.\n const displayedAmountOff = usePriceWithTaxes ? priceWithTaxes(amountOff, item) : amountOff;\n const label =\n format === 'absolute'\n ? `-${formatPrice(displayedAmountOff, locale, item.currency ?? currency)}`\n : `-${percentOff}%`;\n\n return (\n <Badge {...props} className={className} data-fs-product-card-discount=\"\" data-fs-product-card-discount-tier={tier}>\n {children ?? label}\n </Badge>\n );\n}\n\nexport function ProductCardAddToCart({\n className,\n children,\n onClick,\n $actions: $actionsProp,\n ...props\n}: ProductCardAddToCartProps) {\n const $actions = useActionsOverride($actionsProp);\n const item = useProductListItem();\n\n function handleClick(e: React.MouseEvent<HTMLButtonElement>) {\n void $actions?.cart?.addItem?.({ variantId: item.id, seller: '1' });\n onClick?.(e);\n }\n\n return (\n <Button {...props} className={className} onClick={handleClick} data-fs-product-card-add-to-cart=\"\">\n {children}\n </Button>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Namespace export\n// ---------------------------------------------------------------------------\n\nexport const ProductList = {\n Root: ProductListRoot,\n Grid: ProductListGrid,\n Card: ProductCard,\n CardImage: ProductCardImage,\n CardName: ProductCardName,\n CardPrice: ProductCardPrice,\n CardListPrice: ProductCardListPrice,\n CardTaxLabel: ProductCardTaxLabel,\n CardDiscount: ProductCardDiscount,\n CardAddToCart: ProductCardAddToCart,\n};\n"],"mappings":"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"}
@@ -175,7 +175,7 @@ export declare const RegionUnknownLinkDefinition: ComponentDefinition<RegionUnkn
175
175
  * into the Apollo cache (Decision 5). That is why a rejected write needs no rollback and cannot
176
176
  * leave the header showing a postal code the server refused.
177
177
  */
178
- export declare function RegionRoot({ $data: $dataProp, $actions, className, children, ...props }: RegionRootProps): import("react/jsx-runtime").JSX.Element;
178
+ export declare function RegionRoot({ $data: $dataProp, $actions: $actionsProp, className, children, ...props }: RegionRootProps): import("react/jsx-runtime").JSX.Element;
179
179
  export declare function RegionTrigger({ unsetLabel, $content, className, children, ...props }: RegionTriggerProps): import("react/jsx-runtime").JSX.Element;
180
180
  export declare function RegionValue({ className, ...props }: RegionValueProps): import("react/jsx-runtime").JSX.Element | null;
181
181
  export declare function RegionLoading({ className, children, ...props }: RegionLoadingProps): import("react/jsx-runtime").JSX.Element | null;
@@ -4,6 +4,7 @@ import { defineContentType } from "../../core/dist/define-content-type.js";
4
4
  import "../../core/dist/index.js";
5
5
  import { useIntl } from "../../sdk/i18n/client.js";
6
6
  import { graphql } from "../../sdk/generated/gql.js";
7
+ import { useActionsOverride } from "../../sdk/actions/actions-override-context.js";
7
8
  import { useDataOverride } from "../../sdk/data/data-override-context.js";
8
9
  import { resolveSafeHref } from "../../utils/safe-href.js";
9
10
  import { ReactiveRootContext } from "../../sdk/data/page-reactive-data-root.js";
@@ -291,8 +292,10 @@ const RegionUnknownLinkDefinition = defineComponent({
291
292
  * into the Apollo cache (Decision 5). That is why a rejected write needs no rollback and cannot
292
293
  * leave the header showing a postal code the server refused.
293
294
  */
294
- function RegionRoot({ $data: $dataProp, $actions, className, children, ...props }) {
295
- const region = useDataOverride($dataProp) ?? null;
295
+ function RegionRoot({ $data: $dataProp, $actions: $actionsProp, className, children, ...props }) {
296
+ const $data = useDataOverride($dataProp);
297
+ const $actions = useActionsOverride($actionsProp);
298
+ const region = $data ?? null;
296
299
  const { loading: rootLoading } = useContext(ReactiveRootContext);
297
300
  const loading = rootLoading && region == null;
298
301
  const postalCode = region?.postalCode ?? null;
@@ -1 +1 @@
1
- {"version":3,"file":"region.js","names":[],"sources":["../../../src/matter/region/region.tsx"],"sourcesContent":["'use client';\n\nimport { type ContentValue, defineComponent, defineContentType } from '@vtex/fs-core';\nimport {\n type ComponentPropsWithRef,\n cloneElement,\n createContext,\n isValidElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { DialogRootProps } from '../../particles/dialog/dialog.js';\nimport { PostalCodeField } from '../../particles/postal-code-field/postal-code-field.js';\nimport type { ComponentActions } from '../../sdk/actions/types.js';\nimport { useDataOverride } from '../../sdk/data/data-override-context.js';\nimport { ReactiveRootContext } from '../../sdk/data/page-reactive-data-root.js';\nimport { graphql } from '../../sdk/generated/gql.js';\nimport { type MessageId, useIntl } from '../../sdk/i18n/client.js';\nimport type { RegionEntity } from '../../sdk/region/operations.js';\nimport type { RegionUserError } from '../../sdk/region/region-operations.js';\nimport { resolveSafeHref } from '../../utils/safe-href.js';\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment — typed via @graphql-codegen client-preset\n// ---------------------------------------------------------------------------\n\nconst fragment = graphql(`\n fragment RegionData on Query {\n region {\n ...RegionFragment\n }\n }\n`);\n\n// ---------------------------------------------------------------------------\n// Content models — one per copy-bearing part (Decision 7)\n// ---------------------------------------------------------------------------\n\nexport const RegionTriggerContent = defineContentType({\n id: 'region-trigger',\n title: 'Region Trigger',\n description: 'Copy for the control that opens the region modal.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n unsetLabel: { type: 'string', title: 'Label when no region is set' },\n ariaLabel: { type: 'string', title: 'Accessible label' },\n },\n },\n});\n\nexport const RegionSubmitContent = defineContentType({\n id: 'region-submit',\n title: 'Region Submit',\n description: 'Copy for the region submit control.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n label: { type: 'string', title: 'Button label' },\n pendingLabel: { type: 'string', title: 'Pending label' },\n },\n },\n});\n\nexport const RegionUnknownLinkContent = defineContentType({\n id: 'region-unknown-link',\n title: 'Region \"I don\\'t know my postal code\" Link',\n description: 'Copy and destination for the postal-code lookup link. Absent href omits the link.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n text: { type: 'string', minLength: 1, title: 'Link text' },\n href: {\n type: 'string',\n title: 'Destination',\n format: 'uri-reference',\n // The set `resolveSafeHref` admits, so the Admin form cannot reject a destination the render\n // gate would have kept. Author-time only and lookahead-free, as in the link-bearing particles.\n pattern: '^(?:https?://.*|mailto:.+|tel:.+|/(?:[^/].*)?)$',\n },\n },\n },\n});\n\nexport const RegionErrorContent = defineContentType({\n id: 'region-error',\n title: 'Region Error',\n description: 'Fallback message shown when a write fails without a server message.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n genericMessage: { type: 'string', title: 'Generic failure message' },\n },\n },\n});\n\n// ---------------------------------------------------------------------------\n// Region context\n// ---------------------------------------------------------------------------\n\nexport type RegionContextValue = Readonly<{\n /** Persisted postal code, formatted; null until a region is set. */\n postalCode: string | null;\n /** Persisted city; null when the contract does not resolve one. */\n city: string | null;\n /** The reactive read has not resolved and no cached value is available. */\n loading: boolean;\n /** The modal's draft value, masked as the shopper typed it. */\n draft: string;\n setDraft: (value: string) => void;\n /** A write is in flight. */\n pending: boolean;\n /** Errors from the latest write; empty when there are none. */\n errors: readonly RegionUserError[];\n open: boolean;\n setOpen: (open: boolean) => void;\n /** Dispatches setPostalCode with the current draft. No-op while pending. */\n submit: () => void;\n}>;\n\nconst REGION_CONTEXT_UNSET = Symbol('RegionContextUnset');\n\nconst RegionContextInternal = createContext<RegionContextValue | typeof REGION_CONTEXT_UNSET>(REGION_CONTEXT_UNSET);\n\nexport const RegionContext = RegionContextInternal as React.Context<RegionContextValue>;\n\n/**\n * Read the active region and the modal's write state from the nearest `RegionRoot`. Throws when\n * called outside a `RegionRoot` subtree, mirroring `useSession()` and `useCart()`: a misplaced part\n * is a composition bug the author should see immediately, not a silent degrade to \"no region\". The\n * compiler catches the same mistake earlier through `requiresContext: 'region'` (ADR-0009).\n */\nexport function useRegion(): RegionContextValue {\n const ctx = useContext(RegionContextInternal);\n if (ctx === REGION_CONTEXT_UNSET) {\n throw new Error('Region components must be used inside a <RegionRoot> subtree.');\n }\n return ctx;\n}\n\n/**\n * Whether a `RegionForm` encloses the subtree. `RegionSubmit` is independently placeable, so it\n * cannot assume a form owner: with one it is a real submit button, which is what makes Enter in the\n * field submit; without one a `type=\"submit\"` button would have nothing to submit and its click\n * would do nothing. Reading this keeps the form composable rather than load-bearing.\n */\nconst RegionFormContext = createContext(false);\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type RegionRootProps = ComponentPropsWithRef<'div'> & {\n /**\n * Region entity slice forwarded by the reactive runtime (`ReactiveDataProvider`) — the value of the\n * `region` top-level field on `RegionData on Query`, not the wrapping `{ region: … }` shape.\n * Receives `null` while the entity has not resolved and when no region is set.\n */\n $data?: RegionEntity | null;\n /** Injected by the compiler from `actions: { region: ['setPostalCode'] }`; the region write surface. */\n $actions?: ComponentActions;\n children?: ReactNode;\n};\n\nexport type RegionTriggerProps = ComponentPropsWithRef<'button'> & {\n /** Shown when no region is set. */\n unsetLabel?: string;\n /** Merchant-authored copy; wins over the props when present. */\n $content?: ContentValue<typeof RegionTriggerContent>;\n};\n\n/** Children are omitted: what this renders is the resolved region, never authored content. */\nexport type RegionValueProps = Omit<ComponentPropsWithRef<'span'>, 'children'>;\n\nexport type RegionLoadingProps = ComponentPropsWithRef<'div'>;\n\nexport type RegionModalControllerProps = {\n children?: ReactNode;\n};\n\nexport type RegionFormProps = ComponentPropsWithRef<'form'>;\n\nexport type RegionPostalCodeControlProps = {\n className?: string;\n /** Mask template forwarded to `PostalCodeField`; defaults to that particle's BR CEP mask. */\n mask?: string;\n label?: string;\n placeholder?: string;\n /** The field's own incomplete-value message. Server rejections surface through `RegionError`. */\n errorMessage?: string;\n};\n\nexport type RegionSubmitProps = ComponentPropsWithRef<'button'> & {\n label?: string;\n pendingLabel?: string;\n /** Merchant-authored copy; wins over the props when present. */\n $content?: ContentValue<typeof RegionSubmitContent>;\n};\n\n/** Children are omitted: what this renders is the write's own error, never authored content. */\nexport type RegionErrorProps = Omit<ComponentPropsWithRef<'div'>, 'children'> & {\n /** Merchant-authored fallback copy; wins over the prop when present. */\n $content?: ContentValue<typeof RegionErrorContent>;\n};\n\nexport type RegionUnknownLinkProps = ComponentPropsWithRef<'a'> & {\n /** Merchant-authored text and destination; each wins over its prop when present. */\n $content?: ContentValue<typeof RegionUnknownLinkContent>;\n};\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const RegionRootDefinition = defineComponent<RegionRootProps>({\n name: 'RegionRoot',\n importPath: '@vtex/faststore/components',\n component: RegionRoot,\n fragment,\n fetchStrategy: 'reactive',\n providesContext: 'region',\n actions: { region: ['setPostalCode'] },\n editor: { label: 'Region Root', category: 'content' },\n schema: {},\n guideline:\n 'Provides region context and reads the active postal code and city reactively. Place it in the shared header layout and compose RegionTrigger, RegionValue, RegionLoading, and a RegionModalController inside it. Every Region* component must be a descendant. See region.md.',\n});\n\nexport const RegionTriggerDefinition = defineComponent<RegionTriggerProps>({\n name: 'RegionTrigger',\n importPath: '@vtex/faststore/components',\n component: RegionTrigger,\n content: RegionTriggerContent,\n requiresContext: 'region',\n editor: { label: 'Region Trigger', category: 'action' },\n schema: {\n unsetLabel: { type: 'string', default: 'Informe seu CEP', label: 'Label when no region is set' },\n },\n guideline:\n 'Opens the region modal. Shows its unset label while no region is set; compose RegionValue as its child so the active region reads inside the control once one is. Its accessible name carries the active region, so a screen reader announces where the shopper is shopping. Must be inside RegionRoot.',\n});\n\nexport const RegionValueDefinition = defineComponent<RegionValueProps>({\n name: 'RegionValue',\n importPath: '@vtex/faststore/components',\n component: RegionValue,\n requiresContext: 'region',\n editor: { label: 'Region Value', category: 'content' },\n schema: {},\n guideline:\n 'The active region, as city plus postal code. Renders nothing while the region is unresolved or unset, so it needs no visibility prop — place it inside RegionTrigger, or anywhere in the header that should echo the region. The city is wrapped in its own element (data-fs-region-value-city) so a store can style or hide it; when the contract resolves no city, the postal code renders alone with no separator. A plain space separates the two in the text, so the accessible name reads \"São Paulo 01310-100\"; a flex container strips it, so a gap swatch still sets the visible spacing. Must be inside RegionRoot.',\n});\n\nexport const RegionLoadingDefinition = defineComponent<RegionLoadingProps>({\n name: 'RegionLoading',\n importPath: '@vtex/faststore/components',\n component: RegionLoading,\n requiresContext: 'region',\n editor: { label: 'Region Loading', category: 'content' },\n schema: {},\n guideline:\n 'Renders its children only while the region read has not resolved. Supply a skeleton placeholder as its child, sized like the resolved display so the header does not reflow when the region arrives. RegionValue renders nothing while this branch is showing, so the two never double up. Must be inside RegionRoot.',\n});\n\nexport const RegionModalControllerDefinition = defineComponent<RegionModalControllerProps>({\n name: 'RegionModalController',\n importPath: '@vtex/faststore/components',\n component: RegionModalController,\n requiresContext: 'region',\n editor: { label: 'Region Modal Controller', category: 'action' },\n schema: {},\n guideline:\n 'Wraps a single DialogRoot child (the region modal) and makes it controlled by the region state: RegionTrigger opens it, Escape and the backdrop close it, and a successful write closes it. Do not set raw open/onOpenChange on the wrapped DialogRoot. Compose DialogPortal, DialogBackdrop, DialogViewport and DialogPopup inside it, with DialogTitle and DialogDescription naming the dialog, and place a RegionForm holding RegionPostalCodeControl and RegionSubmit in the popup, alongside RegionError and RegionUnknownLink. Same pattern as CartOpenOnAdd wrapping the mini-cart DrawerRoot. Must be inside RegionRoot.',\n});\n\nexport const RegionFormDefinition = defineComponent<RegionFormProps>({\n name: 'RegionForm',\n importPath: '@vtex/faststore/components',\n component: RegionForm,\n requiresContext: 'region',\n editor: { label: 'Region Form', category: 'action' },\n schema: {},\n guideline:\n 'Form wrapper holding RegionPostalCodeControl and RegionSubmit inside the region modal, so pressing Enter in the field submits. Same role as ShippingSimulatorForm. Compose it whenever both parts sit together; without it the shopper must reach the button, which still works but is not what a one-field form leads them to expect. Must be inside a RegionModalController dialog.',\n});\n\nexport const RegionPostalCodeControlDefinition = defineComponent<RegionPostalCodeControlProps>({\n name: 'RegionPostalCodeControl',\n importPath: '@vtex/faststore/components',\n component: RegionPostalCodeControl,\n requiresContext: 'region',\n editor: { label: 'Region Postal Code Control', category: 'content' },\n schema: {\n mask: { type: 'string', default: '99999-999', label: 'Mask pattern' },\n label: { type: 'string', label: 'Label' },\n placeholder: { type: 'string', label: 'Placeholder' },\n errorMessage: { type: 'string', label: 'Incomplete-value message' },\n },\n guideline:\n 'The postal-code input inside the region modal: a PostalCodeField bound to the region draft, seeded from the persisted value every time the modal opens. Set mask to the country format at build time (default 99999-999, BR CEP). Server rejections mark the field invalid and take focus back to it; their message is shown by RegionError, not repeated here, and the first keystroke after one clears it. Must be inside a RegionModalController dialog.',\n});\n\nexport const RegionSubmitDefinition = defineComponent<RegionSubmitProps>({\n name: 'RegionSubmit',\n importPath: '@vtex/faststore/components',\n component: RegionSubmit,\n content: RegionSubmitContent,\n requiresContext: 'region',\n editor: { label: 'Region Submit', category: 'action' },\n schema: {\n label: { type: 'string', default: 'Aplicar', label: 'Button label' },\n pendingLabel: { type: 'string', default: 'Aplicando...', label: 'Pending label' },\n },\n guideline:\n \"Submits the region draft. Inside a RegionForm it is the form's submit button, which is what makes Enter in the field work; on its own it dispatches directly. Inert while empty and while a write is in flight, so a double activation dispatches one mutation; the pending state is exposed as aria-busy with no built-in spinner. On success the modal closes; on a rejection it stays open and RegionError explains why. Must be inside a RegionModalController dialog.\",\n});\n\nexport const RegionErrorDefinition = defineComponent<RegionErrorProps>({\n name: 'RegionError',\n importPath: '@vtex/faststore/components',\n component: RegionError,\n content: RegionErrorContent,\n requiresContext: 'region',\n editor: { label: 'Region Error', category: 'content' },\n schema: {},\n guideline:\n \"Shows why the latest region write was refused, announced as an alert when it appears. Renders nothing when there is no error, so it needs no visibility prop. The server's own message is shown verbatim; the authored generic message covers a transport failure, where there is no server message to show. Must be inside a RegionModalController dialog.\",\n});\n\nexport const RegionUnknownLinkDefinition = defineComponent<RegionUnknownLinkProps>({\n name: 'RegionUnknownLink',\n importPath: '@vtex/faststore/components',\n component: RegionUnknownLink,\n content: RegionUnknownLinkContent,\n requiresContext: 'region',\n editor: { label: 'Region Unknown Postal Code Link', category: 'navigation' },\n schema: {\n href: { type: 'string', label: 'Lookup service URL' },\n target: { type: ['_self', '_blank'] as const, default: '_self', label: 'Open in' },\n },\n guideline:\n 'The \"I don\\'t know my postal code\" escape hatch, pointing at a postal-code lookup service. Renders nothing until a destination is set, so a store without a lookup service composes it and simply gets no link rather than a dead one. Its text comes from authored content or its children. Must be inside a RegionModalController dialog.',\n});\n\n// ---------------------------------------------------------------------------\n// Implementations\n// ---------------------------------------------------------------------------\n\n/**\n * Reads the active region reactively and publishes it, the modal's draft, and the write state as\n * `region` context.\n *\n * The resolved value is never copied into state: `RegionValue` renders only what the fragment\n * resolves, and a successful write updates the display solely by the mutation payload normalizing\n * into the Apollo cache (Decision 5). That is why a rejected write needs no rollback and cannot\n * leave the header showing a postal code the server refused.\n */\nexport function RegionRoot({ $data: $dataProp, $actions, className, children, ...props }: RegionRootProps) {\n const $data = useDataOverride($dataProp);\n const region = $data ?? null;\n const { loading: rootLoading } = useContext(ReactiveRootContext);\n // A null region is ambiguous on its own — an unresolved read and a shopper who never set one look\n // the same — so the root query's in-flight flag is what separates \"loading\" from \"unset\". Without\n // it a first-time shopper would sit under a skeleton forever instead of seeing the unset prompt.\n const loading = rootLoading && region == null;\n\n const postalCode = region?.postalCode ?? null;\n\n const [open, setOpenState] = useState(false);\n const [draft, setDraftState] = useState('');\n const [pending, setPending] = useState(false);\n const [errors, setErrors] = useState<readonly RegionUserError[]>([]);\n // State is batched, so two activations in one tick would both pass a `pending` check. The ref is\n // what makes \"exactly one mutation per activation\" hold.\n const inFlightRef = useRef(false);\n\n /**\n * Seeds the draft from the persisted value on open and clears it on close (Decision 6), so a\n * reopened modal always shows the truth rather than an abandoned edit.\n */\n const setOpen = useCallback(\n (next: boolean) => {\n setOpenState(next);\n setDraftState(next ? (postalCode ?? '') : '');\n setErrors([]);\n },\n [postalCode],\n );\n\n /**\n * Editing is the shopper's answer to a rejection, so the message and the invalid flag go with the\n * first keystroke. Left standing, they describe a value that is no longer in the field — and\n * `RegionPostalCodeControl`, which withholds its own incomplete-value copy while a rejection\n * stands, would stay silent for the whole edit. Returns the same array when there is nothing to\n * clear, so ordinary typing does not re-render the error subtree.\n */\n const setDraft = useCallback((value: string) => {\n setDraftState(value);\n setErrors((current) => (current.length === 0 ? current : []));\n }, []);\n\n const submit = useCallback(() => {\n if (inFlightRef.current) return;\n const setPostalCode = $actions?.region?.setPostalCode;\n if (!setPostalCode) return;\n\n inFlightRef.current = true;\n setPending(true);\n setErrors([]);\n\n void setPostalCode(draft)\n .then((writeErrors) => {\n if (writeErrors.length > 0) {\n // A rejection keeps the modal open so the message has somewhere to show (US-3).\n setErrors(writeErrors);\n return;\n }\n setOpenState(false);\n setDraftState('');\n })\n .finally(() => {\n inFlightRef.current = false;\n setPending(false);\n });\n }, [$actions, draft]);\n\n const value: RegionContextValue = useMemo(\n () => ({\n postalCode,\n city: region?.city ?? null,\n loading,\n draft,\n setDraft,\n pending,\n errors,\n open,\n setOpen,\n submit,\n }),\n [postalCode, region?.city, loading, draft, setDraft, pending, errors, open, setOpen, submit],\n );\n\n return (\n <RegionContextInternal value={value}>\n <div {...props} className={className} data-fs-region-root=\"\">\n {children}\n </div>\n </RegionContextInternal>\n );\n}\n\nexport function RegionTrigger({\n unsetLabel = 'Informe seu CEP',\n $content,\n className,\n children,\n ...props\n}: RegionTriggerProps) {\n const { postalCode, city, loading, setOpen } = useRegion();\n const isUnset = postalCode == null;\n\n const resolvedUnsetLabel = $content?.unsetLabel?.trim() ? $content.unsetLabel : unsetLabel;\n const authoredAriaLabel = $content?.ariaLabel?.trim() ? $content.ariaLabel : props['aria-label'];\n\n /**\n * WCAG 2.5.3 wants the accessible name to contain the visible label, so an authored label prefixes\n * the text on screen rather than replacing it: a name of \"Alterar região\" over text reading\n * \"Informe seu CEP\" leaves a voice-control user nothing to say. The region is spaced the way\n * `RegionValue` spaces it, since the name has to carry that text verbatim. Empty while the region\n * is unresolved, when the button reads nothing, and with no authored label the name falls back to\n * the text itself, which already satisfies the rule.\n */\n let visibleLabel = '';\n if (!loading) {\n visibleLabel = isUnset ? resolvedUnsetLabel : [city, postalCode].filter(Boolean).join(' ');\n }\n\n const ariaLabel = authoredAriaLabel && visibleLabel ? `${authoredAriaLabel}: ${visibleLabel}` : authoredAriaLabel;\n\n return (\n <button\n {...props}\n type=\"button\"\n onClick={() => setOpen(true)}\n // Not yet interactive while the region is unresolved: activating it would seed the draft from a\n // value that has not arrived.\n disabled={loading || props.disabled}\n aria-label={ariaLabel}\n className={className}\n data-fs-region-trigger=\"\"\n {...(isUnset ? { 'data-fs-region-trigger-unset': '' } : {})}\n >\n {isUnset && !loading ? resolvedUnsetLabel : null}\n {children}\n </button>\n );\n}\n\nexport function RegionValue({ className, ...props }: RegionValueProps) {\n const { postalCode, city, loading } = useRegion();\n if (loading || postalCode == null) return null;\n\n return (\n <span {...props} className={className} data-fs-region-value=\"\">\n {city ? <span data-fs-region-value-city=\"\">{city}</span> : null}\n {/* A real text node, not a styling concern: it is what separates the two values in the\n accessible name — this is the trigger's name whenever no aria-label is authored — and in any\n non-flex placement. It must stay a plain space: a flex container strips it as leading\n whitespace, so a store's `gap` still governs the visible spacing, where an &nbsp; would\n survive and widen the row. Absent with no city, so the postal code never renders padded. */}\n {city ? ' ' : null}\n {postalCode}\n </span>\n );\n}\n\nexport function RegionLoading({ className, children, ...props }: RegionLoadingProps) {\n const { loading } = useRegion();\n if (!loading) return null;\n\n return (\n <div {...props} className={className} data-fs-region-loading=\"\">\n {children}\n </div>\n );\n}\n\n/**\n * Makes the wrapped `DialogRoot` controlled by the region state. Mirrors\n * `ShippingAddressSavedAddressDrawerController`'s `cloneElement` mechanism; expects a single\n * `DialogRoot` child. Dismissal is never blocked — the Phase 1 modal is always dismissible.\n */\nexport function RegionModalController({ children }: RegionModalControllerProps) {\n const { open, setOpen } = useRegion();\n\n if (!isValidElement<Pick<DialogRootProps, 'open' | 'onOpenChange'>>(children)) {\n return children;\n }\n\n return cloneElement(children, {\n open,\n onOpenChange: (next: boolean) => setOpen(next),\n });\n}\n\n/**\n * Gives the field and the submit a form owner, so Enter in the postal code submits the way it does\n * in every other short form in the repo. Mirrors `ShippingSimulatorForm`: the form's only job is to\n * route native submission to the context's `submit()`.\n */\nexport function RegionForm({ className, children, ...props }: RegionFormProps) {\n const { submit } = useRegion();\n\n return (\n <RegionFormContext value={true}>\n <form\n {...props}\n className={className}\n data-fs-region-form=\"\"\n onSubmit={(event) => {\n event.preventDefault();\n submit();\n }}\n >\n {children}\n </form>\n </RegionFormContext>\n );\n}\n\nexport function RegionPostalCodeControl({\n className,\n mask,\n label,\n placeholder,\n errorMessage,\n}: RegionPostalCodeControlProps) {\n const { draft, setDraft, errors, pending } = useRegion();\n const rootRef = useRef<HTMLDivElement>(null);\n const refused = errors.length > 0;\n\n /**\n * A refusal has to leave the shopper on the value they need to correct. Disabling the input while\n * the write is in flight blurs it and the browser drops focus to the document body, so a rejection\n * otherwise lands with nothing focused inside an open dialog — Enter does nothing and Tab restarts\n * from the top. Waits for `pending` to clear, because focusing a still-disabled input is a no-op:\n * the errors and the pending flag settle in separate microtasks, so there is a render in between\n * where the rejection is known and the field is not yet editable. The field is reached through the\n * root the way ClientProfileForm reaches its own fields.\n */\n useEffect(() => {\n if (!refused || pending) return;\n rootRef.current?.querySelector<HTMLInputElement>('[data-fs-postal-code-field-input]')?.focus();\n }, [refused, pending]);\n\n return (\n <PostalCodeField\n ref={rootRef}\n className={className}\n data-fs-region-postal-code-control=\"\"\n mask={mask}\n label={label}\n placeholder={placeholder}\n // The field renders this whenever it is invalid, from either source, so it is withheld while a\n // server rejection stands: that message is RegionError's, and the field's own copy describes an\n // incomplete value — wrong for a complete code the server refused, and contradictory stacked\n // under the real reason.\n errorMessage={errors.length > 0 ? undefined : errorMessage}\n value={draft}\n onValueChange={setDraft}\n // Still flagged invalid, so the refused value carries aria-invalid and the invalid styling.\n invalid={errors.length > 0}\n disabled={pending}\n />\n );\n}\n\nexport function RegionSubmit({\n label = 'Aplicar',\n pendingLabel = 'Aplicando...',\n $content,\n className,\n children,\n ...props\n}: RegionSubmitProps) {\n const { draft, pending, submit } = useRegion();\n const inForm = useContext(RegionFormContext);\n\n const resolvedLabel = $content?.label?.trim() ? $content.label : label;\n const resolvedPendingLabel = $content?.pendingLabel?.trim() ? $content.pendingLabel : pendingLabel;\n\n return (\n <button\n {...props}\n // The form routes native submission to the same submit(); alone there is no form to submit.\n type={inForm ? 'submit' : 'button'}\n onClick={inForm ? props.onClick : submit}\n disabled={pending || draft.trim() === '' || props.disabled}\n aria-busy={pending || undefined}\n className={className}\n data-fs-region-submit=\"\"\n {...(pending ? { 'data-fs-region-submit-pending': '' } : {})}\n >\n {children ?? (pending ? resolvedPendingLabel : resolvedLabel)}\n </button>\n );\n}\n\n/**\n * The catalog copy for each rejection the client can name: the server's `RegionErrorCode` values plus\n * the client-minted transport failure. The server's own `message` is English, so it is shown only for\n * a code this map does not know — a newer server than this package.\n */\nconst REGION_ERROR_MESSAGE_IDS: Readonly<Record<string, MessageId>> = {\n INVALID_POSTAL_CODE: 'fs/region.error.invalid-postal-code',\n UNSERVED_POSTAL_CODE: 'fs/region.error.unserved-postal-code',\n NETWORK_ERROR: 'fs/region.error.network',\n};\n\nexport function RegionError({ $content, className, ...props }: RegionErrorProps) {\n const { errors } = useRegion();\n const intl = useIntl();\n if (errors.length === 0) return null;\n\n const genericMessage = $content?.genericMessage?.trim() ? $content.genericMessage : null;\n\n function copyFor(error: RegionUserError): string {\n // A transport failure carries no server message worth showing a shopper, so the authored\n // generic copy wins there and only there. Every other code the catalog knows renders in the\n // store's language; a code it does not know falls back to the server's own message.\n if (error.code === 'NETWORK_ERROR' && genericMessage) return genericMessage;\n const id = error.code ? REGION_ERROR_MESSAGE_IDS[error.code] : undefined;\n return id ? intl.formatMessage({ id }) : error.message;\n }\n\n return (\n <div {...props} role=\"alert\" className={className} data-fs-region-error=\"\">\n {errors.map((error) => (\n <div key={`${error.code ?? ''}${error.message}`}>{copyFor(error)}</div>\n ))}\n </div>\n );\n}\n\nexport function RegionUnknownLink({ $content, className, children, href, rel, ...props }: RegionUnknownLinkProps) {\n // Called for its guard alone: a part placed outside a RegionRoot must fail here the way its\n // siblings do, even though this one reads nothing from the context.\n useRegion();\n\n const authoredHref = $content?.href?.trim() ? $content.href : href?.trim();\n // Absent is \"no link\", the ordinary shape for a store with no lookup service, so it never reaches\n // the allowlist or the log.\n if (!authoredHref) return null;\n\n // No read-time validator enforces the model's `format` or `pattern`, so this is the only gate on\n // the CMS value.\n const destination = resolveSafeHref(authoredHref);\n if (!destination.safe) {\n // A bounded reason and no authored value, the convention the content runtime's own log follows.\n console.warn('[RegionUnknownLink] link destination rejected', { reason: destination.reason });\n return null;\n }\n\n const text = $content?.text?.trim() ? $content.text : children;\n if (!text) return null;\n\n // A lookup service is a natural new-tab destination, so a store may pass target. The tab-nabbing\n // defense is then this component's responsibility, not the composer's — the same contract\n // NavigationLink states. Target keywords and link types are both ASCII case-insensitive per HTML,\n // so _BLANK opens a new tab and a caller-passed NOOPENER already counts.\n const relTokens = rel?.split(/\\s+/).filter(Boolean) ?? [];\n const present = new Set(relTokens.map((token) => token.toLowerCase()));\n const safeRel =\n props.target?.toLowerCase() === '_blank'\n ? [...relTokens, ...['noopener', 'noreferrer'].filter((token) => !present.has(token))].join(' ')\n : rel;\n\n return (\n <a {...props} href={destination.href} rel={safeRel} className={className} data-fs-region-unknown-link=\"\">\n {text}\n </a>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Namespace export\n// ---------------------------------------------------------------------------\n\nexport const Region = {\n Root: RegionRoot,\n Trigger: RegionTrigger,\n Value: RegionValue,\n Loading: RegionLoading,\n ModalController: RegionModalController,\n Form: RegionForm,\n PostalCodeControl: RegionPostalCodeControl,\n Submit: RegionSubmit,\n Error: RegionError,\n UnknownLink: RegionUnknownLink,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"region.js","names":[],"sources":["../../../src/matter/region/region.tsx"],"sourcesContent":["'use client';\n\nimport { type ContentValue, defineComponent, defineContentType } from '@vtex/fs-core';\nimport {\n type ComponentPropsWithRef,\n cloneElement,\n createContext,\n isValidElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { DialogRootProps } from '../../particles/dialog/dialog.js';\nimport { PostalCodeField } from '../../particles/postal-code-field/postal-code-field.js';\nimport { useActionsOverride } from '../../sdk/actions/actions-override-context.js';\nimport type { ComponentActions } from '../../sdk/actions/types.js';\nimport { useDataOverride } from '../../sdk/data/data-override-context.js';\nimport { ReactiveRootContext } from '../../sdk/data/page-reactive-data-root.js';\nimport { graphql } from '../../sdk/generated/gql.js';\nimport { type MessageId, useIntl } from '../../sdk/i18n/client.js';\nimport type { RegionEntity } from '../../sdk/region/operations.js';\nimport type { RegionUserError } from '../../sdk/region/region-operations.js';\nimport { resolveSafeHref } from '../../utils/safe-href.js';\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment — typed via @graphql-codegen client-preset\n// ---------------------------------------------------------------------------\n\nconst fragment = graphql(`\n fragment RegionData on Query {\n region {\n ...RegionFragment\n }\n }\n`);\n\n// ---------------------------------------------------------------------------\n// Content models — one per copy-bearing part (Decision 7)\n// ---------------------------------------------------------------------------\n\nexport const RegionTriggerContent = defineContentType({\n id: 'region-trigger',\n title: 'Region Trigger',\n description: 'Copy for the control that opens the region modal.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n unsetLabel: { type: 'string', title: 'Label when no region is set' },\n ariaLabel: { type: 'string', title: 'Accessible label' },\n },\n },\n});\n\nexport const RegionSubmitContent = defineContentType({\n id: 'region-submit',\n title: 'Region Submit',\n description: 'Copy for the region submit control.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n label: { type: 'string', title: 'Button label' },\n pendingLabel: { type: 'string', title: 'Pending label' },\n },\n },\n});\n\nexport const RegionUnknownLinkContent = defineContentType({\n id: 'region-unknown-link',\n title: 'Region \"I don\\'t know my postal code\" Link',\n description: 'Copy and destination for the postal-code lookup link. Absent href omits the link.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n text: { type: 'string', minLength: 1, title: 'Link text' },\n href: {\n type: 'string',\n title: 'Destination',\n format: 'uri-reference',\n // The set `resolveSafeHref` admits, so the Admin form cannot reject a destination the render\n // gate would have kept. Author-time only and lookahead-free, as in the link-bearing particles.\n pattern: '^(?:https?://.*|mailto:.+|tel:.+|/(?:[^/].*)?)$',\n },\n },\n },\n});\n\nexport const RegionErrorContent = defineContentType({\n id: 'region-error',\n title: 'Region Error',\n description: 'Fallback message shown when a write fails without a server message.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n genericMessage: { type: 'string', title: 'Generic failure message' },\n },\n },\n});\n\n// ---------------------------------------------------------------------------\n// Region context\n// ---------------------------------------------------------------------------\n\nexport type RegionContextValue = Readonly<{\n /** Persisted postal code, formatted; null until a region is set. */\n postalCode: string | null;\n /** Persisted city; null when the contract does not resolve one. */\n city: string | null;\n /** The reactive read has not resolved and no cached value is available. */\n loading: boolean;\n /** The modal's draft value, masked as the shopper typed it. */\n draft: string;\n setDraft: (value: string) => void;\n /** A write is in flight. */\n pending: boolean;\n /** Errors from the latest write; empty when there are none. */\n errors: readonly RegionUserError[];\n open: boolean;\n setOpen: (open: boolean) => void;\n /** Dispatches setPostalCode with the current draft. No-op while pending. */\n submit: () => void;\n}>;\n\nconst REGION_CONTEXT_UNSET = Symbol('RegionContextUnset');\n\nconst RegionContextInternal = createContext<RegionContextValue | typeof REGION_CONTEXT_UNSET>(REGION_CONTEXT_UNSET);\n\nexport const RegionContext = RegionContextInternal as React.Context<RegionContextValue>;\n\n/**\n * Read the active region and the modal's write state from the nearest `RegionRoot`. Throws when\n * called outside a `RegionRoot` subtree, mirroring `useSession()` and `useCart()`: a misplaced part\n * is a composition bug the author should see immediately, not a silent degrade to \"no region\". The\n * compiler catches the same mistake earlier through `requiresContext: 'region'` (ADR-0009).\n */\nexport function useRegion(): RegionContextValue {\n const ctx = useContext(RegionContextInternal);\n if (ctx === REGION_CONTEXT_UNSET) {\n throw new Error('Region components must be used inside a <RegionRoot> subtree.');\n }\n return ctx;\n}\n\n/**\n * Whether a `RegionForm` encloses the subtree. `RegionSubmit` is independently placeable, so it\n * cannot assume a form owner: with one it is a real submit button, which is what makes Enter in the\n * field submit; without one a `type=\"submit\"` button would have nothing to submit and its click\n * would do nothing. Reading this keeps the form composable rather than load-bearing.\n */\nconst RegionFormContext = createContext(false);\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type RegionRootProps = ComponentPropsWithRef<'div'> & {\n /**\n * Region entity slice forwarded by the reactive runtime (`ReactiveDataProvider`) — the value of the\n * `region` top-level field on `RegionData on Query`, not the wrapping `{ region: … }` shape.\n * Receives `null` while the entity has not resolved and when no region is set.\n */\n $data?: RegionEntity | null;\n /** Injected by the compiler from `actions: { region: ['setPostalCode'] }`; the region write surface. */\n $actions?: ComponentActions;\n children?: ReactNode;\n};\n\nexport type RegionTriggerProps = ComponentPropsWithRef<'button'> & {\n /** Shown when no region is set. */\n unsetLabel?: string;\n /** Merchant-authored copy; wins over the props when present. */\n $content?: ContentValue<typeof RegionTriggerContent>;\n};\n\n/** Children are omitted: what this renders is the resolved region, never authored content. */\nexport type RegionValueProps = Omit<ComponentPropsWithRef<'span'>, 'children'>;\n\nexport type RegionLoadingProps = ComponentPropsWithRef<'div'>;\n\nexport type RegionModalControllerProps = {\n children?: ReactNode;\n};\n\nexport type RegionFormProps = ComponentPropsWithRef<'form'>;\n\nexport type RegionPostalCodeControlProps = {\n className?: string;\n /** Mask template forwarded to `PostalCodeField`; defaults to that particle's BR CEP mask. */\n mask?: string;\n label?: string;\n placeholder?: string;\n /** The field's own incomplete-value message. Server rejections surface through `RegionError`. */\n errorMessage?: string;\n};\n\nexport type RegionSubmitProps = ComponentPropsWithRef<'button'> & {\n label?: string;\n pendingLabel?: string;\n /** Merchant-authored copy; wins over the props when present. */\n $content?: ContentValue<typeof RegionSubmitContent>;\n};\n\n/** Children are omitted: what this renders is the write's own error, never authored content. */\nexport type RegionErrorProps = Omit<ComponentPropsWithRef<'div'>, 'children'> & {\n /** Merchant-authored fallback copy; wins over the prop when present. */\n $content?: ContentValue<typeof RegionErrorContent>;\n};\n\nexport type RegionUnknownLinkProps = ComponentPropsWithRef<'a'> & {\n /** Merchant-authored text and destination; each wins over its prop when present. */\n $content?: ContentValue<typeof RegionUnknownLinkContent>;\n};\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const RegionRootDefinition = defineComponent<RegionRootProps>({\n name: 'RegionRoot',\n importPath: '@vtex/faststore/components',\n component: RegionRoot,\n fragment,\n fetchStrategy: 'reactive',\n providesContext: 'region',\n actions: { region: ['setPostalCode'] },\n editor: { label: 'Region Root', category: 'content' },\n schema: {},\n guideline:\n 'Provides region context and reads the active postal code and city reactively. Place it in the shared header layout and compose RegionTrigger, RegionValue, RegionLoading, and a RegionModalController inside it. Every Region* component must be a descendant. See region.md.',\n});\n\nexport const RegionTriggerDefinition = defineComponent<RegionTriggerProps>({\n name: 'RegionTrigger',\n importPath: '@vtex/faststore/components',\n component: RegionTrigger,\n content: RegionTriggerContent,\n requiresContext: 'region',\n editor: { label: 'Region Trigger', category: 'action' },\n schema: {\n unsetLabel: { type: 'string', default: 'Informe seu CEP', label: 'Label when no region is set' },\n },\n guideline:\n 'Opens the region modal. Shows its unset label while no region is set; compose RegionValue as its child so the active region reads inside the control once one is. Its accessible name carries the active region, so a screen reader announces where the shopper is shopping. Must be inside RegionRoot.',\n});\n\nexport const RegionValueDefinition = defineComponent<RegionValueProps>({\n name: 'RegionValue',\n importPath: '@vtex/faststore/components',\n component: RegionValue,\n requiresContext: 'region',\n editor: { label: 'Region Value', category: 'content' },\n schema: {},\n guideline:\n 'The active region, as city plus postal code. Renders nothing while the region is unresolved or unset, so it needs no visibility prop — place it inside RegionTrigger, or anywhere in the header that should echo the region. The city is wrapped in its own element (data-fs-region-value-city) so a store can style or hide it; when the contract resolves no city, the postal code renders alone with no separator. A plain space separates the two in the text, so the accessible name reads \"São Paulo 01310-100\"; a flex container strips it, so a gap swatch still sets the visible spacing. Must be inside RegionRoot.',\n});\n\nexport const RegionLoadingDefinition = defineComponent<RegionLoadingProps>({\n name: 'RegionLoading',\n importPath: '@vtex/faststore/components',\n component: RegionLoading,\n requiresContext: 'region',\n editor: { label: 'Region Loading', category: 'content' },\n schema: {},\n guideline:\n 'Renders its children only while the region read has not resolved. Supply a skeleton placeholder as its child, sized like the resolved display so the header does not reflow when the region arrives. RegionValue renders nothing while this branch is showing, so the two never double up. Must be inside RegionRoot.',\n});\n\nexport const RegionModalControllerDefinition = defineComponent<RegionModalControllerProps>({\n name: 'RegionModalController',\n importPath: '@vtex/faststore/components',\n component: RegionModalController,\n requiresContext: 'region',\n editor: { label: 'Region Modal Controller', category: 'action' },\n schema: {},\n guideline:\n 'Wraps a single DialogRoot child (the region modal) and makes it controlled by the region state: RegionTrigger opens it, Escape and the backdrop close it, and a successful write closes it. Do not set raw open/onOpenChange on the wrapped DialogRoot. Compose DialogPortal, DialogBackdrop, DialogViewport and DialogPopup inside it, with DialogTitle and DialogDescription naming the dialog, and place a RegionForm holding RegionPostalCodeControl and RegionSubmit in the popup, alongside RegionError and RegionUnknownLink. Same pattern as CartOpenOnAdd wrapping the mini-cart DrawerRoot. Must be inside RegionRoot.',\n});\n\nexport const RegionFormDefinition = defineComponent<RegionFormProps>({\n name: 'RegionForm',\n importPath: '@vtex/faststore/components',\n component: RegionForm,\n requiresContext: 'region',\n editor: { label: 'Region Form', category: 'action' },\n schema: {},\n guideline:\n 'Form wrapper holding RegionPostalCodeControl and RegionSubmit inside the region modal, so pressing Enter in the field submits. Same role as ShippingSimulatorForm. Compose it whenever both parts sit together; without it the shopper must reach the button, which still works but is not what a one-field form leads them to expect. Must be inside a RegionModalController dialog.',\n});\n\nexport const RegionPostalCodeControlDefinition = defineComponent<RegionPostalCodeControlProps>({\n name: 'RegionPostalCodeControl',\n importPath: '@vtex/faststore/components',\n component: RegionPostalCodeControl,\n requiresContext: 'region',\n editor: { label: 'Region Postal Code Control', category: 'content' },\n schema: {\n mask: { type: 'string', default: '99999-999', label: 'Mask pattern' },\n label: { type: 'string', label: 'Label' },\n placeholder: { type: 'string', label: 'Placeholder' },\n errorMessage: { type: 'string', label: 'Incomplete-value message' },\n },\n guideline:\n 'The postal-code input inside the region modal: a PostalCodeField bound to the region draft, seeded from the persisted value every time the modal opens. Set mask to the country format at build time (default 99999-999, BR CEP). Server rejections mark the field invalid and take focus back to it; their message is shown by RegionError, not repeated here, and the first keystroke after one clears it. Must be inside a RegionModalController dialog.',\n});\n\nexport const RegionSubmitDefinition = defineComponent<RegionSubmitProps>({\n name: 'RegionSubmit',\n importPath: '@vtex/faststore/components',\n component: RegionSubmit,\n content: RegionSubmitContent,\n requiresContext: 'region',\n editor: { label: 'Region Submit', category: 'action' },\n schema: {\n label: { type: 'string', default: 'Aplicar', label: 'Button label' },\n pendingLabel: { type: 'string', default: 'Aplicando...', label: 'Pending label' },\n },\n guideline:\n \"Submits the region draft. Inside a RegionForm it is the form's submit button, which is what makes Enter in the field work; on its own it dispatches directly. Inert while empty and while a write is in flight, so a double activation dispatches one mutation; the pending state is exposed as aria-busy with no built-in spinner. On success the modal closes; on a rejection it stays open and RegionError explains why. Must be inside a RegionModalController dialog.\",\n});\n\nexport const RegionErrorDefinition = defineComponent<RegionErrorProps>({\n name: 'RegionError',\n importPath: '@vtex/faststore/components',\n component: RegionError,\n content: RegionErrorContent,\n requiresContext: 'region',\n editor: { label: 'Region Error', category: 'content' },\n schema: {},\n guideline:\n \"Shows why the latest region write was refused, announced as an alert when it appears. Renders nothing when there is no error, so it needs no visibility prop. The server's own message is shown verbatim; the authored generic message covers a transport failure, where there is no server message to show. Must be inside a RegionModalController dialog.\",\n});\n\nexport const RegionUnknownLinkDefinition = defineComponent<RegionUnknownLinkProps>({\n name: 'RegionUnknownLink',\n importPath: '@vtex/faststore/components',\n component: RegionUnknownLink,\n content: RegionUnknownLinkContent,\n requiresContext: 'region',\n editor: { label: 'Region Unknown Postal Code Link', category: 'navigation' },\n schema: {\n href: { type: 'string', label: 'Lookup service URL' },\n target: { type: ['_self', '_blank'] as const, default: '_self', label: 'Open in' },\n },\n guideline:\n 'The \"I don\\'t know my postal code\" escape hatch, pointing at a postal-code lookup service. Renders nothing until a destination is set, so a store without a lookup service composes it and simply gets no link rather than a dead one. Its text comes from authored content or its children. Must be inside a RegionModalController dialog.',\n});\n\n// ---------------------------------------------------------------------------\n// Implementations\n// ---------------------------------------------------------------------------\n\n/**\n * Reads the active region reactively and publishes it, the modal's draft, and the write state as\n * `region` context.\n *\n * The resolved value is never copied into state: `RegionValue` renders only what the fragment\n * resolves, and a successful write updates the display solely by the mutation payload normalizing\n * into the Apollo cache (Decision 5). That is why a rejected write needs no rollback and cannot\n * leave the header showing a postal code the server refused.\n */\nexport function RegionRoot({\n $data: $dataProp,\n $actions: $actionsProp,\n className,\n children,\n ...props\n}: RegionRootProps) {\n const $data = useDataOverride($dataProp);\n const $actions = useActionsOverride($actionsProp);\n const region = $data ?? null;\n const { loading: rootLoading } = useContext(ReactiveRootContext);\n // A null region is ambiguous on its own — an unresolved read and a shopper who never set one look\n // the same — so the root query's in-flight flag is what separates \"loading\" from \"unset\". Without\n // it a first-time shopper would sit under a skeleton forever instead of seeing the unset prompt.\n const loading = rootLoading && region == null;\n\n const postalCode = region?.postalCode ?? null;\n\n const [open, setOpenState] = useState(false);\n const [draft, setDraftState] = useState('');\n const [pending, setPending] = useState(false);\n const [errors, setErrors] = useState<readonly RegionUserError[]>([]);\n // State is batched, so two activations in one tick would both pass a `pending` check. The ref is\n // what makes \"exactly one mutation per activation\" hold.\n const inFlightRef = useRef(false);\n\n /**\n * Seeds the draft from the persisted value on open and clears it on close (Decision 6), so a\n * reopened modal always shows the truth rather than an abandoned edit.\n */\n const setOpen = useCallback(\n (next: boolean) => {\n setOpenState(next);\n setDraftState(next ? (postalCode ?? '') : '');\n setErrors([]);\n },\n [postalCode],\n );\n\n /**\n * Editing is the shopper's answer to a rejection, so the message and the invalid flag go with the\n * first keystroke. Left standing, they describe a value that is no longer in the field — and\n * `RegionPostalCodeControl`, which withholds its own incomplete-value copy while a rejection\n * stands, would stay silent for the whole edit. Returns the same array when there is nothing to\n * clear, so ordinary typing does not re-render the error subtree.\n */\n const setDraft = useCallback((value: string) => {\n setDraftState(value);\n setErrors((current) => (current.length === 0 ? current : []));\n }, []);\n\n const submit = useCallback(() => {\n if (inFlightRef.current) return;\n const setPostalCode = $actions?.region?.setPostalCode;\n if (!setPostalCode) return;\n\n inFlightRef.current = true;\n setPending(true);\n setErrors([]);\n\n void setPostalCode(draft)\n .then((writeErrors) => {\n if (writeErrors.length > 0) {\n // A rejection keeps the modal open so the message has somewhere to show (US-3).\n setErrors(writeErrors);\n return;\n }\n setOpenState(false);\n setDraftState('');\n })\n .finally(() => {\n inFlightRef.current = false;\n setPending(false);\n });\n }, [$actions, draft]);\n\n const value: RegionContextValue = useMemo(\n () => ({\n postalCode,\n city: region?.city ?? null,\n loading,\n draft,\n setDraft,\n pending,\n errors,\n open,\n setOpen,\n submit,\n }),\n [postalCode, region?.city, loading, draft, setDraft, pending, errors, open, setOpen, submit],\n );\n\n return (\n <RegionContextInternal value={value}>\n <div {...props} className={className} data-fs-region-root=\"\">\n {children}\n </div>\n </RegionContextInternal>\n );\n}\n\nexport function RegionTrigger({\n unsetLabel = 'Informe seu CEP',\n $content,\n className,\n children,\n ...props\n}: RegionTriggerProps) {\n const { postalCode, city, loading, setOpen } = useRegion();\n const isUnset = postalCode == null;\n\n const resolvedUnsetLabel = $content?.unsetLabel?.trim() ? $content.unsetLabel : unsetLabel;\n const authoredAriaLabel = $content?.ariaLabel?.trim() ? $content.ariaLabel : props['aria-label'];\n\n /**\n * WCAG 2.5.3 wants the accessible name to contain the visible label, so an authored label prefixes\n * the text on screen rather than replacing it: a name of \"Alterar região\" over text reading\n * \"Informe seu CEP\" leaves a voice-control user nothing to say. The region is spaced the way\n * `RegionValue` spaces it, since the name has to carry that text verbatim. Empty while the region\n * is unresolved, when the button reads nothing, and with no authored label the name falls back to\n * the text itself, which already satisfies the rule.\n */\n let visibleLabel = '';\n if (!loading) {\n visibleLabel = isUnset ? resolvedUnsetLabel : [city, postalCode].filter(Boolean).join(' ');\n }\n\n const ariaLabel = authoredAriaLabel && visibleLabel ? `${authoredAriaLabel}: ${visibleLabel}` : authoredAriaLabel;\n\n return (\n <button\n {...props}\n type=\"button\"\n onClick={() => setOpen(true)}\n // Not yet interactive while the region is unresolved: activating it would seed the draft from a\n // value that has not arrived.\n disabled={loading || props.disabled}\n aria-label={ariaLabel}\n className={className}\n data-fs-region-trigger=\"\"\n {...(isUnset ? { 'data-fs-region-trigger-unset': '' } : {})}\n >\n {isUnset && !loading ? resolvedUnsetLabel : null}\n {children}\n </button>\n );\n}\n\nexport function RegionValue({ className, ...props }: RegionValueProps) {\n const { postalCode, city, loading } = useRegion();\n if (loading || postalCode == null) return null;\n\n return (\n <span {...props} className={className} data-fs-region-value=\"\">\n {city ? <span data-fs-region-value-city=\"\">{city}</span> : null}\n {/* A real text node, not a styling concern: it is what separates the two values in the\n accessible name — this is the trigger's name whenever no aria-label is authored — and in any\n non-flex placement. It must stay a plain space: a flex container strips it as leading\n whitespace, so a store's `gap` still governs the visible spacing, where an &nbsp; would\n survive and widen the row. Absent with no city, so the postal code never renders padded. */}\n {city ? ' ' : null}\n {postalCode}\n </span>\n );\n}\n\nexport function RegionLoading({ className, children, ...props }: RegionLoadingProps) {\n const { loading } = useRegion();\n if (!loading) return null;\n\n return (\n <div {...props} className={className} data-fs-region-loading=\"\">\n {children}\n </div>\n );\n}\n\n/**\n * Makes the wrapped `DialogRoot` controlled by the region state. Mirrors\n * `ShippingAddressSavedAddressDrawerController`'s `cloneElement` mechanism; expects a single\n * `DialogRoot` child. Dismissal is never blocked — the Phase 1 modal is always dismissible.\n */\nexport function RegionModalController({ children }: RegionModalControllerProps) {\n const { open, setOpen } = useRegion();\n\n if (!isValidElement<Pick<DialogRootProps, 'open' | 'onOpenChange'>>(children)) {\n return children;\n }\n\n return cloneElement(children, {\n open,\n onOpenChange: (next: boolean) => setOpen(next),\n });\n}\n\n/**\n * Gives the field and the submit a form owner, so Enter in the postal code submits the way it does\n * in every other short form in the repo. Mirrors `ShippingSimulatorForm`: the form's only job is to\n * route native submission to the context's `submit()`.\n */\nexport function RegionForm({ className, children, ...props }: RegionFormProps) {\n const { submit } = useRegion();\n\n return (\n <RegionFormContext value={true}>\n <form\n {...props}\n className={className}\n data-fs-region-form=\"\"\n onSubmit={(event) => {\n event.preventDefault();\n submit();\n }}\n >\n {children}\n </form>\n </RegionFormContext>\n );\n}\n\nexport function RegionPostalCodeControl({\n className,\n mask,\n label,\n placeholder,\n errorMessage,\n}: RegionPostalCodeControlProps) {\n const { draft, setDraft, errors, pending } = useRegion();\n const rootRef = useRef<HTMLDivElement>(null);\n const refused = errors.length > 0;\n\n /**\n * A refusal has to leave the shopper on the value they need to correct. Disabling the input while\n * the write is in flight blurs it and the browser drops focus to the document body, so a rejection\n * otherwise lands with nothing focused inside an open dialog — Enter does nothing and Tab restarts\n * from the top. Waits for `pending` to clear, because focusing a still-disabled input is a no-op:\n * the errors and the pending flag settle in separate microtasks, so there is a render in between\n * where the rejection is known and the field is not yet editable. The field is reached through the\n * root the way ClientProfileForm reaches its own fields.\n */\n useEffect(() => {\n if (!refused || pending) return;\n rootRef.current?.querySelector<HTMLInputElement>('[data-fs-postal-code-field-input]')?.focus();\n }, [refused, pending]);\n\n return (\n <PostalCodeField\n ref={rootRef}\n className={className}\n data-fs-region-postal-code-control=\"\"\n mask={mask}\n label={label}\n placeholder={placeholder}\n // The field renders this whenever it is invalid, from either source, so it is withheld while a\n // server rejection stands: that message is RegionError's, and the field's own copy describes an\n // incomplete value — wrong for a complete code the server refused, and contradictory stacked\n // under the real reason.\n errorMessage={errors.length > 0 ? undefined : errorMessage}\n value={draft}\n onValueChange={setDraft}\n // Still flagged invalid, so the refused value carries aria-invalid and the invalid styling.\n invalid={errors.length > 0}\n disabled={pending}\n />\n );\n}\n\nexport function RegionSubmit({\n label = 'Aplicar',\n pendingLabel = 'Aplicando...',\n $content,\n className,\n children,\n ...props\n}: RegionSubmitProps) {\n const { draft, pending, submit } = useRegion();\n const inForm = useContext(RegionFormContext);\n\n const resolvedLabel = $content?.label?.trim() ? $content.label : label;\n const resolvedPendingLabel = $content?.pendingLabel?.trim() ? $content.pendingLabel : pendingLabel;\n\n return (\n <button\n {...props}\n // The form routes native submission to the same submit(); alone there is no form to submit.\n type={inForm ? 'submit' : 'button'}\n onClick={inForm ? props.onClick : submit}\n disabled={pending || draft.trim() === '' || props.disabled}\n aria-busy={pending || undefined}\n className={className}\n data-fs-region-submit=\"\"\n {...(pending ? { 'data-fs-region-submit-pending': '' } : {})}\n >\n {children ?? (pending ? resolvedPendingLabel : resolvedLabel)}\n </button>\n );\n}\n\n/**\n * The catalog copy for each rejection the client can name: the server's `RegionErrorCode` values plus\n * the client-minted transport failure. The server's own `message` is English, so it is shown only for\n * a code this map does not know — a newer server than this package.\n */\nconst REGION_ERROR_MESSAGE_IDS: Readonly<Record<string, MessageId>> = {\n INVALID_POSTAL_CODE: 'fs/region.error.invalid-postal-code',\n UNSERVED_POSTAL_CODE: 'fs/region.error.unserved-postal-code',\n NETWORK_ERROR: 'fs/region.error.network',\n};\n\nexport function RegionError({ $content, className, ...props }: RegionErrorProps) {\n const { errors } = useRegion();\n const intl = useIntl();\n if (errors.length === 0) return null;\n\n const genericMessage = $content?.genericMessage?.trim() ? $content.genericMessage : null;\n\n function copyFor(error: RegionUserError): string {\n // A transport failure carries no server message worth showing a shopper, so the authored\n // generic copy wins there and only there. Every other code the catalog knows renders in the\n // store's language; a code it does not know falls back to the server's own message.\n if (error.code === 'NETWORK_ERROR' && genericMessage) return genericMessage;\n const id = error.code ? REGION_ERROR_MESSAGE_IDS[error.code] : undefined;\n return id ? intl.formatMessage({ id }) : error.message;\n }\n\n return (\n <div {...props} role=\"alert\" className={className} data-fs-region-error=\"\">\n {errors.map((error) => (\n <div key={`${error.code ?? ''}${error.message}`}>{copyFor(error)}</div>\n ))}\n </div>\n );\n}\n\nexport function RegionUnknownLink({ $content, className, children, href, rel, ...props }: RegionUnknownLinkProps) {\n // Called for its guard alone: a part placed outside a RegionRoot must fail here the way its\n // siblings do, even though this one reads nothing from the context.\n useRegion();\n\n const authoredHref = $content?.href?.trim() ? $content.href : href?.trim();\n // Absent is \"no link\", the ordinary shape for a store with no lookup service, so it never reaches\n // the allowlist or the log.\n if (!authoredHref) return null;\n\n // No read-time validator enforces the model's `format` or `pattern`, so this is the only gate on\n // the CMS value.\n const destination = resolveSafeHref(authoredHref);\n if (!destination.safe) {\n // A bounded reason and no authored value, the convention the content runtime's own log follows.\n console.warn('[RegionUnknownLink] link destination rejected', { reason: destination.reason });\n return null;\n }\n\n const text = $content?.text?.trim() ? $content.text : children;\n if (!text) return null;\n\n // A lookup service is a natural new-tab destination, so a store may pass target. The tab-nabbing\n // defense is then this component's responsibility, not the composer's — the same contract\n // NavigationLink states. Target keywords and link types are both ASCII case-insensitive per HTML,\n // so _BLANK opens a new tab and a caller-passed NOOPENER already counts.\n const relTokens = rel?.split(/\\s+/).filter(Boolean) ?? [];\n const present = new Set(relTokens.map((token) => token.toLowerCase()));\n const safeRel =\n props.target?.toLowerCase() === '_blank'\n ? [...relTokens, ...['noopener', 'noreferrer'].filter((token) => !present.has(token))].join(' ')\n : rel;\n\n return (\n <a {...props} href={destination.href} rel={safeRel} className={className} data-fs-region-unknown-link=\"\">\n {text}\n </a>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Namespace export\n// ---------------------------------------------------------------------------\n\nexport const Region = {\n Root: RegionRoot,\n Trigger: RegionTrigger,\n Value: RegionValue,\n Loading: RegionLoading,\n ModalController: RegionModalController,\n Form: RegionForm,\n PostalCodeControl: RegionPostalCodeControl,\n Submit: RegionSubmit,\n Error: RegionError,\n UnknownLink: RegionUnknownLink,\n};\n"],"mappings":"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"}
@@ -50,7 +50,7 @@ const SessionRootDefinition = defineComponent({
50
50
  },
51
51
  schema: {},
52
52
  providesContext: "session",
53
- guideline: "Read-only identity-state provider, normally the root of the shared layout so the header and every page it serves resolve the same session: reads the Session and broadcasts the anonymous/identified/logged-in tri-state plus the raw loggedIn and canEditData flags. Layout scope fetches client-side with the shopper cookie; page scope fetches server-side without it. Exactly one per rendered tree — a page must not add its own when the layout already hosts one. See session.md."
53
+ guideline: "Read-only identity-state provider, normally the root of the shared layout so the header and every page it serves resolve the same session: reads the Session and broadcasts the anonymous/identified/logged-in tri-state plus the raw loggedIn and canEditData flags. Layout scope fetches once client-side and reuses the cache across navigation; page scope fetches fresh server-side on every request. Exactly one per rendered tree — a page must not add its own when the layout already hosts one. See session.md."
54
54
  });
55
55
  const SessionGuardDefinition = defineComponent({
56
56
  name: "SessionGuard",
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","names":[],"sources":["../../../src/matter/session/session.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport { createContext, type ReactNode, useContext, useMemo } from 'react';\nimport { useDataOverride } from '../../sdk/data/data-override-context.js';\nimport { graphql } from '../../sdk/generated/gql.js';\nimport type { SessionEntity } from '../../sdk/session/operations.js';\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment — typed via @graphql-codegen client-preset\n// ---------------------------------------------------------------------------\n\nconst fragment = graphql(`\n fragment SessionData on Query {\n session {\n ...SessionFragment\n }\n }\n`);\n\n// ---------------------------------------------------------------------------\n// Tri-state derivation — the single home of the truth table (Decision 2).\n// ---------------------------------------------------------------------------\n\nexport type SessionState = 'anonymous' | 'identified' | 'loggedIn';\n\n/**\n * Derive the identity tri-state from the two raw session flags. `loggedIn` takes precedence: an\n * authenticated shopper is always `loggedIn` regardless of `isIdentified`.\n */\nexport function deriveSessionState({\n loggedIn,\n isIdentified,\n}: {\n loggedIn: boolean;\n isIdentified: boolean;\n}): SessionState {\n if (loggedIn) return 'loggedIn';\n if (isIdentified) return 'identified';\n return 'anonymous';\n}\n\n// ---------------------------------------------------------------------------\n// Session context (read-only derived state + raw flags)\n// ---------------------------------------------------------------------------\n\nexport type SessionContextValue = Readonly<{\n /** Derived tri-state: anonymous | identified | loggedIn. */\n state: SessionState;\n /** Raw authenticated-session flag from `session.loggedIn`. */\n loggedIn: boolean;\n /** Raw edit-permission flag from `session.canEditData`; lets Controls render editable vs. masked. */\n canEditData: boolean;\n /** True while the Session entity has not yet resolved. */\n loading: boolean;\n}>;\n\nconst SESSION_DATA_UNSET = Symbol('SessionContextUnset');\n\nconst SessionContextInternal = createContext<SessionContextValue | typeof SESSION_DATA_UNSET>(SESSION_DATA_UNSET);\n\nexport const SessionContext = SessionContextInternal as React.Context<SessionContextValue>;\n\n/**\n * Read the derived session state and raw flags from the nearest `SessionRoot`. Throws when called\n * outside a `SessionRoot` subtree (a misplaced `useSession()` is a composition bug the author should\n * see immediately, not a silent degrade to anonymous), mirroring `useCart()` (Decision 6).\n *\n * While the Session entity is unresolved the context carries `loading: true` and `state:\n * 'anonymous'` (the least-privileged default); consumers that must not flash gate on `loading`.\n */\nexport function useSession(): SessionContextValue {\n const ctx = useContext(SessionContextInternal);\n if (ctx === SESSION_DATA_UNSET) {\n throw new Error('useSession() must be used inside a <SessionRoot> subtree.');\n }\n return ctx;\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type SessionRootProps = {\n /**\n * Session entity slice forwarded by the reactive runtime (`ReactiveDataProvider`) — the value of\n * the `session` top-level field on `SessionData on Query`, not the wrapping `{ session: … }` shape.\n * Receives `null` while the entity has not yet resolved.\n */\n $data?: SessionEntity | null;\n children?: ReactNode;\n};\n\nexport type SessionGuardProps = {\n /** The session states under which the children render. Renders nothing for any other state. */\n when?: SessionState[];\n children?: ReactNode;\n};\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const SessionRootDefinition = defineComponent<SessionRootProps>({\n name: 'SessionRoot',\n importPath: '@vtex/faststore/components',\n component: SessionRoot,\n fragment,\n fetchStrategy: 'reactive',\n editor: { label: 'Session Root', category: 'identity' },\n schema: {},\n providesContext: 'session',\n guideline:\n 'Read-only identity-state provider, normally the root of the shared layout so the header and every page it serves resolve the same session: reads the Session and broadcasts the anonymous/identified/logged-in tri-state plus the raw loggedIn and canEditData flags. Layout scope fetches client-side with the shopper cookie; page scope fetches server-side without it. Exactly one per rendered tree — a page must not add its own when the layout already hosts one. See session.md.',\n});\n\nexport const SessionGuardDefinition = defineComponent<SessionGuardProps>({\n name: 'SessionGuard',\n importPath: '@vtex/faststore/components',\n component: SessionGuard,\n editor: { label: 'Session Guard', category: 'identity' },\n schema: {\n when: { type: 'array', items: ['anonymous', 'identified', 'loggedIn'] as const, label: 'When State' },\n },\n requiresContext: 'session',\n guideline:\n 'Declarative gate: renders its children only when the current session state is one of the values in `when` (e.g. {\"when\":[\"identified\",\"loggedIn\"]}), else renders nothing. Use it in page or layout JSON to show or hide an identity subtree by state without writing the gate into a component. Must be inside SessionRoot, which may be hosted by the page or by its layout.',\n});\n\n// ---------------------------------------------------------------------------\n// Implementations\n// ---------------------------------------------------------------------------\n\nexport function SessionRoot({ $data: $dataProp, children }: SessionRootProps) {\n const $data = useDataOverride($dataProp);\n const session = $data ?? null;\n const loading = session == null;\n\n const value: SessionContextValue = useMemo(() => {\n const loggedIn = session?.loggedIn ?? false;\n const isIdentified = session?.isIdentified ?? false;\n return {\n state: deriveSessionState({ loggedIn, isIdentified }),\n loggedIn,\n canEditData: session?.canEditData ?? false,\n loading,\n };\n }, [session?.loggedIn, session?.isIdentified, session?.canEditData, loading]);\n\n return <SessionContextInternal value={value}>{children}</SessionContextInternal>;\n}\n\nexport function SessionGuard({ when, children }: SessionGuardProps) {\n const { state } = useSession();\n if (!when?.includes(state)) return null;\n return <>{children}</>;\n}\n\n// ---------------------------------------------------------------------------\n// Namespace export\n// ---------------------------------------------------------------------------\n\nexport const Session = {\n Root: SessionRoot,\n Guard: SessionGuard,\n};\n"],"mappings":";;;;;;;;AAYA,MAAM,WAAW,QAAQ;;;;;;CAMxB;;;;;AAYD,SAAgB,mBAAmB,EACjC,UACA,gBAIe;CACf,IAAI,UAAU,OAAO;CACrB,IAAI,cAAc,OAAO;CACzB,OAAO;AACT;AAiBA,MAAM,qBAAqB,OAAO,qBAAqB;AAEvD,MAAM,yBAAyB,cAA+D,kBAAkB;AAEhH,MAAa,iBAAiB;;;;;;;;;AAU9B,SAAgB,aAAkC;CAChD,MAAM,MAAM,WAAW,sBAAsB;CAC7C,IAAI,QAAQ,oBACV,MAAM,IAAI,MAAM,2DAA2D;CAE7E,OAAO;AACT;AA0BA,MAAa,wBAAwB,gBAAkC;CACrE,MAAM;CACN,YAAY;CACZ,WAAW;CACX;CACA,eAAe;CACf,QAAQ;EAAE,OAAO;EAAgB,UAAU;CAAW;CACtD,QAAQ,CAAC;CACT,iBAAiB;CACjB,WACE;AACJ,CAAC;AAED,MAAa,yBAAyB,gBAAmC;CACvE,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAiB,UAAU;CAAW;CACvD,QAAQ,EACN,MAAM;EAAE,MAAM;EAAS,OAAO;GAAC;GAAa;GAAc;EAAU;EAAY,OAAO;CAAa,EACtG;CACA,iBAAiB;CACjB,WACE;AACJ,CAAC;AAMD,SAAgB,YAAY,EAAE,OAAO,WAAW,YAA8B;CAE5E,MAAM,UADQ,gBAAgB,SACV,KAAK;CACzB,MAAM,UAAU,WAAW;CAE3B,MAAM,QAA6B,cAAc;EAC/C,MAAM,WAAW,SAAS,YAAY;EAEtC,OAAO;GACL,OAAO,mBAAmB;IAAE;IAAU,cAFnB,SAAS,gBAAgB;GAEO,CAAC;GACpD;GACA,aAAa,SAAS,eAAe;GACrC;EACF;CACF,GAAG;EAAC,SAAS;EAAU,SAAS;EAAc,SAAS;EAAa;CAAO,CAAC;CAE5E,OAAO,oBAAC,wBAAD;EAA+B;EAAQ;CAAiC,CAAA;AACjF;AAEA,SAAgB,aAAa,EAAE,MAAM,YAA+B;CAClE,MAAM,EAAE,UAAU,WAAW;CAC7B,IAAI,CAAC,MAAM,SAAS,KAAK,GAAG,OAAO;CACnC,OAAO,oBAAA,UAAA,EAAG,SAAW,CAAA;AACvB;AAMA,MAAa,UAAU;CACrB,MAAM;CACN,OAAO;AACT"}
1
+ {"version":3,"file":"session.js","names":[],"sources":["../../../src/matter/session/session.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport { createContext, type ReactNode, useContext, useMemo } from 'react';\nimport { useDataOverride } from '../../sdk/data/data-override-context.js';\nimport { graphql } from '../../sdk/generated/gql.js';\nimport type { SessionEntity } from '../../sdk/session/operations.js';\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment — typed via @graphql-codegen client-preset\n// ---------------------------------------------------------------------------\n\nconst fragment = graphql(`\n fragment SessionData on Query {\n session {\n ...SessionFragment\n }\n }\n`);\n\n// ---------------------------------------------------------------------------\n// Tri-state derivation — the single home of the truth table (Decision 2).\n// ---------------------------------------------------------------------------\n\nexport type SessionState = 'anonymous' | 'identified' | 'loggedIn';\n\n/**\n * Derive the identity tri-state from the two raw session flags. `loggedIn` takes precedence: an\n * authenticated shopper is always `loggedIn` regardless of `isIdentified`.\n */\nexport function deriveSessionState({\n loggedIn,\n isIdentified,\n}: {\n loggedIn: boolean;\n isIdentified: boolean;\n}): SessionState {\n if (loggedIn) return 'loggedIn';\n if (isIdentified) return 'identified';\n return 'anonymous';\n}\n\n// ---------------------------------------------------------------------------\n// Session context (read-only derived state + raw flags)\n// ---------------------------------------------------------------------------\n\nexport type SessionContextValue = Readonly<{\n /** Derived tri-state: anonymous | identified | loggedIn. */\n state: SessionState;\n /** Raw authenticated-session flag from `session.loggedIn`. */\n loggedIn: boolean;\n /** Raw edit-permission flag from `session.canEditData`; lets Controls render editable vs. masked. */\n canEditData: boolean;\n /** True while the Session entity has not yet resolved. */\n loading: boolean;\n}>;\n\nconst SESSION_DATA_UNSET = Symbol('SessionContextUnset');\n\nconst SessionContextInternal = createContext<SessionContextValue | typeof SESSION_DATA_UNSET>(SESSION_DATA_UNSET);\n\nexport const SessionContext = SessionContextInternal as React.Context<SessionContextValue>;\n\n/**\n * Read the derived session state and raw flags from the nearest `SessionRoot`. Throws when called\n * outside a `SessionRoot` subtree (a misplaced `useSession()` is a composition bug the author should\n * see immediately, not a silent degrade to anonymous), mirroring `useCart()` (Decision 6).\n *\n * While the Session entity is unresolved the context carries `loading: true` and `state:\n * 'anonymous'` (the least-privileged default); consumers that must not flash gate on `loading`.\n */\nexport function useSession(): SessionContextValue {\n const ctx = useContext(SessionContextInternal);\n if (ctx === SESSION_DATA_UNSET) {\n throw new Error('useSession() must be used inside a <SessionRoot> subtree.');\n }\n return ctx;\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type SessionRootProps = {\n /**\n * Session entity slice forwarded by the reactive runtime (`ReactiveDataProvider`) — the value of\n * the `session` top-level field on `SessionData on Query`, not the wrapping `{ session: … }` shape.\n * Receives `null` while the entity has not yet resolved.\n */\n $data?: SessionEntity | null;\n children?: ReactNode;\n};\n\nexport type SessionGuardProps = {\n /** The session states under which the children render. Renders nothing for any other state. */\n when?: SessionState[];\n children?: ReactNode;\n};\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const SessionRootDefinition = defineComponent<SessionRootProps>({\n name: 'SessionRoot',\n importPath: '@vtex/faststore/components',\n component: SessionRoot,\n fragment,\n fetchStrategy: 'reactive',\n editor: { label: 'Session Root', category: 'identity' },\n schema: {},\n providesContext: 'session',\n guideline:\n 'Read-only identity-state provider, normally the root of the shared layout so the header and every page it serves resolve the same session: reads the Session and broadcasts the anonymous/identified/logged-in tri-state plus the raw loggedIn and canEditData flags. Layout scope fetches once client-side and reuses the cache across navigation; page scope fetches fresh server-side on every request. Exactly one per rendered tree — a page must not add its own when the layout already hosts one. See session.md.',\n});\n\nexport const SessionGuardDefinition = defineComponent<SessionGuardProps>({\n name: 'SessionGuard',\n importPath: '@vtex/faststore/components',\n component: SessionGuard,\n editor: { label: 'Session Guard', category: 'identity' },\n schema: {\n when: { type: 'array', items: ['anonymous', 'identified', 'loggedIn'] as const, label: 'When State' },\n },\n requiresContext: 'session',\n guideline:\n 'Declarative gate: renders its children only when the current session state is one of the values in `when` (e.g. {\"when\":[\"identified\",\"loggedIn\"]}), else renders nothing. Use it in page or layout JSON to show or hide an identity subtree by state without writing the gate into a component. Must be inside SessionRoot, which may be hosted by the page or by its layout.',\n});\n\n// ---------------------------------------------------------------------------\n// Implementations\n// ---------------------------------------------------------------------------\n\nexport function SessionRoot({ $data: $dataProp, children }: SessionRootProps) {\n const $data = useDataOverride($dataProp);\n const session = $data ?? null;\n const loading = session == null;\n\n const value: SessionContextValue = useMemo(() => {\n const loggedIn = session?.loggedIn ?? false;\n const isIdentified = session?.isIdentified ?? false;\n return {\n state: deriveSessionState({ loggedIn, isIdentified }),\n loggedIn,\n canEditData: session?.canEditData ?? false,\n loading,\n };\n }, [session?.loggedIn, session?.isIdentified, session?.canEditData, loading]);\n\n return <SessionContextInternal value={value}>{children}</SessionContextInternal>;\n}\n\nexport function SessionGuard({ when, children }: SessionGuardProps) {\n const { state } = useSession();\n if (!when?.includes(state)) return null;\n return <>{children}</>;\n}\n\n// ---------------------------------------------------------------------------\n// Namespace export\n// ---------------------------------------------------------------------------\n\nexport const Session = {\n Root: SessionRoot,\n Guard: SessionGuard,\n};\n"],"mappings":";;;;;;;;AAYA,MAAM,WAAW,QAAQ;;;;;;CAMxB;;;;;AAYD,SAAgB,mBAAmB,EACjC,UACA,gBAIe;CACf,IAAI,UAAU,OAAO;CACrB,IAAI,cAAc,OAAO;CACzB,OAAO;AACT;AAiBA,MAAM,qBAAqB,OAAO,qBAAqB;AAEvD,MAAM,yBAAyB,cAA+D,kBAAkB;AAEhH,MAAa,iBAAiB;;;;;;;;;AAU9B,SAAgB,aAAkC;CAChD,MAAM,MAAM,WAAW,sBAAsB;CAC7C,IAAI,QAAQ,oBACV,MAAM,IAAI,MAAM,2DAA2D;CAE7E,OAAO;AACT;AA0BA,MAAa,wBAAwB,gBAAkC;CACrE,MAAM;CACN,YAAY;CACZ,WAAW;CACX;CACA,eAAe;CACf,QAAQ;EAAE,OAAO;EAAgB,UAAU;CAAW;CACtD,QAAQ,CAAC;CACT,iBAAiB;CACjB,WACE;AACJ,CAAC;AAED,MAAa,yBAAyB,gBAAmC;CACvE,MAAM;CACN,YAAY;CACZ,WAAW;CACX,QAAQ;EAAE,OAAO;EAAiB,UAAU;CAAW;CACvD,QAAQ,EACN,MAAM;EAAE,MAAM;EAAS,OAAO;GAAC;GAAa;GAAc;EAAU;EAAY,OAAO;CAAa,EACtG;CACA,iBAAiB;CACjB,WACE;AACJ,CAAC;AAMD,SAAgB,YAAY,EAAE,OAAO,WAAW,YAA8B;CAE5E,MAAM,UADQ,gBAAgB,SACV,KAAK;CACzB,MAAM,UAAU,WAAW;CAE3B,MAAM,QAA6B,cAAc;EAC/C,MAAM,WAAW,SAAS,YAAY;EAEtC,OAAO;GACL,OAAO,mBAAmB;IAAE;IAAU,cAFnB,SAAS,gBAAgB;GAEO,CAAC;GACpD;GACA,aAAa,SAAS,eAAe;GACrC;EACF;CACF,GAAG;EAAC,SAAS;EAAU,SAAS;EAAc,SAAS;EAAa;CAAO,CAAC;CAE5E,OAAO,oBAAC,wBAAD;EAA+B;EAAQ;CAAiC,CAAA;AACjF;AAEA,SAAgB,aAAa,EAAE,MAAM,YAA+B;CAClE,MAAM,EAAE,UAAU,WAAW;CAC7B,IAAI,CAAC,MAAM,SAAS,KAAK,GAAG,OAAO;CACnC,OAAO,oBAAA,UAAA,EAAG,SAAW,CAAA;AACvB;AAMA,MAAa,UAAU;CACrB,MAAM;CACN,OAAO;AACT"}